6

我有点厌倦了编写这样的服务层代码行:

下面的代码只是读者的一个例子。所以他们可能有错误或拼写错误,对此感到抱歉:)

//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作为Application Service层(不是域服务)的参数。我敢肯定这个设计有一些问题,但我想听听你的意见。

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);
}

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

作为一个快速而简单的问题,您是否知道任何设计巧妙的代码可以使我们免于编写相同的这些过滤代码行?

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

当然,如果您写了一个完整的解释性回复,我会很高兴的。

谢谢你。

4

3 回答 3

9

你试过简单的||条件吗?

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);

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

于 2013-08-02T22:12:06.047 回答
3

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

例如

如果你有

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

你可以写一些类似的过滤器

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

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

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

然后像这样将这些东西链接在一起:

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

于 2013-08-02T22:29:57.210 回答
2

嗨 Yusuf 这篇文章可能对你有帮助。解决方案看起来很聪明,它实现了动态 where 条件来 linq 查询,看起来很简单。

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

希望能帮助到你

于 2013-08-03T07:10:15.193 回答