文檔目錄html
本節內容:數據庫
規約模式是一個特別的軟件設計模式,業務邏輯可使用boolean邏輯從新連接業務邏輯(維基百科).設計模式
實踐中的大部分狀況,它是爲實體或其它業務對象,定義可複用的過濾器.ide
在此小節,咱們將看到規約模式的必要性,這節是通用的,與ABP的實現無關.性能
假設你有一個服務方法用來計算客戶的總數,如:測試
public class CustomerManager { public int GetCustomerCount() { //TODO... return 0; } }
你可能想經過一個過濾器獲取客戶數,例如:你有優質的客戶(餘額超過$100,000)或根據註冊年份過濾客戶,因而你能夠建立其它方法,如:GetPremiumCustomerCount(),GetCustomerCountRegisteredInYear(int year),GetPremiumCustomerCountRegisterdInYear(int year)等等,當你有更多條件時,不可能爲每種狀況建立一個組合.優化
這個問題的一個解決方法是規約模式,咱們能夠建立一個單獨的方法,獲取一個參數做爲過濾器:spa
public class CustomerManager { private readonly IRepository<Customer> _customerRepository; public CustomerManager(IRepository<Customer> customerRepository) { _customerRepository = customerRepository; } public int GetCustomerCount(ISpecification<Customer> spec) { var customers = _customerRepository.GetAllList(); var customerCount = 0; foreach (var customer in customers) { if (spec.IsSatisfiedBy(customer)) { customerCount++; } } return customerCount; } }
所以,咱們能夠接受實現了 ISpecification<Customer> 接口的任何對象做爲參數,如:
public interface ISpecification<T> { bool IsSatisfiedBy(T obj); }
而後咱們能夠爲一個customer調用 IsSatisfiedBy 來檢查這個customer是否符合意圖.所以,咱們可用不一樣的過濾器調用相同的GetCustomerCount方法,不須要修改方法自己.
理論上這個方案很好了,但在C#中能夠改進得更完美, 例如:從數據庫中獲取全部的客戶來檢查是否知足給定的規約/條件,這可不是頗有效率,接下來的小節裏,咱們將看到ABP的實現,它解決了這個問題.
ABP定義了以下的ISpecification接口:
public interface ISpecification<T> { bool IsSatisfiedBy(T obj); Expression<Func<T, bool>> ToExpression(); }
添加一個 ToExpression() 方法,它返回一個表達式(expression),用來更好地結合IQueryable和表達式樹,所以,爲數據庫層面接受一個過濾器,咱們只須要簡單地傳遞一個規約到一個倉儲裏便可.
咱們一般從 Specification<T> 類繼承,而不是直接實現ISpecification<T>接口.Specification類自動實現IsSatisfiedBy方法,因此,咱們只須要定義ToExpression,讓咱們建立些規約類:
//Customers with $100,000+ balance are assumed as PREMIUM customers. public class PremiumCustomerSpecification : Specification<Customer> { public override Expression<Func<Customer, bool>> ToExpression() { return (customer) => (customer.Balance >= 100000); } } //A parametric specification example. public class CustomerRegistrationYearSpecification : Specification<Customer> { public int Year { get; } public CustomerRegistrationYearSpecification(int year) { Year = year; } public override Expression<Func<Customer, bool>> ToExpression() { return (customer) => (customer.CreationYear == Year); } }
如你所見,咱們只是實現簡單的lambda表達式來定義規約,讓咱們用這些規約獲取客戶數:
count = customerManager.GetCustomerCount(new PremiumCustomerSpecification()); count = customerManager.GetCustomerCount(new CustomerRegistrationYearSpecification(2017));
如今,咱們能夠優化 CustomerManager 在數據庫中接受過濾器:
public class CustomerManager { private readonly IRepository<Customer> _customerRepository; public CustomerManager(IRepository<Customer> customerRepository) { _customerRepository = customerRepository; } public int GetCustomerCount(ISpecification<Customer> spec) { return _customerRepository.Count(spec.ToExpression()); } }
就這麼簡單,咱們能夠傳遞任何規約給倉儲,所以倉儲可使用表達式做爲過濾器.在本例裏,CustomerManager不是必要的,所以咱們能夠直接使用倉儲和規約來查詢數據庫,但考慮一下:咱們想在某客戶上執行一個業務操做,咱們可使用規約和一個領域服務來指定customers,從而繼續工做.
有個強大的功能:能夠用And,Or,Not 和 AndNot 擴展方法,組合規約.如:
var count = customerManager.GetCustomerCount(new PremiumCustomerSpecification().And(new CustomerRegistrationYearSpecification(2017)));
咱們甚至能夠在已有的規約的基礎上,建立一個新規約:
public class NewPremiumCustomersSpecification : AndSpecification<Customer> { public NewPremiumCustomersSpecification() : base(new PremiumCustomerSpecification(), new CustomerRegistrationYearSpecification(2017)) { } }
AndSpecification 是一個子類,它僅在兩個規約都知足的狀況下才符合條件,而後咱們可使用NewPremiumCustomersSpecification ,就像其它的規約同樣:
var count = customerManager.GetCustomerCount(new NewPremiumCustomersSpecification());
因爲規約模式比C#的lambda表達式舊,因此經過拿來與表達式對比.一些開發者可能認爲規約模式再也不須要,咱們能夠直接傳遞表達式給一個倉儲或領域服務,如:
var count = _customerRepository.Count(c => c.Balance > 100000 && c.CreationYear == 2017);
因爲ABP的倉儲支持表達式,這是一個合法的用法,在應用裏,你不是必定要定義或使用任何規約,因而你能夠繼承使用表達式,因此,規約的要點在哪?爲什麼,什麼時候應當考慮使用它們?
使用規約的好處:
英文原文:http://www.aspnetboilerplate.com/Pages/Documents/Specifications