先來看看商品業務服務庫
有控制器、有實體、有自己的服務、有配置
依賴:
這也是所有業務服務庫的大體依賴
必選:
中介者服務(Mediator)
數據庫訪問(ORM.Chloe)
路由(Route)
可選:
對象映射服務(AutoMapper)
掃描服務(ReferenceScan)
庫存業務服務庫(Stock)
控制器:
[ApiController] [APIRouth("Product")] public class ProductController : Controller { IProduct productServer; public ProductController(IProduct productServer) { this.productServer = productServer; } /// <summary> /// 獲取全部商品信息 /// </summary> /// <param name="maxNumber">庫存最大數量</param> /// <returns></returns> [HttpGet] public IActionResult GetAll(int? maxNumber) { var result =productServer.GetAll(maxNumber); return Json(result); } /// <summary> /// 獲得單個商品信息 /// </summary> /// <param name="ID">商品ID</param> /// <returns></returns> [HttpGet] public IActionResult Get(int ID=0) { var result = productServer.GetSingle(ID); return Json(result); } }
其中
APIRouth特性頭是編寫的一個特性
/// <summary> /// API路由 /// </summary> public class APIRouth : Microsoft.AspNetCore.Mvc.RouteAttribute { /// <summary> /// 設計一個API的路由 /// </summary> /// <param name="area">區域名稱</param> public APIRouth(string area) : base($"api/{area}/[controller]/[action]") { } }
這是爲了達到統一API路徑的目的
控制器很簡單
只執行調用IProduct 服務的功能,在後續開發中,他也是比較貧血的一層
可以預料的是,它的作用僅僅只是爲了提供一個訪問入口且寫明註釋,並調用了服務
至於表單驗證,響應模型之類的我們將會在中間件中完成,可以會有困難,但還是儘量中間件中完成,如果不行那麼將會仍在過濾器或者父類控制器完成,儘量保證不會在業務中出現這種重複性代碼
實體:
[Table(Name = "Product")] public class ProductEntity { [ColumnAttribute(IsPrimaryKey = true)] [AutoIncrement] public int ID { get; set; } public string Title { get; set; } public string Describe { get; set; } }
業務處理服務:
public class ProductServer : IProduct { IDbContext DC; IStock stock; IMapper mapper; public ProductServer(IDbContext DC, IStock stock,IMapper mapper) { this.DC = DC; this.stock = stock; this.mapper = mapper; } public List<ProductInfoDTO> GetAll(int? maxNumber) { var result = DC.Query<ProductEntity>().RightJoin(stock.StockNumberQuery(null, maxNumber), (c, z) => c.ID.Equals(z.ProductID)) .Select((x, z) => new ProductInfoDTO() { Describe = x.Describe, ID = x.ID, StockNumber = z.Number, Title = x.Title }); return result.ToList(); } public ProductInfoDTO GetSingle(int ID) { return DC.Query<ProductEntity>().InnerJoin(stock.GetSingle(ID), (c, z) => c.ID.Equals(z.ProductID)).Select((x, z) => new ProductInfoDTO() { Describe = x.Describe, ID = x.ID, StockNumber = z.Number, Title = x.Title }).FirstOrDefault(x => x.ID.Equals(ID)); ; } }
DC 數據庫上下文
stock 庫存服務
mapper (AutoMapper) 對象映射服務
這裏有一個跨服務的聯合查詢
在數據庫中 商品和庫存是兩個關聯的表
代碼中通過
RightJoin
InnerJoin
來進行鏈表查詢
public interface IStock { /// <summary> /// 庫存查詢 /// </summary> /// <param name="minNumber">最小庫存</param> /// <param name="maxNumber">最大庫存</param> /// <returns></returns> IQuery<StockDTO> StockNumberQuery(int? minNumber, int? maxNumber); /// <summary> /// 庫存查詢 /// </summary> /// <param name="product">商品ID</param> /// <returns></returns> IQuery<StockDTO> GetSingle(int productID); /// <summary> /// 消減庫存 /// </summary> /// <param name="productID">商品ID</param> /// <param name="number">數量</param> /// <returns></returns> bool ReducedInventory(int productID,int number); }
跨庫查詢也是一個非常大的問題,這裏我們還只是一個同數據庫不同服務的跨服務查詢,後續的跨服務跨庫查詢纔是真正難以解決的問題
庫存服務返回了一個IQuery查詢對象,方便Chloe進行鏈表操作
最後在Product服務中去執行關聯和返回查詢實體,
這裏沒有用到mapper,因爲Chloe的Select已經足夠使用,沒必要再去添一行代碼去使用mapper
啓動執行:
public class StartExecute : BaseStartExecute { public override void Execute() { MapperPost.CreateMap(x => x.CreateMap<ProductInfoDTO, ProductEntity>()); MapperPost.CreateMap(x => x.CreateMap<ProductEntity, ProductInfoDTO>().ForMember(dest => dest.StockNumber, opt => opt.Ignore())); } }
Mapper需要在啓動的時候也就是ConfigureServices函數中就進行調用,這會有個問題,大家爭搶ConfigureServices函數執行順序,變得非常難以管理,所以
StartExecute出現了,它會在所有服務調用ConfigureServices前調用,並逐個執行Execute函數,這樣一來,就不用關注ConfigureServices函數的執行順序啦
具體代碼後續在說
啓動類:
public static class Startup { public static void ConfigureServices(IServiceCollection services) { services.AddTransient<IProduct, ProductServer>(); } public static void Configure(IApplicationBuilder app, IHostingEnvironment env) { } }
在這裏註冊了IProduct服務,
IProduct服務是在中介者服務中的,中介者服務會在後續說到