博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
使用angular4和asp.net core 2 web api做个练习项目(一)
阅读量:7062 次
发布时间:2019-06-28

本文共 21233 字,大约阅读时间需要 70 分钟。

这是一篇学习笔记. angular 5 正式版都快出了, 不过主要是性能升级.

我认为angular 4还是很适合企业的, 就像.net一样.

我用的是windows 10

安装工具:

git for windows: 官网很慢, 所以找一个镜像站下载: , 淘宝镜像的速度还是蛮快的:

安装的时候, 建议选择这个, 会添加很多命令行工具: 

nodejs: 去官网下载就行: 

正常安装即可. npm的版本不要低于5.0吧:

angular-cli, 官网: 

npm install -g @angular/cli

visual studio code

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, 如果没问题的话, 大概是这样: 

浏览器运行:

 

安装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/clientsng g component components/clientDetailsng g component components/addClientng g component components/editClientng g component components/navbarng 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健):

现在刷新浏览器, 大约这样:

添加navbar:

修改navbar.component.html:

 

修改app.component.html:

运行:

建立Service

建立一个client.service:

ng g service services/client

然后在app.module.ts添加引用:

// Services Importsimport { ClientService } from "./services/client.service";

并添加在providers里:

providers: [    ClientService  ],

前端先暂时到这, 现在开始搞后端 web api.

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

web api项目源码: 

项目列表如图:

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
{ public override void ConfigureDerived(EntityTypeBuilder
builder) { builder.Property(x => x.Balance).HasColumnType("decimal(18,2)"); builder.Property(x => x.Email).IsRequired().HasMaxLength(100); builder.Property(x => x.FirstName).IsRequired().HasMaxLength(50); builder.Property(x => x.LastName).IsRequired().HasMaxLength(50); builder.Property(x => x.Phone).HasMaxLength(50); } }}

其中父类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
: IEntityTypeConfiguration
where T : EntityBase { public virtual void Configure(EntityTypeBuilder
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(50); builder.Property(x => x.UpdateUser).IsRequired().HasMaxLength(50); builder.Property(x => x.LastAction).IsRequired().HasMaxLength(50); ConfigureDerived(builder); } public abstract void ConfigureDerived(EntityTypeBuilder
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
UploadedFiles { get; set; } public DbSet
Clients { get; set; }

然后建立ClientRepository

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

namespace CoreApi.Repositories.Angular{    public interface IClientRepository : IEntityBaseRepository
{ } public class ClientRepository : EntityBaseRepository
, IClientRepository { public ClientRepository(IUnitOfWork unitOfWork) : base(unitOfWork) { } }}

图省事, 我把repository和它的interface放在一个文件了.

IEntityBaseRepository<T>定义了一些常用的方法:

namespace CoreApi.DataContext.Infrastructure{    public interface IEntityBaseRepository
where T : class, IEntityBase, new() { IQueryable
All { get; } IQueryable
AllIncluding(params Expression
>[] includeProperties); int Count(); Task
CountAsync(); T GetSingle(int id); Task
GetSingleAsync(int id); T GetSingle(Expression
> predicate); Task
GetSingleAsync(Expression
> predicate); T GetSingle(Expression
> predicate, params Expression
>[] includeProperties); Task
GetSingleAsync(Expression
> predicate, params Expression
>[] includeProperties); IQueryable
FindBy(Expression
> predicate); void Add(T entity); void Update(T entity); void Delete(T entity); void DeleteWhere(Expression
> predicate); void AddRange(IEnumerable
entities); void DeleteRange(IEnumerable
entities); void Attach(T entity); void AttachRange(IEnumerable
entities); void Detach(T entity); void DetachRange(IEnumerable
entities); void AttachAsModified(T entity); }}

EntityBaseRepository<T>是它的实现:

namespace CoreApi.DataContext.Infrastructure{    public class EntityBaseRepository
: IEntityBaseRepository
where T : class, IEntityBase, new() { #region Properties protected CoreContext Context { get; } public EntityBaseRepository(IUnitOfWork unitOfWork) { Context = unitOfWork as CoreContext; } #endregion public virtual IQueryable
All => Context.Set
(); public virtual IQueryable
AllIncluding(params Expression
>[] includeProperties) { IQueryable
query = Context.Set
(); foreach (var includeProperty in includeProperties) { query = query.Include(includeProperty); } return query; } public virtual int Count() { return Context.Set
().Count(); } public async Task
CountAsync() { return await Context.Set
().CountAsync(); } public T GetSingle(int id) { return Context.Set
().FirstOrDefault(x => x.Id == id); } public async Task
GetSingleAsync(int id) { return await Context.Set
().FirstOrDefaultAsync(x => x.Id == id); } public T GetSingle(Expression
> predicate) { return Context.Set
().FirstOrDefault(predicate); } public async Task
GetSingleAsync(Expression
> predicate) { return await Context.Set
().FirstOrDefaultAsync(predicate); } public T GetSingle(Expression
> predicate, params Expression
>[] includeProperties) { IQueryable
query = Context.Set
(); foreach (var includeProperty in includeProperties) { query = query.Include(includeProperty); } return query.Where(predicate).FirstOrDefault(); } public async Task
GetSingleAsync(Expression
> predicate, params Expression
>[] includeProperties) { IQueryable
query = Context.Set
(); foreach (var includeProperty in includeProperties) { query = query.Include(includeProperty); } return await query.Where(predicate).FirstOrDefaultAsync(); } public virtual IQueryable
FindBy(Expression
> predicate) { return Context.Set
().Where(predicate); } public virtual void Add(T entity) { Context.Set
().Add(entity); } public virtual void Update(T entity) { EntityEntry
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
().Remove(entity); } public virtual void AddRange(IEnumerable
entities) { Context.Set
().AddRange(entities); } public virtual void DeleteRange(IEnumerable
entities) { foreach (var entity in entities) { Context.Set
().Remove(entity); } } public virtual void DeleteWhere(Expression
> predicate) { IEnumerable
entities = Context.Set
().Where(predicate); foreach (var entity in entities) { Context.Entry
(entity).State = EntityState.Deleted; } } public void Attach(T entity) { Context.Set
().Attach(entity); } public void AttachRange(IEnumerable
entities) { foreach (var entity in entities) { Attach(entity); } } public void Detach(T entity) { Context.Entry
(entity).State = EntityState.Detached; } public void DetachRange(IEnumerable
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(100)]        public string Email { get; set; }                [Required]        [MaxLength(50)]        public string FirstName { get; set; }                [Required]        [MaxLength(50)]        public string LastName { get; set; }        [Required]        [MaxLength(50)]        public string Phone { get; set; }    }}

ClientModificationViewModel:

namespace CoreApi.ViewModels.Angular{    public class ClientModificationViewModel    {        public decimal Balance { get; set; }        [Required]        [MaxLength(100)]        public string Email { get; set; }        [Required]        [MaxLength(50)]        public string FirstName { get; set; }        [Required]        [MaxLength(50)]        public string LastName { get; set; }        [Required]        [MaxLength(50)]        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
(); CreateMap
(); CreateMap
(); } }}

ViewModelToDomainMappingProfile:

namespace CoreApi.Web.MyConfigurations{    public class ViewModelToDomainMappingProfile : Profile    {        public override string ProfileName => "ViewModelToDomainMappings";        public ViewModelToDomainMappingProfile()        {            CreateMap
(); CreateMap
(); CreateMap
(); CreateMap
(); } }}

注册Repository的DI:

在web项目的StartUp.cs的ConfigureServices里面为ClientRepository注册DI:

services.AddScoped
();

建立Controller

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

namespace CoreApi.Web.Controllers.Angular{    [Route("api/[controller]")]    public class ClientController : BaseController
{ private readonly IClientRepository _clientRepository; public ClientController(ICoreService
coreService, IClientRepository clientRepository) : base(coreService) { _clientRepository = clientRepository; } [HttpGet] public async Task
GetAll() { var items = await _clientRepository.All.ToListAsync(); var results = Mapper.Map
>(items); return Ok(results); } [HttpGet] [Route("{id}", Name = "GetClient")] public async Task
Get(int id) { var item = await _clientRepository.GetSingleAsync(id); if (item == null) { return NotFound(); } var result = Mapper.Map
(item); return Ok(result); } [HttpPost] public async Task
Post([FromBody] ClientCreationViewModel clientVm) { if (clientVm == null) { return BadRequest(); } if (!ModelState.IsValid) { return BadRequest(ModelState); } var newItem = Mapper.Map
(clientVm); newItem.SetCreation(UserName); _clientRepository.Add(newItem); if (!await UnitOfWork.SaveAsync()) { return StatusCode(500, "保存客户时出错"); } var vm = Mapper.Map
(newItem); return CreatedAtRoute("GetClient", new { id = vm.Id }, vm); } [HttpPut("{id}")] public async Task
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(500, "保存客户时出错"); } return NoContent(); } [HttpPatch("{id}")] public async Task
Patch(int id, [FromBody] JsonPatchDocument
patchDoc) { if (patchDoc == null) { return BadRequest(); } var dbItem = await _clientRepository.GetSingleAsync(id); if (dbItem == null) { return NotFound(); } var toPatchVm = Mapper.Map
(dbItem); patchDoc.ApplyTo(toPatchVm, ModelState); TryValidateModel(toPatchVm); if (!ModelState.IsValid) { return BadRequest(ModelState); } Mapper.Map(toPatchVm, dbItem); if (!await UnitOfWork.SaveAsync()) { return StatusCode(500, "更新的时候出错"); } return NoContent(); } [HttpDelete("{id}")] public async Task
Delete(int id) { var model = await _clientRepository.GetSingleAsync(id); if (model == null) { return NotFound(); } _clientRepository.Delete(model); if (!await UnitOfWork.SaveAsync()) { return StatusCode(500, "删除的时候出错"); } return NoContent(); } }}

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

namespace CoreApi.Web.Controllers.Bases{    public abstract class BaseController
: Controller { protected readonly IUnitOfWork UnitOfWork; protected readonly ILogger
Logger; protected readonly IFileProvider FileProvider; protected readonly ICoreService
CoreService; protected BaseController(ICoreService
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
: IDisposable { IUnitOfWork UnitOfWork { get; } ILogger
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
SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default(CancellationToken)); Task
SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken)); bool Save(); bool Save(bool acceptAllChangesOnSuccess); Task
SaveAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default(CancellationToken)); Task
SaveAsync(CancellationToken cancellationToken = default(CancellationToken)); }}

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

看一下CoreContext:

namespace CoreApi.DataContext.Core{    public class CoreContext : DbContext, IUnitOfWork    {        public CoreContext(DbContextOptions
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
UploadedFiles { get; set; } public DbSet
Clients { get; set; } public bool Save() { return SaveChanges() >= 0; } public bool Save(bool acceptAllChangesOnSuccess) { return SaveChanges(acceptAllChangesOnSuccess) >= 0; } public async Task
SaveAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default(CancellationToken)) { return await SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken) >= 0; } public async Task
SaveAsync(CancellationToken cancellationToken = default(CancellationToken)) { return await SaveChangesAsync(cancellationToken) >= 0; } }}

差不多了, 开始

迁移数据库

在Package Manager Console分别执行 Add-Migration XXX和 Update-database命令.

注意这个时候 解决方案的启动项目必须是Web项目, 如果设置了多个启动项目, 迁移命令会不太好用.

然后运行一下: 选择CoreApi.Web而不是IISExpress, 这样的话端口应该是 

到Swagger里简单测试下

然后进入swagger简单测试一下ClientController: 

先添加数据 POST:

先点击右侧, 然后会把数据的json模板复制到左边的框里, 然后修改值, 然后点击try It out, 结果如下:

然后两个Get, Delete, Put您都应该会测试.

这里试一下 Patch:

再查询一下, 应该没有什么问题.

先写到这, 明天就能差不多写完了吧.

转载地址:http://vpnll.baihongyu.com/

你可能感兴趣的文章
每日英语:Chinese Writer Wins Literature Nobel
查看>>
java中三种主流数据库数据库(sqlserver,db2,oracle)的jdbc连接总结
查看>>
Oracle Apps AutoConfig
查看>>
[leetcode]Flatten Binary Tree to Linked List
查看>>
css颜色代码大全:(网页设计师和平面设计师常用)
查看>>
boost 1.52在windows下的配置
查看>>
素材锦囊——50个高质量的 PSD 素材免费下载《上篇》
查看>>
【转】oc中消息传递机制-附:对performSelector方法的扩充
查看>>
oracle的nvl和sql server的isnull
查看>>
[转]虚拟机下Ubuntu共享主机文件(Ubuntu、VMware、共享)
查看>>
高血压 治疗 偏方
查看>>
HtmlAttribute HTML属性处理类
查看>>
[书目20130316]jQuery UI开发指南
查看>>
Sql Server系列:开发存储过程
查看>>
Find INTCOL#=1001 in col_usage$?
查看>>
AutoCAD 命令统计魔幻球的实现过程--(3)
查看>>
dp学习笔记1
查看>>
newlisp debugger
查看>>
Java进阶02 异常处理
查看>>
图文介绍openLDAP在windows上的安装配置
查看>>