具有实体框架并使用 orderby 和 skip/take 的规范模式

Posted

技术标签:

【中文标题】具有实体框架并使用 orderby 和 skip/take 的规范模式【英文标题】:Specification pattern with entity framework and using orderby and skip/take 【发布时间】:2014-10-04 07:49:21 【问题描述】:

我捡到了一个使用规范模式的项目,一个我以前没有用过的模式,我不得不去研究这个模式。我注意到它没有 OrderBySkip/Take 功能,而且我找不到任何显示如何使用该模式实现此功能的地方。

我正在努力思考如何最好地将其添加到规范模式中。但是我遇到了一些问题,比如规范处理“Expression<Func<T, bool>>”,而我认为我不能将它与 orderby 等一起存储

基本上有这样一个类:

public class Specification<T> : ISpecification<T>

    public Expression<Func<T, bool>> Predicate  get; protected set; 

    public Specification(Expression<Func<T, bool>> predicate)
    
        Predicate = predicate;
    

    public Specification<T> And(Specification<T> specification)
    
        return new Specification<T>(this.Predicate.And(specification.Predicate));
    

    public Specification<T> And(Expression<Func<T, bool>> predicate)
    
        return new Specification<T>(this.Predicate.And(predicate));
    

    public Specification<T> Or(Specification<T> specification)
    
        return new Specification<T>(this.Predicate.Or(specification.Predicate));
    

    public Specification<T> Or(Expression<Func<T, bool>> predicate)
    
        return new Specification<T>(this.Predicate.Or(predicate));
    

    public T SatisfyingItemFrom(IQueryable<T> query)
    
        return query.Where(Predicate).SingleOrDefault();
    

    public IQueryable<T> SatisfyingItemsFrom(IQueryable<T> query)
    
        return query.Where(Predicate);
    

这允许创建规范,传入 where 子句。它还允许使用“And”、“Or”链接规则。例如:

var spec = new Specification<Wave>(w => w.Id == "1").And(w => w.WaveStartSentOn > DateTime.Now);

如何为“OrderBy”和“Take”添加方法?

由于这是现有代码,我无法进行任何会影响现有代码的更改,重构它是一项相当艰巨的工作。所以任何解决方案都需要与现有的东西很好地配合。

【问题讨论】:

【参考方案1】:

怎么样

public class Specification<T> : ISpecification<T>

    public Expression<Func<T, bool>> Predicate  get; protected set; 
    public Func<IQueryable<T>, IOrderedQueryable<T>> Sort get; protected set; 
    public Func<IQueryable<T>, IQueryable<T>> PostProcess get; protected set;

    public Specification<T> OrderBy<TProperty>(Expression<Func<T, TProperty>> property)
    
        var newSpecification = new Specification<T>(Predicate)  PostProcess = PostProcess  ;
        if(Sort != null) 
             newSpecification.Sort = items => Sort(items).ThenBy(property);
         else 
             newSpecification.Sort = items => items.OrderBy(property);
        
        return newSpecification;
    

    public Specification<T> Take(int amount)
    
        var newSpecification = new Specification<T>(Predicate)  Sort = Sort  ;
        if(PostProcess!= null) 
             newSpecification.PostProcess= items => PostProcess(items).Take(amount);
         else 
             newSpecification.PostProcess= items => items.Take(amount);
        
        return newSpecification;
    

    public Specification<T> Skip(int amount)
    
        var newSpecification = new Specification<T>(Predicate)  Sort = Sort  ;
        if(PostProcess!= null) 
             newSpecification.PostProcess= items => PostProcess(items).Skip(amount);
         else 
             newSpecification.PostProcess= items => items.Skip(amount);
        
        return newSpecification;
    

待办事项:

OrderByDescending 的类似构造 更新您的其他方法,例如,当您调用“And”时,“Sort”值和“PostProcess”值不会丢失

那么你的满意方法就变成了:

private IQueryable<T> Prepare(IQueryable<T> query) 

    var filtered = query.Where(Predicate);
    var sorted = Sort(filtered);
    var postProcessed = PostProcess(sorted);
    return postProcessed;


public T SatisfyingItemFrom(IQueryable<T> query)

    return Prepare(query).SingleOrDefault();


public IQueryable<T> SatisfyingItemsFrom(IQueryable<T> query)

    return Prepare(query);

TODO:检查“Prepare”方法中的 Sort 和 PostProcess 是否不为空

用法:

var spec = new Specification<Wave>(w => w.Id == "1")
              .And(w => w.WaveStartSentOn > DateTime.Now)
              .OrderBy(w => w.WaveStartSentOn)
              .Skip(20)
              .Take(5);

【讨论】:

感谢您的回复。这不会有不保持东西被锁住的顺序的问题吗?从某种意义上说,它只是存储排序,实际上并没有将其添加到查询中,以便调用其他操作。 我相信订单会得到尊重。更具体地说,这一行应确保: if(Sort != null) newSpecification.Sort = items => Sort(items).ThenBy(property); 其实,我认为链接的顺序无关紧要,我更多的是考虑你在对象方面会遇到的场景。不在数据库方面,这就是规范的用途!所以我认为你的建议会很好。 @Moeri 你的界面是什么样的? 哈,好问题,这里的界面似乎很多余。我没有原始代码了,但我敢说它包含所有公共方法,并且存储库方法将接受 ISpecification 而不是 Specification。

以上是关于具有实体框架并使用 orderby 和 skip/take 的规范模式的主要内容,如果未能解决你的问题,请参考以下文章

必须在大表上的 .Skip() 和 .Take() 之前在实体框架 4.1 中调用 .ToList()

LINQ 查询添加 orderby 使 Skip 和 Take 不起作用 Linqpad

C# 实体框架 OrderBy Children's Children's with a where 子句

具有 SQL Server 连接的实体框架 6 尝试使用 MySqlClient 并崩溃

实体框架查询忽略我的 orderby

方法 'OrderBy' 必须在方法 'Skip' 之前调用 异常