这是一篇学习笔记. angular 5 正式版都快出了, 不过主要是性能升级.
我认为angular 4还是很适合企业的, 就像.net一样.
我用的是windows 10

安装工具:

git for windows: 官网很慢, 所以找一个镜像站下载: https://github.com/waylau/git-for-win, 淘宝镜像的速度还是蛮快的:
使用angular4和asp.net core 2 web api做个练习项目(一) - 图2
安装的时候, 建议选择这个, 会添加很多命令行工具:

使用angular4和asp.net core 2 web api做个练习项目(一) - 图3

nodejs: 去官网下载就行: https://nodejs.org/en/
正常安装即可. npm的版本不要低于5.0吧:
使用angular4和asp.net core 2 web api做个练习项目(一) - 图4
angular-cli, 官网: https://github.com/angular/angular-cli
npm install -g @angular/cli
visual studio code: https://code.visualstudio.com/
and visual studio 2017 of course.

建立angular项目

进入命令行在某个地方执行命令:
ng new client-panel
这就会建立一个client-panel文件夹, 里面是该项目的文件, 然后它会立即执行npm install命令(这里不要使用淘宝的cnpm进行安装, 有bug), 稍等一会就会结束.
使用vscode打开该目录, 然后在vscode里面打开terminal:
使用angular4和asp.net core 2 web api做个练习项目(一) - 图5
terminal默认的可能是powershell, 如果你感觉powershell有点慢的话, 可以换成bash(安装git时候带的)或者windows command line等.
第一次打开terminal的时候, vscode上方会提示你配置terminal, 这时就可以更换默认的terminal. 否则的话, 你可以点击菜单file-reference-settings, 自己选择一个terminal应用:
使用angular4和asp.net core 2 web api做个练习项目(一) - 图6
同样可以安装几个vscode的插件:
使用angular4和asp.net core 2 web api做个练习项目(一) - 图7
然后试运行一下项目, 在terminal执行 ng serve, 如果没问题的话, 大概是这样:
使用angular4和asp.net core 2 web api做个练习项目(一) - 图8
浏览器运行: http://localhost:4200
使用angular4和asp.net core 2 web api做个练习项目(一) - 图9

安装bootstrap4等:

安装bootstrap4, tether, jquery等:
npm install bootstrap@4.0.0-beta.2 tether jquery —save
安装成功后, 打开 .angular-cli.json, 把相关的css和js添加进去:
使用angular4和asp.net core 2 web api做个练习项目(一) - 图10
然后在运行试试 ng serve, 刷新:
使用angular4和asp.net core 2 web api做个练习项目(一) - 图11
字体已经改变, bootstrap起作用了.

建立Components

建立dashboard:

terminal执行
ng g component components/dashboard
执行成功后会生成4个文件:
使用angular4和asp.net core 2 web api做个练习项目(一) - 图12
并且会自动在app.module.ts里面声明:
使用angular4和asp.net core 2 web api做个练习项目(一) - 图13

建立其他 components:

  1. ng g component components/clients
  2. ng g component components/clientDetails
  3. ng g component components/addClient
  4. ng g component components/editClient
  5. ng g component components/navbar
  6. ng g component components/sidebar
  7. ng g component components/login
  8. ng g component components/register
  9. ng g component components/settings
  10. ng g component components/pageNotFound

使用angular4和asp.net core 2 web api做个练习项目(一) - 图14

建立Route路由

  1. import { BrowserModule } from '@angular/platform-browser';
  2. import { NgModule } from '@angular/core';
  3. import { RouterModule, Routes } from '@angular/router';
  4. import { AppComponent } from './app.component';
  5. import { DashboardComponent } from './components/dashboard/dashboard.component';
  6. import { ClientsComponent } from './components/clients/clients.component';
  7. import { ClientDetailsComponent } from './components/client-details/client-details.component';
  8. import { AddClientComponent } from './components/add-client/add-client.component';
  9. import { EditClientComponent } from './components/edit-client/edit-client.component';
  10. import { NavbarComponent } from './components/navbar/navbar.component';
  11. import { SidebarComponent } from './components/sidebar/sidebar.component';
  12. import { LoginComponent } from './components/login/login.component';
  13. import { RegisterComponent } from './components/register/register.component';
  14. import { SettingsComponent } from './components/settings/settings.component';
  15. import { PageNotFoundComponent } from './components/page-not-found/page-not-found.component';
  16. const appRoutes: Routes = [
  17. { path: '', component: DashboardComponent },
  18. { path: 'register', component: RegisterComponent },
  19. { path: 'login', component: LoginComponent }
  20. ];
  21. @NgModule({
  22. declarations: [
  23. AppComponent,
  24. DashboardComponent,
  25. ClientsComponent,
  26. ClientDetailsComponent,
  27. AddClientComponent,
  28. EditClientComponent,
  29. NavbarComponent,
  30. SidebarComponent,
  31. LoginComponent,
  32. RegisterComponent,
  33. SettingsComponent,
  34. PageNotFoundComponent
  35. ],
  36. imports: [
  37. BrowserModule,
  38. RouterModule.forRoot(appRoutes)
  39. ],
  40. providers: [],
  41. bootstrap: [AppComponent]
  42. })
  43. export class AppModule { }

添加router-outlet:

打开app.component.html, 清空内容, 添加一个div(可以输入div.container然后按tab健):




现在刷新浏览器, 大约这样:
使用angular4和asp.net core 2 web api做个练习项目(一) - 图15

添加navbar:

修改navbar.component.html:

  1. <nav class="navbar navbar-expand-md navbar-light bg-light">
  2. <div class="container">
  3. <a class="navbar-brand" href="#">Client Panel</a>
  4. <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault" aria-controls="navbarsExampleDefault"
  5. aria-expanded="false" aria-label="Toggle navigation">
  6. <span class="navbar-toggler-icon"></span>
  7. </button>
  8. <div class="collapse navbar-collapse" id="navbarsExampleDefault">
  9. <ul class="navbar-nav mr-auto">
  10. <li class="nav-item">
  11. <a class="nav-link" href="#" routerLink="/">Dashboard </a>
  12. </li>
  13. </ul>
  14. <ul class="navbar-nav ml-auto">
  15. <li class="nav-item">
  16. <a class="nav-link" href="#" routerLink="/register">Register </a>
  17. </li>
  18. <li class="nav-item">
  19. <a class="nav-link" href="#" routerLink="/login">Login </a>
  20. </li>
  21. </ul>
  22. </div>
  23. </div>
  24. </nav>

修改app.component.html:

  1. <app-navbar></app-navbar>
  2. <div class="container">
  3. <router-outlet></router-outlet>
  4. </div>

运行:
使用angular4和asp.net core 2 web api做个练习项目(一) - 图16

建立Service

建立一个client.service:
ng g service services/client
然后在app.module.ts添加引用:
// Services Imports
import { ClientService } from “./services/client.service”;
并添加在providers里:
providers: [
ClientService
],
前端先暂时到这, 现在开始搞后端 web api.

建立asp.net core 2.0 的 Web api项目

web api项目源码: https://github.com/solenovex/asp.net-core-2.0-web-api-boilerplate
项目列表如图:
使用angular4和asp.net core 2 web api做个练习项目(一) - 图17
AspNetIdentityAuthorizationServer是一个单独的authorization server, 这里暂时还没用到, 它的端口是5000, 默认不启动.
CoreApi.Infrastructure 里面有一些基类和接口, 还放了一个公共的工具类等.
CoreApi.Models就是 models/entities
CoreApi.DataContext 里面就是DbContext相关的
CoreApi.Repositories 里面是Repositories
CoreApi.Services 里面就是各种services
CoreApi.ViewModels 里面就是各种ViewModels或者叫Dtos
CoreApi.Web是web启动项目.
SharedSettings是横跨authorization server和 web api的一些公共设置.
上面说的这些都没什么用, 下面开始建立Client的api.

建立Client Model(或者叫Entity)

在CoreApi.Models建立文件夹Angular, 然后建立Client.cs:

  1. using CoreApi.Infrastructure.Features.Common;
  2. using Microsoft.EntityFrameworkCore;
  3. using Microsoft.EntityFrameworkCore.Metadata.Builders;
  4. namespace CoreApi.Models.Angular
  5. {
  6. public class Client : EntityBase
  7. {
  8. public decimal Balance { get; set; }
  9. public string Email { get; set; }
  10. public string FirstName { get; set; }
  11. public string LastName { get; set; }
  12. public string Phone { get; set; }
  13. }
  14. public class ClientConfiguration : EntityBaseConfiguration<Client>
  15. {
  16. public override void ConfigureDerived(EntityTypeBuilder<Client> builder)
  17. {
  18. builder.Property(x => x.Balance).HasColumnType("decimal(18,2)");
  19. builder.Property(x => x.Email).IsRequired().HasMaxLength(100);
  20. builder.Property(x => x.FirstName).IsRequired().HasMaxLength(50);
  21. builder.Property(x => x.LastName).IsRequired().HasMaxLength(50);
  22. builder.Property(x => x.Phone).HasMaxLength(50);
  23. }
  24. }
  25. }

其中父类EntityBase里面含有一些通用属性,Id, CreateUser, UpdateUser, CreateTime, UpdateTime, LastAction, 这些是我公司做项目必须的, 你们随意.
下面ClientConfiguration是针对Client的fluent api配置类. 他的父类EntityBaseConfiguration实现了EF的IEntityTypeConfiguration接口, 并在父类里面针对EntityBase那些属性使用fluent api做了限制:

  1. namespace CoreApi.Infrastructure.Features.Common
  2. {
  3. public abstract class EntityBaseConfiguration<T> : IEntityTypeConfiguration<T> where T : EntityBase
  4. {
  5. public virtual void Configure(EntityTypeBuilder<T> builder)
  6. {
  7. builder.HasKey(e => e.Id);
  8. builder.Property(x => x.CreateTime).IsRequired();
  9. builder.Property(x => x.UpdateTime).IsRequired();
  10. builder.Property(x => x.CreateUser).IsRequired().HasMaxLength(50);
  11. builder.Property(x => x.UpdateUser).IsRequired().HasMaxLength(50);
  12. builder.Property(x => x.LastAction).IsRequired().HasMaxLength(50);
  13. ConfigureDerived(builder);
  14. }
  15. public abstract void ConfigureDerived(EntityTypeBuilder<T> b);
  16. }
  17. }

弄完Model和它的配置之后, 就添加到DbContext里面. 打开CoreApi.DataContext的CoreContext, 添加Model和配置:

  1. protected override void OnModelCreating(ModelBuilder modelBuilder)
  2. {
  3. base.OnModelCreating(modelBuilder);
  4. modelBuilder.HasDefaultSchema(AppSettings.DefaultSchema);
  5. modelBuilder.ApplyConfiguration(new UploadedFileConfiguration());
  6. modelBuilder.ApplyConfiguration(new ClientConfiguration());
  7. }
  8. public DbSet<UploadedFile> UploadedFiles { get; set; }
  9. public DbSet<Client> Clients { get; set; }


然后建立ClientRepository

在CoreApi.Repositories里面建立Angular目录, 建立ClientRepository.cs:

  1. namespace CoreApi.Repositories.Angular
  2. {
  3. public interface IClientRepository : IEntityBaseRepository<Client> { }
  4. public class ClientRepository : EntityBaseRepository<Client>, IClientRepository
  5. {
  6. public ClientRepository(IUnitOfWork unitOfWork) : base(unitOfWork)
  7. {
  8. }
  9. }
  10. }

图省事, 我把repository和它的interface放在一个文件了.
IEntityBaseRepository定义了一些常用的方法:

  1. namespace CoreApi.DataContext.Infrastructure
  2. {
  3. public interface IEntityBaseRepository<T> where T : class, IEntityBase, new()
  4. {
  5. IQueryable<T> All { get; }
  6. IQueryable<T> AllIncluding(params Expression<Func<T, object>>[] includeProperties);
  7. int Count();
  8. Task<int> CountAsync();
  9. T GetSingle(int id);
  10. Task<T> GetSingleAsync(int id);
  11. T GetSingle(Expression<Func<T, bool>> predicate);
  12. Task<T> GetSingleAsync(Expression<Func<T, bool>> predicate);
  13. T GetSingle(Expression<Func<T, bool>> predicate, params Expression<Func<T, object>>[] includeProperties);
  14. Task<T> GetSingleAsync(Expression<Func<T, bool>> predicate, params Expression<Func<T, object>>[] includeProperties);
  15. IQueryable<T> FindBy(Expression<Func<T, bool>> predicate);
  16. void Add(T entity);
  17. void Update(T entity);
  18. void Delete(T entity);
  19. void DeleteWhere(Expression<Func<T, bool>> predicate);
  20. void AddRange(IEnumerable<T> entities);
  21. void DeleteRange(IEnumerable<T> entities);
  22. void Attach(T entity);
  23. void AttachRange(IEnumerable<T> entities);
  24. void Detach(T entity);
  25. void DetachRange(IEnumerable<T> entities);
  26. void AttachAsModified(T entity);
  27. }
  28. }

EntityBaseRepository是它的实现:

  1. namespace CoreApi.DataContext.Infrastructure
  2. {
  3. public class EntityBaseRepository<T> : IEntityBaseRepository<T>
  4. where T : class, IEntityBase, new()
  5. {
  6. #region Properties
  7. protected CoreContext Context { get; }
  8. public EntityBaseRepository(IUnitOfWork unitOfWork)
  9. {
  10. Context = unitOfWork as CoreContext;
  11. }
  12. #endregion
  13. public virtual IQueryable<T> All => Context.Set<T>();
  14. public virtual IQueryable<T> AllIncluding(params Expression<Func<T, object>>[] includeProperties)
  15. {
  16. IQueryable<T> query = Context.Set<T>();
  17. foreach (var includeProperty in includeProperties)
  18. {
  19. query = query.Include(includeProperty);
  20. }
  21. return query;
  22. }
  23. public virtual int Count()
  24. {
  25. return Context.Set<T>().Count();
  26. }
  27. public async Task<int> CountAsync()
  28. {
  29. return await Context.Set<T>().CountAsync();
  30. }
  31. public T GetSingle(int id)
  32. {
  33. return Context.Set<T>().FirstOrDefault(x => x.Id == id);
  34. }
  35. public async Task<T> GetSingleAsync(int id)
  36. {
  37. return await Context.Set<T>().FirstOrDefaultAsync(x => x.Id == id);
  38. }
  39. public T GetSingle(Expression<Func<T, bool>> predicate)
  40. {
  41. return Context.Set<T>().FirstOrDefault(predicate);
  42. }
  43. public async Task<T> GetSingleAsync(Expression<Func<T, bool>> predicate)
  44. {
  45. return await Context.Set<T>().FirstOrDefaultAsync(predicate);
  46. }
  47. public T GetSingle(Expression<Func<T, bool>> predicate, params Expression<Func<T, object>>[] includeProperties)
  48. {
  49. IQueryable<T> query = Context.Set<T>();
  50. foreach (var includeProperty in includeProperties)
  51. {
  52. query = query.Include(includeProperty);
  53. }
  54. return query.Where(predicate).FirstOrDefault();
  55. }
  56. public async Task<T> GetSingleAsync(Expression<Func<T, bool>> predicate, params Expression<Func<T, object>>[] includeProperties)
  57. {
  58. IQueryable<T> query = Context.Set<T>();
  59. foreach (var includeProperty in includeProperties)
  60. {
  61. query = query.Include(includeProperty);
  62. }
  63. return await query.Where(predicate).FirstOrDefaultAsync();
  64. }
  65. public virtual IQueryable<T> FindBy(Expression<Func<T, bool>> predicate)
  66. {
  67. return Context.Set<T>().Where(predicate);
  68. }
  69. public virtual void Add(T entity)
  70. {
  71. Context.Set<T>().Add(entity);
  72. }
  73. public virtual void Update(T entity)
  74. {
  75. EntityEntry<T> dbEntityEntry = Context.Entry(entity);
  76. dbEntityEntry.State = EntityState.Modified;
  77. dbEntityEntry.Property(x => x.Id).IsModified = false;
  78. dbEntityEntry.Property(x => x.CreateUser).IsModified = false;
  79. dbEntityEntry.Property(x => x.CreateTime).IsModified = false;
  80. }
  81. public virtual void Delete(T entity)
  82. {
  83. Context.Set<T>().Remove(entity);
  84. }
  85. public virtual void AddRange(IEnumerable<T> entities)
  86. {
  87. Context.Set<T>().AddRange(entities);
  88. }
  89. public virtual void DeleteRange(IEnumerable<T> entities)
  90. {
  91. foreach (var entity in entities)
  92. {
  93. Context.Set<T>().Remove(entity);
  94. }
  95. }
  96. public virtual void DeleteWhere(Expression<Func<T, bool>> predicate)
  97. {
  98. IEnumerable<T> entities = Context.Set<T>().Where(predicate);
  99. foreach (var entity in entities)
  100. {
  101. Context.Entry<T>(entity).State = EntityState.Deleted;
  102. }
  103. }
  104. public void Attach(T entity)
  105. {
  106. Context.Set<T>().Attach(entity);
  107. }
  108. public void AttachRange(IEnumerable<T> entities)
  109. {
  110. foreach (var entity in entities)
  111. {
  112. Attach(entity);
  113. }
  114. }
  115. public void Detach(T entity)
  116. {
  117. Context.Entry<T>(entity).State = EntityState.Detached;
  118. }
  119. public void DetachRange(IEnumerable<T> entities)
  120. {
  121. foreach (var entity in entities)
  122. {
  123. Detach(entity);
  124. }
  125. }
  126. public void AttachAsModified(T entity)
  127. {
  128. Attach(entity);
  129. Update(entity);
  130. }
  131. }
  132. }

建立Client的ViewModels

在CoreApi.ViewModels建立Angular文件夹, 分别针对查询, 新增, 修改建立3个ViewModel(Dto):

  1. namespace CoreApi.ViewModels.Angular
  2. {
  3. public class ClientViewModel : EntityBase
  4. {
  5. public decimal Balance { get; set; }
  6. public string Email { get; set; }
  7. public string FirstName { get; set; }
  8. public string LastName { get; set; }
  9. public string Phone { get; set; }
  10. }
  11. }

ClientCreationViewModel:

  1. namespace CoreApi.ViewModels.Angular
  2. {
  3. public class ClientCreationViewModel
  4. {
  5. public decimal Balance { get; set; }
  6. [Required]
  7. [MaxLength(100)]
  8. public string Email { get; set; }
  9. [Required]
  10. [MaxLength(50)]
  11. public string FirstName { get; set; }
  12. [Required]
  13. [MaxLength(50)]
  14. public string LastName { get; set; }
  15. [Required]
  16. [MaxLength(50)]
  17. public string Phone { get; set; }
  18. }
  19. }

ClientModificationViewModel:

  1. namespace CoreApi.ViewModels.Angular
  2. {
  3. public class ClientModificationViewModel
  4. {
  5. public decimal Balance { get; set; }
  6. [Required]
  7. [MaxLength(100)]
  8. public string Email { get; set; }
  9. [Required]
  10. [MaxLength(50)]
  11. public string FirstName { get; set; }
  12. [Required]
  13. [MaxLength(50)]
  14. public string LastName { get; set; }
  15. [Required]
  16. [MaxLength(50)]
  17. public string Phone { get; set; }
  18. }
  19. }

配置AutoMapper

针对Client和它的Viewmodels, 分别从两个方向进行配置:
DomainToViewModelMappingProfile:

  1. namespace CoreApi.Web.MyConfigurations
  2. {
  3. public class DomainToViewModelMappingProfile : Profile
  4. {
  5. public override string ProfileName => "DomainToViewModelMappings";
  6. public DomainToViewModelMappingProfile()
  7. {
  8. CreateMap<UploadedFile, UploadedFileViewModel>();
  9. CreateMap<Client, ClientViewModel>();
  10. CreateMap<Client, ClientModificationViewModel>();
  11. }
  12. }
  13. }

ViewModelToDomainMappingProfile:

  1. namespace CoreApi.Web.MyConfigurations
  2. {
  3. public class ViewModelToDomainMappingProfile : Profile
  4. {
  5. public override string ProfileName => "ViewModelToDomainMappings";
  6. public ViewModelToDomainMappingProfile()
  7. {
  8. CreateMap<UploadedFileViewModel, UploadedFile>();
  9. CreateMap<ClientViewModel, Client>();
  10. CreateMap<ClientCreationViewModel, Client>();
  11. CreateMap<ClientModificationViewModel, Client>();
  12. }
  13. }
  14. }

注册Repository的DI:

在web项目的StartUp.cs的ConfigureServices里面为ClientRepository注册DI:
services.AddScoped();

建立Controller

在controllers目录建立Angular/ClientController.cs:

  1. namespace CoreApi.Web.Controllers.Angular
  2. {
  3. [Route("api/[controller]")]
  4. public class ClientController : BaseController<ClientController>
  5. {
  6. private readonly IClientRepository _clientRepository;
  7. public ClientController(ICoreService<ClientController> coreService,
  8. IClientRepository clientRepository) : base(coreService)
  9. {
  10. _clientRepository = clientRepository;
  11. }
  12. [HttpGet]
  13. public async Task<IActionResult> GetAll()
  14. {
  15. var items = await _clientRepository.All.ToListAsync();
  16. var results = Mapper.Map<IEnumerable<ClientViewModel>>(items);
  17. return Ok(results);
  18. }
  19. [HttpGet]
  20. [Route("{id}", Name = "GetClient")]
  21. public async Task<IActionResult> Get(int id)
  22. {
  23. var item = await _clientRepository.GetSingleAsync(id);
  24. if (item == null)
  25. {
  26. return NotFound();
  27. }
  28. var result = Mapper.Map<ClientViewModel>(item);
  29. return Ok(result);
  30. }
  31. [HttpPost]
  32. public async Task<IActionResult> Post([FromBody] ClientCreationViewModel clientVm)
  33. {
  34. if (clientVm == null)
  35. {
  36. return BadRequest();
  37. }
  38. if (!ModelState.IsValid)
  39. {
  40. return BadRequest(ModelState);
  41. }
  42. var newItem = Mapper.Map<Client>(clientVm);
  43. newItem.SetCreation(UserName);
  44. _clientRepository.Add(newItem);
  45. if (!await UnitOfWork.SaveAsync())
  46. {
  47. return StatusCode(500, "保存客户时出错");
  48. }
  49. var vm = Mapper.Map<ClientViewModel>(newItem);
  50. return CreatedAtRoute("GetClient", new { id = vm.Id }, vm);
  51. }
  52. [HttpPut("{id}")]
  53. public async Task<IActionResult> Put(int id, [FromBody] ClientModificationViewModel clientVm)
  54. {
  55. if (clientVm == null)
  56. {
  57. return BadRequest();
  58. }
  59. if (!ModelState.IsValid)
  60. {
  61. return BadRequest(ModelState);
  62. }
  63. var dbItem = await _clientRepository.GetSingleAsync(id);
  64. if (dbItem == null)
  65. {
  66. return NotFound();
  67. }
  68. Mapper.Map(clientVm, dbItem);
  69. dbItem.SetModification(UserName);
  70. _clientRepository.Update(dbItem);
  71. if (!await UnitOfWork.SaveAsync())
  72. {
  73. return StatusCode(500, "保存客户时出错");
  74. }
  75. return NoContent();
  76. }
  77. [HttpPatch("{id}")]
  78. public async Task<IActionResult> Patch(int id, [FromBody] JsonPatchDocument<ClientModificationViewModel> patchDoc)
  79. {
  80. if (patchDoc == null)
  81. {
  82. return BadRequest();
  83. }
  84. var dbItem = await _clientRepository.GetSingleAsync(id);
  85. if (dbItem == null)
  86. {
  87. return NotFound();
  88. }
  89. var toPatchVm = Mapper.Map<ClientModificationViewModel>(dbItem);
  90. patchDoc.ApplyTo(toPatchVm, ModelState);
  91. TryValidateModel(toPatchVm);
  92. if (!ModelState.IsValid)
  93. {
  94. return BadRequest(ModelState);
  95. }
  96. Mapper.Map(toPatchVm, dbItem);
  97. if (!await UnitOfWork.SaveAsync())
  98. {
  99. return StatusCode(500, "更新的时候出错");
  100. }
  101. return NoContent();
  102. }
  103. [HttpDelete("{id}")]
  104. public async Task<IActionResult> Delete(int id)
  105. {
  106. var model = await _clientRepository.GetSingleAsync(id);
  107. if (model == null)
  108. {
  109. return NotFound();
  110. }
  111. _clientRepository.Delete(model);
  112. if (!await UnitOfWork.SaveAsync())
  113. {
  114. return StatusCode(500, "删除的时候出错");
  115. }
  116. return NoContent();
  117. }
  118. }
  119. }

首先, Controller继承了ControllerBase这个类, ControllerBase是自己写的类, 里面可以放置一些公用的方法或属性, 目前里面的东西都没用:

  1. namespace CoreApi.Web.Controllers.Bases
  2. {
  3. public abstract class BaseController<T> : Controller
  4. {
  5. protected readonly IUnitOfWork UnitOfWork;
  6. protected readonly ILogger<T> Logger;
  7. protected readonly IFileProvider FileProvider;
  8. protected readonly ICoreService<T> CoreService;
  9. protected BaseController(ICoreService<T> coreService)
  10. {
  11. CoreService = coreService;
  12. UnitOfWork = coreService.UnitOfWork;
  13. Logger = coreService.Logger;
  14. FileProvider = coreService.FileProvider;
  15. }
  16. #region Current Information
  17. protected DateTime Now => DateTime.Now;
  18. protected string UserName => User.Identity.Name ?? "Anonymous";
  19. #endregion
  20. }
  21. }

由于父类构造函数依赖的类太多了, 所以我建立了一个CoreService, 里面包含着这些依赖, 然后用一个变量就注入进去了, 这种写法不一定正确:

  1. public interface ICoreService<out T> : IDisposable
  2. {
  3. IUnitOfWork UnitOfWork { get; }
  4. ILogger<T> Logger { get; }
  5. IFileProvider FileProvider { get; }
  6. }

Controller里面的方法应该都能看明白吧. 需要提一下的是UnitOfWork.

Unit Of Work

我才用的是UnitOfWork和Repository模式, 多个Repository挂起的数据库操作, 可以使用一个UnitOfWork一次性提交.
由于DBContext已经实现了UnitOfWork模式, 所以可以直接在Controller里面使用DbContext, 但是我还是做了一个接口 IUnitOfWork:

  1. namespace CoreApi.DataContext.Infrastructure
  2. {
  3. public interface IUnitOfWork: IDisposable
  4. {
  5. int SaveChanges();
  6. int SaveChanges(bool acceptAllChangesOnSuccess);
  7. Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default(CancellationToken));
  8. Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken));
  9. bool Save();
  10. bool Save(bool acceptAllChangesOnSuccess);
  11. Task<bool> SaveAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default(CancellationToken));
  12. Task<bool> SaveAsync(CancellationToken cancellationToken = default(CancellationToken));
  13. }
  14. }

里面前4个方法就是DbContext内置的方法, 后面4个方法可有可无, 就是上面4个方法的简单变形.

  1. namespace CoreApi.DataContext.Core
  2. {
  3. public class CoreContext : DbContext, IUnitOfWork
  4. {
  5. public CoreContext(DbContextOptions<CoreContext> options)
  6. : base(options)
  7. {
  8. }
  9. protected override void OnModelCreating(ModelBuilder modelBuilder)
  10. {
  11. base.OnModelCreating(modelBuilder);
  12. modelBuilder.HasDefaultSchema(AppSettings.DefaultSchema);
  13. modelBuilder.ApplyConfiguration(new UploadedFileConfiguration());
  14. modelBuilder.ApplyConfiguration(new ClientConfiguration());
  15. }
  16. public DbSet<UploadedFile> UploadedFiles { get; set; }
  17. public DbSet<Client> Clients { get; set; }
  18. public bool Save()
  19. {
  20. return SaveChanges() >= 0;
  21. }
  22. public bool Save(bool acceptAllChangesOnSuccess)
  23. {
  24. return SaveChanges(acceptAllChangesOnSuccess) >= 0;
  25. }
  26. public async Task<bool> SaveAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default(CancellationToken))
  27. {
  28. return await SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken) >= 0;
  29. }
  30. public async Task<bool> SaveAsync(CancellationToken cancellationToken = default(CancellationToken))
  31. {
  32. return await SaveChangesAsync(cancellationToken) >= 0;
  33. }
  34. }
  35. }

差不多了, 开始

迁移数据库

在Package Manager Console分别执行 Add-Migration XXX和 Update-database命令.
注意这个时候 解决方案的启动项目必须是Web项目, 如果设置了多个启动项目, 迁移命令会不太好用.
然后运行一下: 选择CoreApi.Web而不是IISExpress, 这样的话端口应该是 http://localhost:5001/api/values
使用angular4和asp.net core 2 web api做个练习项目(一) - 图18

到Swagger里简单测试下

然后进入swagger简单测试一下ClientController: http://localhost:5001/swagger/
使用angular4和asp.net core 2 web api做个练习项目(一) - 图19

先添加数据 POST:

使用angular4和asp.net core 2 web api做个练习项目(一) - 图20
先点击右侧, 然后会把数据的json模板复制到左边的框里, 然后修改值, 然后点击try It out, 结果如下:
使用angular4和asp.net core 2 web api做个练习项目(一) - 图21
然后两个Get, Delete, Put您都应该会测试.
这里试一下 Patch:
使用angular4和asp.net core 2 web api做个练习项目(一) - 图22
再查询一下, 应该没有什么问题.
先写到这, 明天就能差不多写完了吧.