我有点厌倦了编写这样的服务层代码:
下面的代码只是读者的一个例子。所以他们可能有错误或错别字,抱歉:)
//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方法但参数不同,现在采用表达式列表。所以这意味着我在控制器中创建谓词列表并将其发送到此处。
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);
}
我知道这个问题变得有点长,但我希望我清楚地问我想问什么。
作为一个快速而简单的问题,您是否知道任何设计巧妙的代码,以避免我们编写这些过滤代码的相同行?
顺便说一句,我不是在寻找设计模式解决方案或大答案,你可以给我一些例子或者说如何找到更好的路径就足够了。当然如果你写一个完整的解释回复,我会被评估。
谢谢。
答案 0 :(得分: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);
?我希望在查询优化之后,无操作过滤器相当于根本不添加过滤器。
答案 1 :(得分: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"));
这样可以使您的过滤逻辑更加动态,而无需创建难以维护和复杂的catchall样式过滤器
实际上这意味着
在我的博客中,我用一些真实的例子来讨论这个方法。 http://blog.staticvoid.co.nz/2013/2/25/a_case_for_generic_repositories
答案 2 :(得分:2)
您好Yusuf这篇文章可能对您有所帮助.Solution看起来很聪明,它实现了linq查询条件的动态,看起来很简单。
http://amitech.co/amitech-lab/item/dynamically-add-conditions-in-linq
希望有所帮助