傳統倉儲介面隨查詢需求增長而膨脹:
interface ICustomerRepository {
Customer GetByName(string name);
Customer GetByUserName(string userName);
IList<Customer> GetAllRetired(); // 「退休」定義模糊:年齡?病退?性別?
}
缺點:擴展需修改介面與實作、語義不明確、難以測試。
核心介面:
interface ISpecification {
bool IsSatisfiedBy(object obj);
Expression<Func<object, bool>> Expression { get; } // 引入 LINQ Expression
}
倉儲簡化為:
interface IRepository<TEntity> {
TEntity GetBySpecification(ISpecification spec);
IEnumerable<TEntity> GetAllBySpecification(ISpecification spec);
}
具體規約類別:
class NameSpecification : Specification {
string _name;
public NameSpecification(string name) { _name = name; }
public override Expression<Func<object, bool>> Expression
=> o => ((Customer)o).FirstName == _name;
}
class RetiredSpecification : Specification {
public override Expression<Func<object, bool>> Expression
=> o => ((Customer)o).Age >= 60;
}
用法:repo.GetAllBySpecification(new RetiredSpecification())。解耦倉儲與查詢條件,條件可組合、可測試、可擴展。
Expression<Func<T, bool>>,直接傳遞給 EF/NHibernate/Db4o 的 LINQ Provider,在資料庫端過濾,避免全表掃描。Specification.Eval(o => (o as Customer).UserName == "daxnet"),無需建立類別即可即時建構規約。定義 ISpecificationParser<TResult>,針對 NHibernate 實現 NHibernateSpecificationParser 將規約轉為 ICriteria;EF 則直接使用 Expression,無需額外解析器。

Specification + LINQ Expression 實現了:查詢語義明確化、倉儲介面穩定化、跨 ORM 可移植、效能下推資料庫。歡迎討論更多實踐細節。