1、項目初始化
創建一個ASP.NET Core Web API的項目,取消Https和身份驗證。
API項目實際上是一個控制臺程序,這點可以在項目的屬性的輸出類型中看到。
launchSettings.json,在這里可以配置運行項目的名稱,端口號,路徑等。
在Program.cs文件中對項目的構造器和中間件進行了配置,首先是創建構造器,添加控制器,終結點,Swagger,構建構造器。在中間件模塊里首先是判斷是否是開發模式,如果是的話便開始注冊Swagger和它的UI界面,授權,路由映射。
最后是運行項目,在Swagger中調用接口可以看到返回值,如果是簡單的項目比如一個只有OCR功能的項目是不需要另外再創建其他項目的,除非是一個比較復雜的才需要創建多個層級的項目。
2、倉儲+服務架構模式講解
這一節開始搭建項目,搭建項目很重要,會影響日常開發和維護的流程,這里采用的是這種架構:
1、接口層(控制器)暴露在最外面,由它去調用服務層;
2、服務層是個中轉站,它會去調用倉儲層拿到數據庫實體,并映射為數據模型對象Vo,這里的Vo應該與倉儲層的數據庫實體有差異,避免暴露一些關鍵信息,比如隱藏ID,修改字段名;另外一個好處是假如數據庫的數據做了遷移,需要修改字段名,只需要修改數據實體即可,無需修改Vo,前端是不用動的,減少了開發量。
3、倉儲層與數據庫做交互,拿到數據庫實體,它也會去調用公共層的代碼,現在在底層引用了一個類庫后,引用它的項目的引用的項目也能使用這個類庫,就很方便不用重復引用了,而且避免出現不同的版本。
還有就是在服務層和倉儲層都定義了一套實現類與接口類,設計到延遲的功能都使用了異步和等待。
Mock了一個數據 = 造假數據,具體來說就是將定義好的數據寫到程序中,作為固定返回,C#中有專門做假數據的類庫。
3、泛型基類的妙用
這一節在倉儲層和服務層定義了一對抽象化的類與接口,IBaseRepository和BaseRepository,通過往里面傳入泛型的方式簡化了類和接口的創建,但這樣寫是無法處理復雜的業務邏輯的。
public interface IBaseRepository<TEntity> where TEntity : class{Task<List<TEntity>> Query();}
public class BaseRepository<TEntity> : IBaseRepository<TEntity> where TEntity : class, new(){public async Task<List<TEntity>> Query(){await Task.CompletedTask;var str = "[{\"Id\":1, \"Name\":\"李四\"}]";var list = Newtonsoft.Json.JsonConvert.DeserializeObject<List<TEntity>>(str) ?? new List<TEntity>();return list;}}
4、泛型對象的關系映射
由于上一節最終返回的是一個數據模型而不是視圖模型,所以需要用到AutoMapper讓兩種類型實現一次配置后自動轉換。
具體步驟:
1、下載Nuget包,AutoMapper 12.0.1,AutoMapper.Extensions.Microsoft.DependencyInjection 12.0.0
2、創建配置文件CustomProfile,可以看到里面是對兩個類里面屬性的一個映射,CreateMap的第一個是起始類Role,第二個是目標類RoleVo,值得注意的是ForMember的第一個參數卻是目標類RoleVo的屬性RoleName,第二個參數是起始類Role的屬性
public class CustomProfile : Profile{public CustomProfile(){CreateMap<Role, RoleVo>().ForMember(a => a.RoleName, o => o.MapFrom(d => d.Name));CreateMap<RoleVo, Role>().ForMember(a => a.Name, o => o.MapFrom(d => d.RoleName));}}
另外還需要創建一個AutoMapper的配置文件AutoMapperConfig,這個在后面的注入環節會用到。
3、在Program的Main函數中對AutoMapper進行構建
builder.Services.AddAutoMapper(typeof(AutoMapperConfig));AutoMapperConfig.RegisterMappings();
然后在服務層使用它,這里需要進行依賴注入,然后使用AutoMapper的Map方法,泛型是目標類,入參是起始類。
public class BaseService<TEntity, TVo> : IBaseService<TEntity, TVo> where TEntity : class, new(){public readonly IMapper _mapper;public BaseService(IMapper mapper){_mapper = mapper;}public async Task<List<TVo>> Query(){var baseRepository = new BaseRepository<TEntity>();var entities = await baseRepository.Query();return _mapper.Map<List<TVo>>(entities);}}
5、依賴注入
雖然使用依賴注入需要進行額外的配置,但好處多多,比如不用擔心內存泄漏,何時被GC回收。
在Main函數中對創建的類進行注冊有三種模式:Singleton、Scoped、Transient
Singleton可以理解為單例模式,只有一個實例,是全局的。
Scoped可以理解為在一次請求中用到的是同一個實例,例如:在一個Action中,如果兩次用到同一個實例,在這種模式下將會是相同的。
Transient可以理解為瞬時的,例如::在一個Action中,如果兩次用到同一個實例,在這種模式下將會是不同的。
最常用的還是Scoped模式,在ASP.NET中在Action中使用new關鍵字創建的對象的時候也是這種方式。
ASP.NET Core和ASP.NET一個很明顯的區別就在于應盡量避免去new一個對象,而是通過依賴注入來獲得它。
具體步驟:
1、在Main函數注入服務類
builder.Services.AddScoped(typeof(IBaseService<,>), typeof(BaseService<,>));
2、在Controller中通過構造函數拿到它,然后在Action中使用