建立Asp.net core並使用Angular 6 作為UI
公司產品前端採用Angular,API則使用ASP.NET Core,文章我只以這兩大前後端 framework為主。
建立ASP.NET core web application專案
選擇core2.1和Angular
Note:安裝core2.1就沒必要安裝Angular template
預設為 Angular 5,所以須升級相關library為Angular 6,
相關的軟體安裝請參考Installing Angular 6
ng update @angular/cli
可以看到會幫我們更新專案中的package.json
ng update @angular/core
ng update @angular/material
專案執行如下
Note:ASP.net core針對開發環境,預設內部使用ng serve,我們不需要額外執行ng serve,
你可以在Output看到類似如下文字:
NG Live Development Server is listening on localhost:13589, open your browser on http://localhost:13589/
專案結構如下
ClientApp folder就是Angular UI部分。
這裡我會新增EF Entities和Dbcontext netstandard2.0專案,
實現EventLog entity的CRUD,下面我們先來把API部分搞定
新增Entity
public sealed class EventLog
{
public long Id { get; set; }
public int EventID { get; set; }
public string LogLevel { get; set; }
public string Message { get; set; }
public string Exception { get; set; }
public DateTime CreatedTime { get; set; }
}
新增DemoContext
public class DemoContext : DbContext
{
public DbSet<EventLog> EventLogs { get; set; }
public DemoContext(DbContextOptions<DemoContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasDefaultSchema("dbo");
modelBuilder.ApplyConfiguration(new EventLogConfiguration());
}
}
新增IServiceCollectionExtension,用來注入DemoContext
public static class IServiceCollectionExtension
{
public static IServiceCollection AddDemoContext(this IServiceCollection services, IConfiguration configuration)
{
var connectionString = configuration.GetConnectionString("Demo");
services.AddDbContextPool<DemoContext>(
options =>
{
var builder = new SqlConnectionStringBuilder(connectionString)
{
ConnectTimeout = 30,
Pooling = true,
ConnectRetryCount = 3,
ConnectRetryInterval = 10
};
options.UseSqlServer(builder.ToString(), b => b.MaxBatchSize(500));
options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
options.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning));
});
return services;
}
}
Website專案部分,新增Dao、Service、Model folder
新增Model
public class GetEventLogResponse
{
public long Id { get; set; }
public int EventID { get; set; }
public DateTime CreatedTime { get; set; }
public string Message { get; set; }
}
新增Dao
public interface IEventLogDao
{
Task<List<GetEventLogResponse>> GetEventLogsAsync();
Task<GetEventLogResponse> GetEventLogAsync(long id);
Task<int> AddEventLogAsync(IEnumerable<EventLog> eventLogs);
Task<int> DeleteEventLogAsync(IEnumerable<EventLog> eventLogs);
Task<int> UpdateEventLogAsync(IEnumerable<EventLog> eventLogs);
}
internal sealed class EventLogDao : IEventLogDao
{
private readonly DemoContext _demoContext;
public EventLogDao(DemoContext demoContext)
{
_demoContext = demoContext;
}
Task<int> IEventLogDao.AddEventLogAsync(IEnumerable<EventLog> eventLogs)
{
_demoContext.EventLogs.AddRangeAsync(eventLogs);
return _demoContext.SaveChangesAsync();
}
Task<int> IEventLogDao.DeleteEventLogAsync(IEnumerable<EventLog> eventLogs)
{
_demoContext.EventLogs.RemoveRange(eventLogs);
return _demoContext.SaveChangesAsync();
}
Task<GetEventLogResponse> IEventLogDao.GetEventLogAsync(long id)
{
return (from e in _demoContext.EventLogs
where e.Id == id
select new GetEventLogResponse
{
Id = e.Id,
EventID = e.EventID,
Message = e.Message,
CreatedTime = e.CreatedTime
}).FirstOrDefaultAsync();
}
Task<List<GetEventLogResponse>> IEventLogDao.GetEventLogsAsync()
{
return (from e in _demoContext.EventLogs
select new GetEventLogResponse
{
Id = e.Id,
EventID = e.EventID,
Message = e.Message,
CreatedTime = e.CreatedTime
}).ToListAsync();
}
Task<int> IEventLogDao.UpdateEventLogAsync(IEnumerable<EventLog> eventLogs)
{
_demoContext.EventLogs.UpdateRange(eventLogs);
return _demoContext.SaveChangesAsync();
}
}
新增Service
public interface IEventLogService
{
Task<List<GetEventLogResponse>> GetEventLogsAsync();
Task<GetEventLogResponse> GetEventLogAsync(long id);
Task<int> CreateEventLogAsync(IEnumerable<EventLog> eventLogs);
Task<int> RemoveEventLogAsync(IEnumerable<EventLog> eventLogs);
Task<int> UpdateEventLogAsync(IEnumerable<EventLog> eventLogs);
}
internal sealed class EventLogService : IEventLogService
{
private readonly IEventLogDao _eventLogDao;
public EventLogService(IEventLogDao eventLogDao)
{
_eventLogDao = eventLogDao;
}
Task<int> IEventLogService.CreateEventLogAsync(IEnumerable<EventLog> eventLogs)
{
return _eventLogDao.AddEventLogAsync(eventLogs);
}
Task<GetEventLogResponse> IEventLogService.GetEventLogAsync(long id)
{
return _eventLogDao.GetEventLogAsync(id);
}
Task<List<GetEventLogResponse>> IEventLogService.GetEventLogsAsync()
{
return _eventLogDao.GetEventLogsAsync();
}
Task<int> IEventLogService.RemoveEventLogAsync(IEnumerable<EventLog> eventLogs)
{
return _eventLogDao.DeleteEventLogAsync(eventLogs);
}
Task<int> IEventLogService.UpdateEventLogAsync(IEnumerable<EventLog> eventLogs)
{
return _eventLogDao.UpdateEventLogAsync(eventLogs);
}
}
新增Controller
[Route("api/eventlog")]
[ApiController]
public class EventLogController : ControllerBase
{
private readonly IEventLogService _eventLogService;
public EventLogController(IEventLogService eventLogService)
{
_eventLogService = eventLogService;
}
[HttpGet]
[Route("all")]
public async Task<IEnumerable<GetEventLogResponse>> GetEventLogsAsync()
{
return await _eventLogService.GetEventLogsAsync();
}
[HttpGet]
[Route("{id}")]
public async Task<GetEventLogResponse> GetEventLogAsync(long id)
{
return await _eventLogService.GetEventLogAsync(id);
}
}
新增IServiceCollectionExtension
public static class IServiceCollectionExtension
{
public static IServiceCollection AddEventLog(this IServiceCollection services)
{
services.AddScoped<IEventLogDao, EventLogDao>();
services.AddScoped<IEventLogService, EventLogService>();
return services;
}
}
修改startup.cs,注入相關Service
先簡單用postman測試API
到這裡API就算完成了,下面繼續Angular UI部分。
app下新增service folder,並透過CLI新增Angular service
ng g service /service/eventlog
修改eventlog.service.ts
import { Injectable, Inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Router } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
@Injectable()
export class EventlogService {
myAppUrl: string = "";
constructor(private _http: HttpClient, @Inject('BASE_URL') baseUrl: string) {
this.myAppUrl = baseUrl;
}
getEventLogs() {
// HttpClient.get() applies res.json() automatically and returns
return this._http.get(this.myAppUrl + 'api/eventlog/all')
.catch(this.errorHandler);
}
getEventLogsById(id: number) {
return this._http.get(this.myAppUrl + "api/eventlog/" + id)
.catch(this.errorHandler)
}
errorHandler(error: Response) {
console.log(error);
return Observable.throw(error);
}
}
透過CLI新增Angular component
ng g component eventlog
修改app.module.ts,新增eventlog的router和provider
Import { EventlogService } from './service/eventlog.service'
修改nav-menu.component.html,新增EventLog menu
<li [routerLinkActive]='["link-active"]'>
<a [routerLink]='["/eventlog"]' (click)='collapse()'>
<span class='glyphicon glyphicon-th-list'></span> EventLog
</a>
</li>
修改eventlog.component.html
<h1>EventLog View</h1>
<p *ngIf="!eventlogs"><em>Loading...</em></p>
<p>
<a [routerLink]="['/eventlog']" (click)="getEventLogs()">Index</a> |
<a [routerLink]="['/addeventlog']">Create New</a>
</p>
<table class='table' *ngIf="eventlogs">
<thead>
<tr>
<th>Id</th>
<th>EventID</th>
<th>Message</th>
<th>CreatedTime</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let eventlog of eventlogs">
<td> <a [routerLink]="" (click)="getEventLogsById(eventlog.id)">{{ eventlog.id }}</a></td>
<td>{{ eventlog.eventID }}</td>
<td>{{ eventlog.message }}</td>
<td>{{ eventlog.createdTime }}</td>
<td>
<a [routerLink]="['/eventlog/edit/', eventlog.id]">Edit</a> |
<a [routerLink]="" (click)="delete(eventlog.id)">Delete</a>
</td>
</tr>
</tbody>
</table>
修改eventlog.component.ts
import { Component, OnInit, Inject } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { HttpClient } from '@angular/common/http';
import { Time } from '@angular/common';
import { EventlogService } from '../service/eventlog.service'
@Component({
selector: 'app-eventlog',
templateUrl: './eventlog.component.html',
styleUrls: ['./eventlog.component.css']
})
export class EventlogComponent implements OnInit {
public eventlogs: EventLogs[];
constructor(public http: HttpClient, private _router: Router, private _eventLogService: EventlogService) {
this.getEventLogs();
}
ngOnInit() {
}
getEventLogs() {
this._eventLogService.getEventLogs().subscribe(result =>
this.eventlogs = result
)
}
getEventLogsById(id: number) {
this._eventLogService.getEventLogsById(id).subscribe(result =>
this.eventlogs = result
)
}
}
interface EventLogs {
id: number;
eventID: number;
message: string;
createdTime: Date;
}
到這裡我們已經完成透過Angular 6來顯示來自webapi的資料。
目前Angular專案部分結構
結果
篇幅有點長,後面我們在繼續Create、Update、Delete相關實作。
參考
Use the Angular project template with ASP.NET Core
Upgrade Angular 5 app to Angular 6 with Visual Studio 2017
Use DbContextPooling to improve the performance: .Net Core 2.1 feature
CRUD Operations With ASP.NET Core Using Angular 5 and ADO.NET