我正在尝试创建一个方法来封装LINQ Where
调用(在IQueryable上),以便对集合中的特定字段进行过滤,但是如何使其工作则不知所措。
例如,我有一组Job对象,类似于以下内容:
public class Job
{
public int Id { get; set; }
public int StatusId { get; set; }
}
public class StatusItem
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsAvailable { get; set; }
public static readonly StatusItem Canceled = new StatusItem() { Id = (int)StatusEnum.Canceled, Name = StatusEnum.Canceled.ToString(), IsAvailable = true };
public static readonly StatusItem Created = new StatusItem() { Id = (int)StatusEnum.Created, Name = StatusEnum.Created.ToString(), IsAvailable = true };
public static readonly StatusItem Open = new StatusItem() { Id = (int)StatusEnum.Open, Name = StatusEnum.Open.ToString(), IsAvailable = true };
public static readonly StatusItem Assigned = new StatusItem() { Id = (int)StatusEnum.Assigned, Name = StatusEnum.Assigned.ToString(), IsAvailable = false };
}
我希望有一个服务方法只使用系统定义的状态强制执行过滤,如下所示:
IEnumerable<Job> GetAll(Expression<Func<StatusItem, bool>> statusFilter)
{
// Jobs is IQueryable<job>. How do I apply statusFilter to Job.StatusId?
return jobs.Where(/* some magic here? */);
}
通话类似于:
return JobService.GetAll(s => s > StatusItem.Open && s < StatusItem.Assigned);
编辑:一直盯着这个太久了。脑现在糊里糊涂。尝试修复以前的错误
答案 0 :(得分:0)
我不知道这是否是您所需要的,但请查看:
jobs.Where(x => statusFilter.Compile().Invoke((StatusEnum)x.StatusId));
还考虑将属性StatusId更改为StatusEnum。属性也应该公开。
class Job
{
public int Id { get; set; }
public StatusEnum StatusId { get; set; }
}
使用这种声明,不需要转换为StatusEnum:
jobs.Where(x => statusFilter.Compile().Invoke(x.StatusId));
答案 1 :(得分:0)
最简单的方法是使用Expression<Func<Job, bool>>
代替Expression<Func<StatusEnum, bool>>
,这样可以让您编写如下内容:
IEnumerable<Job> GetAll(Expression<Func<Job, bool>> jobFilter)
{
return jobs.Where(jobFilter);
}
如果您想要通过状态以外的其他方式进行过滤,它还具有更灵活的优势。
如果您真的想使用Expression<Func<StatusEnum, bool>>
,它会变得更加复杂,因为您需要重写表达式以从Expression<Func<Job, bool>>
生成Expression<Func<StatusEnum, bool>>
。这是一种方法:
IEnumerable<Job> GetAll(Expression<Func<StatusEnum, bool>> statusFilter)
{
var job = Expression.Parameter(typeof(Job), "job");
var visitor = new ParameterReplacementVisitor(
statusFilter.Parameters[0],
Expression.Property(job, nameof(Job.StatusId)));
Expression<Func<Job, bool>> jobFilter =
Expression.Lambda<Func<Job, bool>>(
visitor.Visit(statusFilter.Body),
job);
return jobs.Where(jobFilter);
}
class ParameterReplacementVisitor : ExpressionVisitor
{
private readonly ParameterExpression _parameter;
private readonly Expression _replacement;
public ParameterReplacementVisitor(ParameterExpression parameter, Expression replacement)
{
_parameter = parameter;
_replacement = replacement;
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (node == _parameter)
return _replacement;
return node;
}
}