使用angular4和asp.net core 2 web api做个练习项目(一)
这是一篇学习笔记. angular 5 正式版都快出了, 不过主要是性能升级.
我认为angular 4还是很适合企业的, 就像.net一样.
我用的是windows 10
安装工具:
git for windows: 官网很慢, 所以找一个镜像站下载: https://github.com/waylau/git-for-win, 淘宝镜像的速度还是蛮快的:
安装的时候, 建议选择这个, 会添加很多命令行工具:
nodejs: 去官网下载就行: https://nodejs.org/en/
正常安装即可. npm的版本不要低于5.0吧:
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:
terminal默认的可能是powershell, 如果你感觉powershell有点慢的话, 可以换成bash(安装git时候带的)或者windows command line等.
第一次打开terminal的时候, vscode上方会提示你配置terminal, 这时就可以更换默认的terminal. 否则的话, 你可以点击菜单file-reference-settings, 自己选择一个terminal应用:
同样可以安装几个vscode的插件:
然后试运行一下项目, 在terminal执行 ng serve, 如果没问题的话, 大概是这样:
浏览器运行: http://localhost:4200
安装bootstrap4等:
安装bootstrap4, tether, jquery等:
- npm install bootstrap@4.0.0-beta.2 tether jquery --save
安装成功后, 打开 .angular-cli.json, 把相关的css和js添加进去:
然后在运行试试 ng serve, 刷新:
字体已经改变, bootstrap起作用了.
建立Components
建立dashboard:
terminal执行
- ng g component components/dashboard
执行成功后会生成4个文件:
并且会自动在app.module.ts里面声明:
建立其他 components:
- ng g component components/clients
- ng g component components/clientDetails
- ng g component components/addClient
- ng g component components/editClient
- ng g component components/navbar
- ng g component components/sidebar
ng g component components/login
ng g component components/register
ng g component components/settings
ng g component components/pageNotFound
建立Route路由
- import { BrowserModule } from '@angular/platform-browser';
- import { NgModule } from '@angular/core';
- import { RouterModule, Routes } from '@angular/router';
- import { AppComponent } from './app.component';
- import { DashboardComponent } from './components/dashboard/dashboard.component';
- import { ClientsComponent } from './components/clients/clients.component';
- import { ClientDetailsComponent } from './components/client-details/client-details.component';
- import { AddClientComponent } from './components/add-client/add-client.component';
- import { EditClientComponent } from './components/edit-client/edit-client.component';
- import { NavbarComponent } from './components/navbar/navbar.component';
- import { SidebarComponent } from './components/sidebar/sidebar.component';
- import { LoginComponent } from './components/login/login.component';
- import { RegisterComponent } from './components/register/register.component';
- import { SettingsComponent } from './components/settings/settings.component';
- import { PageNotFoundComponent } from './components/page-not-found/page-not-found.component';
- const appRoutes: Routes = [
- { path: '', component: DashboardComponent },
- { path: 'register', component: RegisterComponent },
- { path: 'login', component: LoginComponent }
- ];
- @NgModule({
- declarations: [
- AppComponent,
- DashboardComponent,
- ClientsComponent,
- ClientDetailsComponent,
- AddClientComponent,
- EditClientComponent,
- NavbarComponent,
- SidebarComponent,
- LoginComponent,
- RegisterComponent,
- SettingsComponent,
- PageNotFoundComponent
- ],
- imports: [
- BrowserModule,
- RouterModule.forRoot(appRoutes)
- ],
- providers: [],
- bootstrap: [AppComponent]
- })
- export class AppModule { }
添加router-outlet:
打开app.component.html, 清空内容, 添加一个div(可以输入div.container然后按tab健):
- <div class="container">
- <router-outlet></router-outlet>
- </div>
现在刷新浏览器, 大约这样:
添加navbar:
修改navbar.component.html:
- <nav class="navbar navbar-expand-md navbar-light bg-light">
- <div class="container">
- <a class="navbar-brand" href="#">Client Panel</a>
- <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault" aria-controls="navbarsExampleDefault"
- aria-expanded="false" aria-label="Toggle navigation">
- <span class="navbar-toggler-icon"></span>
- </button>
- <div class="collapse navbar-collapse" id="navbarsExampleDefault">
- <ul class="navbar-nav mr-auto">
- <li class="nav-item">
- <a class="nav-link" href="#" routerLink="/">Dashboard </a>
- </li>
- </ul>
- <ul class="navbar-nav ml-auto">
- <li class="nav-item">
- <a class="nav-link" href="#" routerLink="/register">Register </a>
- </li>
- <li class="nav-item">
- <a class="nav-link" href="#" routerLink="/login">Login </a>
- </li>
- </ul>
- </div>
- </div>
- </nav>
修改app.component.html:
- <app-navbar></app-navbar>
- <div class="container">
- <router-outlet></router-outlet>
- </div>
运行:
建立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
项目列表如图:
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:
- using CoreApi.Infrastructure.Features.Common;
- using Microsoft.EntityFrameworkCore;
- using Microsoft.EntityFrameworkCore.Metadata.Builders;
- namespace CoreApi.Models.Angular
- {
- public class Client : EntityBase
- {
- public decimal Balance { get; set; }
- public string Email { get; set; }
- public string FirstName { get; set; }
- public string LastName { get; set; }
- public string Phone { get; set; }
- }
- public class ClientConfiguration : EntityBaseConfiguration<Client>
- {
- public override void ConfigureDerived(EntityTypeBuilder<Client> builder)
- {
- builder.Property(x => x.Balance).HasColumnType("decimal(18,2)");
- builder.Property(x => x.Email).IsRequired().HasMaxLength();
- builder.Property(x => x.FirstName).IsRequired().HasMaxLength();
- builder.Property(x => x.LastName).IsRequired().HasMaxLength();
- builder.Property(x => x.Phone).HasMaxLength();
- }
- }
- }
其中父类EntityBase里面含有一些通用属性,Id, CreateUser, UpdateUser, CreateTime, UpdateTime, LastAction, 这些是我公司做项目必须的, 你们随意.
下面ClientConfiguration是针对Client的fluent api配置类. 他的父类EntityBaseConfiguration实现了EF的IEntityTypeConfiguration接口, 并在父类里面针对EntityBase那些属性使用fluent api做了限制:
- namespace CoreApi.Infrastructure.Features.Common
- {
- public abstract class EntityBaseConfiguration<T> : IEntityTypeConfiguration<T> where T : EntityBase
- {
- public virtual void Configure(EntityTypeBuilder<T> builder)
- {
- builder.HasKey(e => e.Id);
- builder.Property(x => x.CreateTime).IsRequired();
- builder.Property(x => x.UpdateTime).IsRequired();
- builder.Property(x => x.CreateUser).IsRequired().HasMaxLength();
- builder.Property(x => x.UpdateUser).IsRequired().HasMaxLength();
- builder.Property(x => x.LastAction).IsRequired().HasMaxLength();
- ConfigureDerived(builder);
- }
- public abstract void ConfigureDerived(EntityTypeBuilder<T> b);
- }
- }
弄完Model和它的配置之后, 就添加到DbContext里面. 打开CoreApi.DataContext的CoreContext, 添加Model和配置:
- protected override void OnModelCreating(ModelBuilder modelBuilder)
- {
- base.OnModelCreating(modelBuilder);
- modelBuilder.HasDefaultSchema(AppSettings.DefaultSchema);
- modelBuilder.ApplyConfiguration(new UploadedFileConfiguration());
- modelBuilder.ApplyConfiguration(new ClientConfiguration());
- }
- public DbSet<UploadedFile> UploadedFiles { get; set; }
- public DbSet<Client> Clients { get; set; }
然后建立ClientRepository
在CoreApi.Repositories里面建立Angular目录, 建立ClientRepository.cs:
- namespace CoreApi.Repositories.Angular
- {
- public interface IClientRepository : IEntityBaseRepository<Client> { }
- public class ClientRepository : EntityBaseRepository<Client>, IClientRepository
- {
- public ClientRepository(IUnitOfWork unitOfWork) : base(unitOfWork)
- {
- }
- }
- }
图省事, 我把repository和它的interface放在一个文件了.
IEntityBaseRepository<T>定义了一些常用的方法:
- namespace CoreApi.DataContext.Infrastructure
- {
- public interface IEntityBaseRepository<T> where T : class, IEntityBase, new()
- {
- IQueryable<T> All { get; }
- IQueryable<T> AllIncluding(params Expression<Func<T, object>>[] includeProperties);
- int Count();
- Task<int> CountAsync();
- T GetSingle(int id);
- Task<T> GetSingleAsync(int id);
- T GetSingle(Expression<Func<T, bool>> predicate);
- Task<T> GetSingleAsync(Expression<Func<T, bool>> predicate);
- T GetSingle(Expression<Func<T, bool>> predicate, params Expression<Func<T, object>>[] includeProperties);
- Task<T> GetSingleAsync(Expression<Func<T, bool>> predicate, params Expression<Func<T, object>>[] includeProperties);
- IQueryable<T> FindBy(Expression<Func<T, bool>> predicate);
- void Add(T entity);
- void Update(T entity);
- void Delete(T entity);
- void DeleteWhere(Expression<Func<T, bool>> predicate);
- void AddRange(IEnumerable<T> entities);
- void DeleteRange(IEnumerable<T> entities);
- void Attach(T entity);
- void AttachRange(IEnumerable<T> entities);
- void Detach(T entity);
- void DetachRange(IEnumerable<T> entities);
- void AttachAsModified(T entity);
- }
- }
EntityBaseRepository<T>是它的实现:
- namespace CoreApi.DataContext.Infrastructure
- {
- public class EntityBaseRepository<T> : IEntityBaseRepository<T>
- where T : class, IEntityBase, new()
- {
- #region Properties
- protected CoreContext Context { get; }
- public EntityBaseRepository(IUnitOfWork unitOfWork)
- {
- Context = unitOfWork as CoreContext;
- }
- #endregion
- public virtual IQueryable<T> All => Context.Set<T>();
- public virtual IQueryable<T> AllIncluding(params Expression<Func<T, object>>[] includeProperties)
- {
- IQueryable<T> query = Context.Set<T>();
- foreach (var includeProperty in includeProperties)
- {
- query = query.Include(includeProperty);
- }
- return query;
- }
- public virtual int Count()
- {
- return Context.Set<T>().Count();
- }
- public async Task<int> CountAsync()
- {
- return await Context.Set<T>().CountAsync();
- }
- public T GetSingle(int id)
- {
- return Context.Set<T>().FirstOrDefault(x => x.Id == id);
- }
- public async Task<T> GetSingleAsync(int id)
- {
- return await Context.Set<T>().FirstOrDefaultAsync(x => x.Id == id);
- }
- public T GetSingle(Expression<Func<T, bool>> predicate)
- {
- return Context.Set<T>().FirstOrDefault(predicate);
- }
- public async Task<T> GetSingleAsync(Expression<Func<T, bool>> predicate)
- {
- return await Context.Set<T>().FirstOrDefaultAsync(predicate);
- }
- public T GetSingle(Expression<Func<T, bool>> predicate, params Expression<Func<T, object>>[] includeProperties)
- {
- IQueryable<T> query = Context.Set<T>();
- foreach (var includeProperty in includeProperties)
- {
- query = query.Include(includeProperty);
- }
- return query.Where(predicate).FirstOrDefault();
- }
- public async Task<T> GetSingleAsync(Expression<Func<T, bool>> predicate, params Expression<Func<T, object>>[] includeProperties)
- {
- IQueryable<T> query = Context.Set<T>();
- foreach (var includeProperty in includeProperties)
- {
- query = query.Include(includeProperty);
- }
- return await query.Where(predicate).FirstOrDefaultAsync();
- }
- public virtual IQueryable<T> FindBy(Expression<Func<T, bool>> predicate)
- {
- return Context.Set<T>().Where(predicate);
- }
- public virtual void Add(T entity)
- {
- Context.Set<T>().Add(entity);
- }
- public virtual void Update(T entity)
- {
- EntityEntry<T> dbEntityEntry = Context.Entry(entity);
- dbEntityEntry.State = EntityState.Modified;
- dbEntityEntry.Property(x => x.Id).IsModified = false;
- dbEntityEntry.Property(x => x.CreateUser).IsModified = false;
- dbEntityEntry.Property(x => x.CreateTime).IsModified = false;
- }
- public virtual void Delete(T entity)
- {
- Context.Set<T>().Remove(entity);
- }
- public virtual void AddRange(IEnumerable<T> entities)
- {
- Context.Set<T>().AddRange(entities);
- }
- public virtual void DeleteRange(IEnumerable<T> entities)
- {
- foreach (var entity in entities)
- {
- Context.Set<T>().Remove(entity);
- }
- }
- public virtual void DeleteWhere(Expression<Func<T, bool>> predicate)
- {
- IEnumerable<T> entities = Context.Set<T>().Where(predicate);
- foreach (var entity in entities)
- {
- Context.Entry<T>(entity).State = EntityState.Deleted;
- }
- }
- public void Attach(T entity)
- {
- Context.Set<T>().Attach(entity);
- }
- public void AttachRange(IEnumerable<T> entities)
- {
- foreach (var entity in entities)
- {
- Attach(entity);
- }
- }
- public void Detach(T entity)
- {
- Context.Entry<T>(entity).State = EntityState.Detached;
- }
- public void DetachRange(IEnumerable<T> entities)
- {
- foreach (var entity in entities)
- {
- Detach(entity);
- }
- }
- public void AttachAsModified(T entity)
- {
- Attach(entity);
- Update(entity);
- }
- }
- }
建立Client的ViewModels
在CoreApi.ViewModels建立Angular文件夹, 分别针对查询, 新增, 修改建立3个ViewModel(Dto):
ClientViewModel:
- namespace CoreApi.ViewModels.Angular
- {
- public class ClientViewModel : EntityBase
- {
- public decimal Balance { get; set; }
- public string Email { get; set; }
- public string FirstName { get; set; }
- public string LastName { get; set; }
- public string Phone { get; set; }
- }
- }
ClientCreationViewModel:
- namespace CoreApi.ViewModels.Angular
- {
- public class ClientCreationViewModel
- {
- public decimal Balance { get; set; }
- [Required]
- [MaxLength()]
- public string Email { get; set; }
- [Required]
- [MaxLength()]
- public string FirstName { get; set; }
- [Required]
- [MaxLength()]
- public string LastName { get; set; }
- [Required]
- [MaxLength()]
- public string Phone { get; set; }
- }
- }
ClientModificationViewModel:
- namespace CoreApi.ViewModels.Angular
- {
- public class ClientModificationViewModel
- {
- public decimal Balance { get; set; }
- [Required]
- [MaxLength()]
- public string Email { get; set; }
- [Required]
- [MaxLength()]
- public string FirstName { get; set; }
- [Required]
- [MaxLength()]
- public string LastName { get; set; }
- [Required]
- [MaxLength()]
- public string Phone { get; set; }
- }
- }
配置AutoMapper
针对Client和它的Viewmodels, 分别从两个方向进行配置:
DomainToViewModelMappingProfile:
- namespace CoreApi.Web.MyConfigurations
- {
- public class DomainToViewModelMappingProfile : Profile
- {
- public override string ProfileName => "DomainToViewModelMappings";
- public DomainToViewModelMappingProfile()
- {
- CreateMap<UploadedFile, UploadedFileViewModel>();
- CreateMap<Client, ClientViewModel>();
- CreateMap<Client, ClientModificationViewModel>();
- }
- }
- }
ViewModelToDomainMappingProfile:
- namespace CoreApi.Web.MyConfigurations
- {
- public class ViewModelToDomainMappingProfile : Profile
- {
- public override string ProfileName => "ViewModelToDomainMappings";
- public ViewModelToDomainMappingProfile()
- {
- CreateMap<UploadedFileViewModel, UploadedFile>();
- CreateMap<ClientViewModel, Client>();
- CreateMap<ClientCreationViewModel, Client>();
- CreateMap<ClientModificationViewModel, Client>();
- }
- }
- }
注册Repository的DI:
在web项目的StartUp.cs的ConfigureServices里面为ClientRepository注册DI:
- services.AddScoped<IClientRepository, ClientRepository>();
建立Controller
在controllers目录建立Angular/ClientController.cs:
- namespace CoreApi.Web.Controllers.Angular
- {
- [Route("api/[controller]")]
- public class ClientController : BaseController<ClientController>
- {
- private readonly IClientRepository _clientRepository;
- public ClientController(ICoreService<ClientController> coreService,
- IClientRepository clientRepository) : base(coreService)
- {
- _clientRepository = clientRepository;
- }
- [HttpGet]
- public async Task<IActionResult> GetAll()
- {
- var items = await _clientRepository.All.ToListAsync();
- var results = Mapper.Map<IEnumerable<ClientViewModel>>(items);
- return Ok(results);
- }
- [HttpGet]
- [Route("{id}", Name = "GetClient")]
- public async Task<IActionResult> Get(int id)
- {
- var item = await _clientRepository.GetSingleAsync(id);
- if (item == null)
- {
- return NotFound();
- }
- var result = Mapper.Map<ClientViewModel>(item);
- return Ok(result);
- }
- [HttpPost]
- public async Task<IActionResult> Post([FromBody] ClientCreationViewModel clientVm)
- {
- if (clientVm == null)
- {
- return BadRequest();
- }
- if (!ModelState.IsValid)
- {
- return BadRequest(ModelState);
- }
- var newItem = Mapper.Map<Client>(clientVm);
- newItem.SetCreation(UserName);
- _clientRepository.Add(newItem);
- if (!await UnitOfWork.SaveAsync())
- {
- return StatusCode(, "保存客户时出错");
- }
- var vm = Mapper.Map<ClientViewModel>(newItem);
- return CreatedAtRoute("GetClient", new { id = vm.Id }, vm);
- }
- [HttpPut("{id}")]
- public async Task<IActionResult> Put(int id, [FromBody] ClientModificationViewModel clientVm)
- {
- if (clientVm == null)
- {
- return BadRequest();
- }
- if (!ModelState.IsValid)
- {
- return BadRequest(ModelState);
- }
- var dbItem = await _clientRepository.GetSingleAsync(id);
- if (dbItem == null)
- {
- return NotFound();
- }
- Mapper.Map(clientVm, dbItem);
- dbItem.SetModification(UserName);
- _clientRepository.Update(dbItem);
- if (!await UnitOfWork.SaveAsync())
- {
- return StatusCode(, "保存客户时出错");
- }
- return NoContent();
- }
- [HttpPatch("{id}")]
- public async Task<IActionResult> Patch(int id, [FromBody] JsonPatchDocument<ClientModificationViewModel> patchDoc)
- {
- if (patchDoc == null)
- {
- return BadRequest();
- }
- var dbItem = await _clientRepository.GetSingleAsync(id);
- if (dbItem == null)
- {
- return NotFound();
- }
- var toPatchVm = Mapper.Map<ClientModificationViewModel>(dbItem);
- patchDoc.ApplyTo(toPatchVm, ModelState);
- TryValidateModel(toPatchVm);
- if (!ModelState.IsValid)
- {
- return BadRequest(ModelState);
- }
- Mapper.Map(toPatchVm, dbItem);
- if (!await UnitOfWork.SaveAsync())
- {
- return StatusCode(, "更新的时候出错");
- }
- return NoContent();
- }
- [HttpDelete("{id}")]
- public async Task<IActionResult> Delete(int id)
- {
- var model = await _clientRepository.GetSingleAsync(id);
- if (model == null)
- {
- return NotFound();
- }
- _clientRepository.Delete(model);
- if (!await UnitOfWork.SaveAsync())
- {
- return StatusCode(, "删除的时候出错");
- }
- return NoContent();
- }
- }
- }
首先, Controller继承了ControllerBase这个类, ControllerBase是自己写的类, 里面可以放置一些公用的方法或属性, 目前里面的东西都没用:
- namespace CoreApi.Web.Controllers.Bases
- {
- public abstract class BaseController<T> : Controller
- {
- protected readonly IUnitOfWork UnitOfWork;
- protected readonly ILogger<T> Logger;
- protected readonly IFileProvider FileProvider;
- protected readonly ICoreService<T> CoreService;
- protected BaseController(ICoreService<T> coreService)
- {
- CoreService = coreService;
- UnitOfWork = coreService.UnitOfWork;
- Logger = coreService.Logger;
- FileProvider = coreService.FileProvider;
- }
- #region Current Information
- protected DateTime Now => DateTime.Now;
- protected string UserName => User.Identity.Name ?? "Anonymous";
- #endregion
- }
- }
由于父类构造函数依赖的类太多了, 所以我建立了一个CoreService, 里面包含着这些依赖, 然后用一个变量就注入进去了, 这种写法不一定正确:
- public interface ICoreService<out T> : IDisposable
- {
- IUnitOfWork UnitOfWork { get; }
- ILogger<T> Logger { get; }
- IFileProvider FileProvider { get; }
- }
Controller里面的方法应该都能看明白吧. 需要提一下的是UnitOfWork.
Unit Of Work
我才用的是UnitOfWork和Repository模式, 多个Repository挂起的数据库操作, 可以使用一个UnitOfWork一次性提交.
由于DBContext已经实现了UnitOfWork模式, 所以可以直接在Controller里面使用DbContext, 但是我还是做了一个接口 IUnitOfWork:
- namespace CoreApi.DataContext.Infrastructure
- {
- public interface IUnitOfWork: IDisposable
- {
- int SaveChanges();
- int SaveChanges(bool acceptAllChangesOnSuccess);
- Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default(CancellationToken));
- Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken));
- bool Save();
- bool Save(bool acceptAllChangesOnSuccess);
- Task<bool> SaveAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default(CancellationToken));
- Task<bool> SaveAsync(CancellationToken cancellationToken = default(CancellationToken));
- }
- }
里面前4个方法就是DbContext内置的方法, 后面4个方法可有可无, 就是上面4个方法的简单变形.
看一下CoreContext:
- namespace CoreApi.DataContext.Core
- {
- public class CoreContext : DbContext, IUnitOfWork
- {
- public CoreContext(DbContextOptions<CoreContext> options)
- : base(options)
- {
- }
- protected override void OnModelCreating(ModelBuilder modelBuilder)
- {
- base.OnModelCreating(modelBuilder);
- modelBuilder.HasDefaultSchema(AppSettings.DefaultSchema);
- modelBuilder.ApplyConfiguration(new UploadedFileConfiguration());
- modelBuilder.ApplyConfiguration(new ClientConfiguration());
- }
- public DbSet<UploadedFile> UploadedFiles { get; set; }
- public DbSet<Client> Clients { get; set; }
- public bool Save()
- {
- return SaveChanges() >= ;
- }
- public bool Save(bool acceptAllChangesOnSuccess)
- {
- return SaveChanges(acceptAllChangesOnSuccess) >= ;
- }
- public async Task<bool> SaveAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default(CancellationToken))
- {
- return await SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken) >= ;
- }
- public async Task<bool> SaveAsync(CancellationToken cancellationToken = default(CancellationToken))
- {
- return await SaveChangesAsync(cancellationToken) >= ;
- }
- }
- }
差不多了, 开始
迁移数据库
在Package Manager Console分别执行 Add-Migration XXX和 Update-database命令.
注意这个时候 解决方案的启动项目必须是Web项目, 如果设置了多个启动项目, 迁移命令会不太好用.
然后运行一下: 选择CoreApi.Web而不是IISExpress, 这样的话端口应该是 http://localhost:5001/api/values
到Swagger里简单测试下
然后进入swagger简单测试一下ClientController: http://localhost:5001/swagger/
先添加数据 POST:
先点击右侧, 然后会把数据的json模板复制到左边的框里, 然后修改值, 然后点击try It out, 结果如下:
然后两个Get, Delete, Put您都应该会测试.
这里试一下 Patch:
再查询一下, 应该没有什么问题.
先写到这, 明天就能差不多写完了吧.
使用angular4和asp.net core 2 web api做个练习项目(一)的更多相关文章
- 使用angular4和asp.net core 2 web api做个练习项目(四)
第一部分: http://www.cnblogs.com/cgzl/p/7755801.html 第二部分: http://www.cnblogs.com/cgzl/p/7763397.html 第三 ...
- 使用angular4和asp.net core 2 web api做个练习项目(二), 这部分都是angular
上一篇: http://www.cnblogs.com/cgzl/p/7755801.html 完成client.service.ts: import { Injectable } from '@an ...
- 使用angular4和asp.net core 2 web api做个练习项目(三)
第一部分: http://www.cnblogs.com/cgzl/p/7755801.html 第二部分: http://www.cnblogs.com/cgzl/p/7763397.html 后台 ...
- angular4和asp.net core 2 web api
angular4和asp.net core 2 web api 这是一篇学习笔记. angular 5 正式版都快出了, 不过主要是性能升级. 我认为angular 4还是很适合企业的, 就像.net ...
- 温故知新,使用ASP.NET Core创建Web API,永远第一次
ASP.NET Core简介 ASP.NET Core是一个跨平台的高性能开源框架,用于生成启用云且连接Internet的新式应用. 使用ASP.NET Core,您可以: 生成Web应用和服务.物联 ...
- 基于ASP.NET Core 创建 Web API
使用 Visual Studio 创建项目. 文件->新建->项目,选择创建 ASP.NET Core Web 应用程序. 基于 ASP.NET Core 2.0 ,选择API,身份验证选 ...
- ASP.NET Core Restful Web API 相关资源索引
GraphQL 使用ASP.NET Core开发GraphQL服务器 -- 预备知识(上) 使用ASP.NET Core开发GraphQL服务器 -- 预备知识(下) [视频] 使用ASP.NET C ...
- 使用 ASP.NET Core 创建 Web API及链接sqlserver数据库
创建 Web API https://docs.microsoft.com/zh-cn/aspnet/core/tutorials/first-web-api?view=aspnetcore-3.0& ...
- ASP.NET Core 中基于 API Key 对私有 Web API 进行保护
这两天遇到一个应用场景,需要对内网调用的部分 web api 进行安全保护,只允许请求头账户包含指定 key 的客户端进行调用.在网上找到一篇英文博文 ASP.NET Core - Protect y ...
随机推荐
- JavaEE成长之路
前言 学习Java已经有一段时间了,在学习的过程中也走过了不少弯路. 写下这篇博文,主要是想记录下自己学习编程之路,以及反思自己在学习的时候出现的问题,下面也会给出我自认为学习JavaEE的路线,想要 ...
- Ningx集群环境搭建
Ningx集群环境搭建 Nginx是什么? Nginx ("engine x") 是⼀个⾼性能的 HTTP 和 反向代理 服务器,也是⼀个 IMAP/ POP3/SMTP 代理服务 ...
- S3C2440 时钟设置分析(FCLK, HCLK, PCLK)
时钟对于一个系统的重要性不言而喻,时钟决定了系统发送数据的快慢,高性能的芯片往往能支持更快速度的时钟,从而提供更好的体验. S3C2440的输入时钟频率是12MHZ,对于这款芯片,显然速度是不够的,所 ...
- AngularJS -- Module (模块)
点击查看AngularJS系列目录 转载请注明出处:http://www.cnblogs.com/leosx/ 什么是AngularJS的模块 我们所说的模块,是你的AngularJS应用程序的一个组 ...
- JSP内置对象的实验报告,页面登陆设计
JSP内置对象的实验报告 一.实验目的: 本实验的目的是让学生掌握怎样在JSP中使用内置对象request.page.response等. 二.实验要求: 编写四个JSP 页面login.jsp.Re ...
- java从命令行接收多个数字,求和程序分析
问题:编写一个程序,此程序从命令行接收多个数字,求和之后输出结果. 1.设计思想 (1)声明两个变量接收输入的字符串 (2)将字符串转换成int类型 (3)输出求和 2.程序流程图 3.源程序代码 i ...
- Max Consecutive Ones
Given a binary array, find the maximum number of consecutive 1s in this array. Example 1: Input: [1, ...
- Day2 python基础学习
http://www.pythondoc.com/ Python中文学习大本营 本节内容: 一.字符串操作 二.列表操作 三.元组操作 四.字典操作 五.集合操作 六.字符编码操作 一.字符串操作 1 ...
- Python文件读写模式
r 打开只读文件,该文件必须存在. r+ 打开可读写的文件,该文件必须存在.可读,可写,可追加. w 打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失.若文件不存在则建立该文件. w+ 打 ...
- JS表单提交的几种方式
第一种方式 : 表单提交,在 form 标签中增加 onsubmit 事件来判断表单是否提交成功 <script type="text/javascript"> fun ...