筛选SQL Server上的实体框架结果
本文关键字:实体 框架 结果 SQL Server 筛选 | 更新日期: 2023-09-27 18:21:04
首先:我们不是在做TPH(按层次结构的表),这会让事情简单得多。
我有大约20个POCO,它们在某些情况下都具有相似的特性。我关心的类似属性是___.CreatedDate
和___.UpdatedDate
。对于一些POCO来说,UpdatedDate
没有意义,所以它们没有这个属性。CreatedDate
将始终存在,UpdatedDate
可能为空。有时还有第三个领域。
无论我在查询20个POCO中的哪一个,从数据库中检索对象的查询看起来总是一样的。然而,查询被重复了20次,每种类型的对象都有一次。我能够将检索部分分解为一个扩展方法(脱离IDbSet<T>
)来进行初始查找和联接,将日期范围过滤作为一项练习留给消费者。我现在想将20个看起来几乎相同的日期范围过滤器合并为一个,但在查询理解方面遇到了问题。
根据POCO,日期筛选逻辑是应该检查UpdatedDate,并将其值与阈值进行比较。如果UpdatedDate为null(或属性不存在),则应改用CreatedDate。
<背景>
我首先在每个POCO上创建一个静态属性getter,并将其命名为"DateFields"。它被键入为IEnumerable<Expression<Func<T, DateTime?>>>
,看起来像这样:
get
{
yield return x => x.UpdatedDate;
yield return x => x.CreatedDate;
yield return x => x.Date;
}
这些字段按照我希望检查的顺序返回,并且对于每个POCO都是唯一的。
然后我创建了一个谓词来检查每个值的高低范围:
public static bool DatesBetween<T> (T value, IEnumerable<Expression<Func<T, DateTime?>>> dates, DateTime? dateFrom, DateTime? dateTo)
{
var firstDate = dates
.Select(expression => expression.Compile())
.FirstOrDefault(func => func(value) != null);
if (firstDate == null)
return false;
var minDate = dateFrom.GetValueOrDefault(SqlDateTime.MinValue.Value);
var maxDate = dateTo.GetValueOrDefault(SqlDateTime.MaxValue.Value);
var actualDate = firstDate(value);
return (minDate <= actualDate && actualDate <= maxDate);
}
然而,正如预期的那样,当我尝试在IQueryable
中使用它时,我会得到一个运行时异常,因为SQL中没有到DatesBetween的转换。我希望通过保留表情树,我可以。。。我不知道。。。让EF开心。整个东西是这样使用的:
return Entities
.GetEntitiesBySomeField(field := "magic value")
.Where(entity => RepositoryExtensions.DatesBetween(entity, MyPOCO.MyDates, dateFrom, dateTo));
我想问的很清楚,有没有一种方法可以在不做AsEnumerable()的情况下实现这种通用过滤(它确实有效,但不能实现我在DB上过滤的目标)。
我最近在一个项目中做了类似的事情。我最终实现了存储库模式,并在存储库上创建了一个名为"ApplyFilter"的方法,看起来像:
void ApplyFilter(Expression<Func<TEntity, bool>> predicate)
{
if(this.resultSet == null)
{
this.resultSet = this.context.Set<TEntity>().AsQueryable();
}
this.resultSet = this.resultSet.Where(predicate);
}
在我的存储库中有一个名为"ResultSet"的属性,它刚刚返回IQueryable<TEntity>resultSet字段,并强制枚举结果(触发EF的数据库调用)。通过这种方式,在枚举结果之前,我可以应用所有动态创建的表达式。
我的代码和你的代码之间的真正区别是,我的代码将布尔谓词附加到最终表达式树中,当你试图在最终表达式树内使用你的布尔谓词时。我的方法基本上是在SQL中创建/附加WHERE子句,而您的方法则尝试从"DatesBetween"方法生成SQL函数调用。
这里有一些尝试:
public static Expression<Func<TEntity, bool>> MakeDateRange<TEntity>(DateTime? dateFrom, DateTime? dateTo)
{
var et = typeof(TEntity);
var param = Expression.Parameter(et, "a");
var prop = et.GetProperty("UpdatedDate");
Expression body = null, left = null, right = null;
if (prop == null)
{
prop = et.GetProperty("CreatedDate");
if (prop == null)
{
prop = et.GetProperty("Date");
}
}
if (dateFrom.HasValue)
{
left = Expression.GreaterThanOrEqual(Expression.PropertyOrField(param, prop.Name), Expression.Constant(dateFrom.GetValueOrDefault()));
}
if (dateTo.HasValue)
{
right = Expression.LessThanOrEqual(Expression.PropertyOrField(param, prop.Name), Expression.Constant(dateTo.GetValueOrDefault()));
}
if (left != null && right != null)
{
body = Expression.AndAlso(left, right);
}
else if (left != null)
{
body = left;
}
else
{
body = right;
}
return Expression.Lambda<Func<TEntity, bool>>(body, param);
}
如果我理解的话,你想在多个结构相似的表上运行相同的查询吗?
如果是这种情况,请考虑创建一个定义这些属性的接口,并让POCO实现该接口。然后让您的泛型参数具有该接口的限制。从那里,您应该能够编写一个使用接口上属性的通用查询。