EF架構~引(yin)入(ru)規(gui)約(Specification)模式,讓程序擴展(zhan)性更強
規(gui)約(Specification)模式(shi)(shi):第一(yi)次看到這(zhe)東西是(shi)在microsoft NLayer項目中,它是(shi)微軟對(dui)DDD的(de)(de)(de)解(jie)說(shuo),就像petshop告訴了(le)我們(men)MVC如何(he)使用(yong)一(yi)樣,這(zhe)個(ge)(ge)規(gui)約模式(shi)(shi)最重要的(de)(de)(de)作用(yong)是(shi)實現了(le)查詢(xun)語句與(yu)查詢(xun)條件的(de)(de)(de)分(fen)離,查詢(xun)語句在底層是(shi)穩(wen)定(ding)的(de)(de)(de),不(bu)變的(de)(de)(de),而(er)查詢(xun)條件是(shi)和具體(ti)(ti)業務,具體(ti)(ti)領域有關(guan)的(de)(de)(de),是(shi)易變的(de)(de)(de),如果我們(men)為每(mei)一(yi)個(ge)(ge)領域的(de)(de)(de)每(mei)一(yi)個(ge)(ge)新(xin)需求都寫一(yi)個(ge)(ge)新(xin)的(de)(de)(de)方(fang)法,那(nei)就會出現很(hen)多重復的(de)(de)(de)代碼,不(bu)利于程序的(de)(de)(de)最終擴展!
下面我(wo)們來看一個經典例子
一個IOrderRepository的(de)接口,定義了一個訂單倉儲(chu)
Order_Info GetOrder_InfoById(int orderID); List<Order_Info> GetOrder_Info(DateTime from, DateTime to); List<Order_Info> GetOrder_InfoByUser(int userID);
代碼(ma)本身(shen)沒有任何問題,你(ni)只要(yao)去實現它就可以了,當(dang)一個新(xin)的需求到了之后,你(ni)的接(jie)口要(yao)被(bei)擴展(這是不被(bei)提倡的,一般我(wo)們會新(xin)建一個接(jie)口),然后修(xiu)改(gai)
原來的實(shi)現類,去實(shi)現接口新的方(fang)法(違背了(le)OCP原則(ze)),這種(zhong)做法是大多部開發團隊所經歷了(le),我,一個普通的人,也經歷了(le),但當(dang)我知(zhi)道(dao)DDD后(hou),當(dang)我看完
microsoft Nlayer項目之后,我(wo)知道,我(wo)一(yi)定(ding)要改變這種局(ju)面,于是,代碼在規約模式的(de)指(zhi)導下,進行重構了,呵呵。
先看一下規約模式的類關系圖(tu)
下面是我對(dui)原來結構(gou)(gou)的修改(gai)(由于原程序是三(san)層(ceng)架(jia)構(gou)(gou),所(suo)以我就不改(gai)變原有架(jia)構(gou)(gou)了,只是對(dui)代(dai)碼進(jin)行重構(gou)(gou),DAL層(ceng),BLL層(ceng),WEB層(ceng))
DAL層
IRepository倉(cang)儲接(jie)口如下,怎么去實現就不放了,呵呵
public interface IRepository<TEntity> where TEntity : class { /// <summary> /// 添加實體并提交到數據服務器 /// </summary> /// <param name="item">Item to add to repository</param> void Add(TEntity item); /// <summary> /// 移除實體并(bing)提交(jiao)到數據(ju)服務器 /// 如果(guo)表存在約束,需要先刪除子表信(xin)息 /// </summary> /// <param name="item">Item to delete</param> void Remove(TEntity item); /// <summary> /// 修(xiu)改(gai)實體并提交到數(shu)據服務(wu)器 /// </summary> /// <param name="item"></param> void Modify(TEntity item); /// <summary> /// 通過指定規約,得到實體對象(xiang) /// </summary> /// <param name="specification"></param> /// <returns></returns> TEntity GetEntity(ISpecification<TEntity> specification); /// <summary> /// 通(tong)用表(biao)達式樹,得到實體 /// </summary> /// <param name="predicate"></param> /// <returns></returns> TEntity GetEntity(Expression<Func<TEntity, bool>> predicate); /// <summary> /// Get all elements of type {T} in repository /// </summary> /// <returns>List of selected elements</returns> IQueryable<TEntity> GetEntities(); /// <summary> /// Get all elements of type {T} that matching a /// Specification <paramref name="specification"/> /// </summary> /// <param name="specification">Specification that result meet</param> /// <returns></returns> IQueryable<TEntity> GetEntities(ISpecification<TEntity> specification); /// <summary> /// 通用表(biao)達式樹,得到集合 /// </summary> /// <param name="predicate"></param> /// <returns></returns> IQueryable<TEntity> GetEntities(Expression<Func<TEntity, bool>> predicate); }
IOrderRepository接口如下
public interface IOrderRepository : Domain.Core.IRepository<Order_Info> { void InsertOrder(Order_Info entity); }
DAL底層為數據持久(jiu)化層,它是非常穩(wen)定的(de),只(zhi)提供(gong)最基本的(de)表(biao)操作,具體業(ye)務(wu)如何組成,全放在BLL層去實現
BLL層
這一(yi)層(ceng)中定義(yi)具體業務的規約,并組成查詢方(fang)(fang)法及調用DAL層(ceng)的具體方(fang)(fang)法(DAL層(ceng)來接(jie)受從BLL層(ceng)傳過(guo)來的ISpecification參數)
/// <summary> /// 根(gen)據下單日期得(de)到訂單列表 /// </summary> public class OrderFromDateSpecification : Specification<Order_Info> { DateTime? _fromDate; DateTime? _toDate; public OrderFromDateSpecification(DateTime? fromDate, DateTime? toDate) { _fromDate = fromDate ?? DateTime.MinValue; _toDate = toDate ?? DateTime.MaxValue; } public override global::System.Linq.Expressions.Expression<Func<Order_Info, bool>> SatisfiedBy() { Specification<Order_Info> spec = new TrueSpecification<Order_Info>(); spec &= new DirectSpecification<Order_Info>(o => o.CreateDate >= _fromDate && o.CreateDate <= _toDate); return spec.SatisfiedBy(); } }
/// <summary> /// 通過用(yong)戶信息得到(dao)他(ta)的訂單(dan)列表 /// </summary> public class OrderFromUserSpecification : Specification<Order_Info> { int _userID = default(Int32); public OrderFromUserSpecification(int userID) { _userID = userID; } public override global::System.Linq.Expressions.Expression<Func<Order_Info, bool>> SatisfiedBy() { Specification<Order_Info> spec = new TrueSpecification<Order_Info>(); spec &= new DirectSpecification<Order_Info>(o => o.UserID == _userID); return spec.SatisfiedBy(); } }
業(ye)(ye)務層(ceng)真(zhen)實的查詢(xun)主體(ti),只要在(zai)一個(ge)方法里寫就OK了(le),然后(hou)它(ta)非常穩定,如果(guo)以(yi)后(hou)還有其它(ta)查詢(xun)業(ye)(ye)務出來,直(zhi)接添加(jia)一個(ge)查詢(xun)規約(yue)即可
/// <summary> /// 根據WEB層傳來及(ji)組(zu)件好的規約,返回集體 /// </summary> /// <param name="spec"></param> /// <returns></returns> public List<Order_Info> GetOrder_InfoBySpec(ISpecification<Order_Info> spec) { return _iOrderRepository.GetEntities(spec).ToList(); }
WEB層
Web層建(jian)立一個指定的(de)(de)規約,并為規約組件所需(xu)要的(de)(de)數據即(ji)可
public ActionResult List(int? userID) { ISpecification<Order_Info> spec = new OrderFromUserSpecification(userID ?? 0); var model = orderService.GetOrder_InfoBySpec(spec); return View(model); }
如果這時(shi)來了個新需(xu)要,使用用戶名進行(xing)查詢,你可以直接(jie)建立一個OrderFromUserNameSpecification的(de)規約即可,而不需(xu)要修改OrderService,呵呵!