ABP框架 - 规约

文档目录html

 

本节内容:数据库

 

简介express

规约模式是一个特别的软件设计模式,业务逻辑可使用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的仓储支持表达式,这是一个合法的用法,在应用里,你不是必定要定义或使用任何规约,因而你能够继承使用表达式,因此,规约的要点在哪?为什么,什么时候应当考虑使用它们?

 

什么时候使用

使用规约的好处:

  • 可复用:设想你须要在你代码里多处用到"优质客户"过滤,若是你使用表达式而不建立一个规约,那若是在之后你须要修改"优质客户"的定义会发生什么(如:你想改为余额至少从$100,000到250,000并包含其它条件,诸如客户注册超过3年),若是你使用规约,你只须要修改一个类,若是你使用(复制/粘贴)相同的表达式,你就要修改全部用到的地方.
  • 可组合:你能够联合多个规约来建立新规约,这是另外一种形式的复用.
  • 命名化:PremiumCustomerSpecification比一个复杂的表达式更好地表述了意图, 因此,若是你想一个表达式在你的业务里变得可顾名思义,那么考虑使用规约.
  • 可测试:一个规约是一个单独的(且易于)可测试的对象.

 

什么时候不用

  • 没有业务表达式: 你能够在你的业务不涉及表达式和操做时不使用规约.
  • 建立报表:若是你只是建立一个报表就不要用规约,而直接使用IQueryable,实际上,你甚至可使用原始的SQL,视图或其它报表工具.DDD不关心报表和底层数据库存储获取查询好处及视图性能.

 

英文原文:http://www.aspnetboilerplate.com/Pages/Documents/Specifications

相关文章
相关标签/搜索