在规范中组合 C# 代码和数据库代码

cbp*_*cbp 5 c# specification-pattern

有时您需要定义一些业务规则,规范模式是一个有用的工具。例如:

public class CanBorrowBooksSpec : ISpecification<Customer>
{
    public bool Satisfies(Customer customer)
    {
         return customer.HasLibraryCard
              && !customer.UnpaidFines.Any();
    }
}
Run Code Online (Sandbox Code Playgroud)

然而,我经常发现我需要将这些规则“推送”到 SQL 中以提高性能或满足记录分页列表之类的要求。

然后,我必须为规则编写代码两次,一次使用 CLR 代码,一次使用 SQL(或 ORM 语言)。

您如何组织这样的代码?

最好将代码放在同一个类中。这样,如果开发人员正在更新业务规则,他们忘记更新两组代码的可能性就较小。例如:

public class CanBorrowBooksSpec : ISpecification<Customer>
{
    public bool Satisfies(Customer customer)
    {
         return customer.HasLibraryCard
              && !customer.UnpaidFines.Any();
    }

    public void AddSql(StringBuilder sql)
    {
        sql.Append(@"customer.HasLibraryCard 
                     AND NOT EXISTS (SELECT Id FROM CustomerUnpaidFines WHERE CustomerId = customer.Id)");
    }
}
Run Code Online (Sandbox Code Playgroud)

然而,这对我来说似乎很难看,因为我们现在将担忧混合在一起。

另一种替代方案是使用 Linq-To-YourORM 解决方案,因为 LINQ 代码可以针对集合运行,也可以转换为 SQL。但我发现,除了最琐碎的场景之外,这种解决方案几乎不可能实现。

你做什么工作?

Era*_*nga 5

我们将规范模式与实体框架一起使用。我们是这样处理的

public interface ISpecification<TEntity>
{
    Expression<Func<TEntity, bool>> Predicate { get; }
}


public class CanBorrowBooksSpec : ISpecification<Customer>
{
    Expression<Func<Customer, bool>> Predicate 
    { 
       get{ return customer => customer.HasLibraryCard
              && !customer.UnpaidFines.Any()} 
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以将它用于 LINQ-to-Entities,例如

db.Customers.Where(canBorrowBooksSpec.Predicate);
Run Code Online (Sandbox Code Playgroud)

在 LINQ 到对象中,例如

customerCollection.Where(canBorrowBooksSpec.Predicate.Compile());
Run Code Online (Sandbox Code Playgroud)