2013-08-02 83 views
6

我有点累的像这样的服务层的代码编写行:强类型的LINQ滤波方法

下面的代码只是为了读者的例子。因此,他们可能有错误或错别字,不好意思吧:)

//ViewModel 
public class EntityIndexFilterPartial 
{ 
    public int? Id { get; set; } 
    public DateTime? StartDate { get; set; } 
    public DateTime? EndDate { get; set; } 
    public IEnumerable<SelectListItem> StatusList { get; set; } 
    public int? StatusID { get; set; } 
} 


//Service Layer method 
//Method parameters are reperesents view model properties 
public IQueryable<Entity> FilterBy(int? id, DateTime? startDate, DateTime? endDate, int? statusId) 
{ 
    var filter = _db.Entities.AsQueryable(); 
    if (id.HasValue) 
     filter = filter.Where(x => x.Id == id.Value); 
    if (startDate.HasValue) 
     filter = filter.Where(x => x.StartDate >= startDate.Value); 
    if (endDate.HasValue) 
     filter = filter.Where(x => x.EndDate <= endDate.Value); 
    if (statusId.HasValue) 
     filter = filter.Where(x => x.EntityStatus.StatusID == statusId); 
    return filter; 
} 

我寻找足够漂亮了一些聪明的设计代码。我知道动态LINQ库,我使用它。但是我正在寻找强类型的过滤。 我不想写魔术字符串或某种他们。

所以基本上我找到了一些解决方案,但我想从这个社会听到一些写得好聪明的代码。当然,可能有几十种解决方案,但是又如何编写强类型过滤服务层代码。任何想法...

这里有一些我的解决方案:

解决方案1: 同FilterBy方法,但参数是不同的,现在正在表达式列表。所以这意味着我在控制器中创建predicateList并将它发送到这里。

public IQueryable<Entity> FilterBy(List<Expression<Func<Entity,bool>>> predicateList) 
{ 
    var filter = _db.Entities.AsQueryable(); 
    foreach (var item in predicateList) 
    { 
     filter = filter.FilterBy(item); 
    } 
    return filter; 
} 

解决方案2: FilterBy方法服用EntityIndexFilterPartial如在应用服务层(未域服务)参数。我确信这个设计有一些问题,但我想听听你的意见。

public IQueryable<Entity> FilterBy(EntityIndexFilterPartial filterPartial) 
{ 
    //I'm calling the domain service layer FilterBy method such as like in solution 1. 
} 

解决方案3: 我觉得这个要比别人更好,但我仍然在想的东西更简单,更好的代码。

//helper class 
public static class FilterByHelper 
{ 
    public static IQueryable<T> If<T>(this IQueryable<T> filter, bool condition, Expression<Func<T, bool>> predicate) 
    { 
     if (condition) 
      return filter.FilterBy(predicate); 
     return filter; 
    } 

    public static IQueryable<T> FilterBy<T>(this IQueryable<T> filter, Expression<Func<T, bool>> predicate) 
    { 
     return filter.Where(predicate); 
    } 
} 


public IQueryable<Entity> FilterBy(int? id, DateTime? startDate, DateTime? endDate, int? statusId) 
{ 
    return _db.Entities 
     .If(id.HasValue, x => x.Id == id.Value) 
     .If(startDate.HasValue, x => x.StartDate >= startDate.Value) 
     .If(endDate.HasValue, x => x.EndDate <= endDate.Value) 
     .If(statusId.HasValue, x => x.EntityStatus.StatusID == statusId); 
} 

我知道这成为一个有点长的问题,但我希望我清楚地问我想问什么。

作为一个快速和简单的问题,你知道从写这些过滤代码相同的路线为我们节省任何设计巧妙的代码?

顺便说一句,我不是寻找设计模式解决方案或巨大的答案,你可以给我一些例子或说如何找到更好的路径就够了。

当然,如果你写一个完整的解释回应,我会appricated。

谢谢。

回答

8

您是否尝试过简单||条件?

return _db.Entities 
    .Where(x => id == null || x.Id == id.Value) 
    .Where(x => startDate == null || x.StartDate >= startDate.Value) 
    .Where(x => endDate == null || x.EndDate <= endDate.Value) 
    .Where(x => statusId == null || x => x.EntityStatus.StatusID == statusId); 

?我希望在查询优化之后,无操作过滤器就相当于根本不添加过滤器。

+0

+1,但恐怕OP不会喜欢这个codez要么;) – Icarus

+0

+1这是很好的把戏。您能否提出一个更好的解决方案来提供服务层方法参数我关于摆脱这些参数的问题的一部分。谢谢:) –

+0

@YusufUzun:对不起,我真的不明白你的意思,我很害怕。你如何通过没有任何参数的过滤器? –

2

我用流利的扩展方法解决了这个问题。我觉得这是解决这类问题的一个很好的解决方案。使用定义过滤器的流畅风格的好处在于,当您真正使用过滤器时,它会生成一些真正可读的代码。这在实践中的作用是让你的服务层更具动态性。

例如

如果你有

public class User { 
    public int Id {get;set;} 
    public DateTime CreatedOn {get;set;} 
    public string Name {get;set;} 
    public DateTime BirthDate {get;set;} 
} 

你可以写类似这样

public IQueriable<User> OlderThan(this IQueriable<User> users, DateTime olderThan){/*implementation*/} 

public IQueriable<User> CreatedAfter(this IQueriable<User> users, DateTime createdAfter){/*implementation*/} 

//or something more generic 
public IQueriable<User> WhereName(this IQueriable<User> users, Expression<Func<string,bool>> nameQuery){/*implementation*/} 

一些过滤器,然后链接这些东西togeather像这样:

users 
    .CreatedAfter(new DateTime(2011,1,1)) 
    .OlderThan(new DateTime(1985,1,2)) 
    .WhereName(n=>n.StartsWith("L")); 

这允许您过滤逻辑更加动态的一点,而无需创建包罗万象的风格滤波器,是很难维持的,复杂的

实际上,这意味着

  • 过滤器可以很简单
  • 您可以创建聚合适用其他过滤器的过滤器内部
  • 过滤器仅执行一个单一的目的
  • 他们能有业务相关的名称

在我的博客中,我用一些真实的例子来谈论这种方法。 http://blog.staticvoid.co.nz/2013/2/25/a_case_for_generic_repositories

+0

+1有趣的想法,所以你说的是创建一个流利的扩展方法,对该域对象的每个过滤。但是,它不会让我们的代码更多。但是,它易于维护和理解。不幸的是,大多数开发人员不想写这样的条件。你是在控制器还是服务层使用这些方法? –

+0

我接受了@JonSkeet的回答,因为评论让我得到了比我更好的方法。但这也是非常好的方法,我也喜欢。不幸的是,有很多开发人员不想为每个属性编写这些代码。我会尽量使它通用。谢谢:) –

+0

@YusufUzun我个人在控制器上使用这些,但很多人不喜欢在该级别使用查询,所以会在不同的层次上枚举集合。在代码量方面,我不认为这种方法在物理上产生更多的代码,它只是以不同的方式分解。你会得到更多的方法,但每个方法都会更简单。这是一种折衷,但这是我个人喜欢这样做的方式。 –

2

您好Yusuf这篇文章可能会对您有所帮助。解决方案看起来很聪明,它实现了动态的条件来查询LINQ并且看起来很简单。

http://amitech.co/amitech-lab/item/dynamically-add-conditions-in-linq

希望它可以帮助

+0

嗨@hkoseoglu,这是另一个不错的用法。我在我的项目中也使用PredicateBuilder。但它仍然没有回应我对这个问题的要求。感谢提醒。 –