[英]LINQ to Entities does not recognize the method - Entity Framework and DDD
[英]LINQ to Entities does not recognize the method (on a related entity)
我试图让一些代码更具可读性,并且我不太了解如何构造扩展方法和/或表达式来实现它。 我们目前有许多实体上有RecordStatusTypeId
(从IRecordStatus
的接口IRecordStatus
)
public interface IRecordStatus
{
int RecordStatusTypeId { get; set; }
RecordStatusType RecordStatusType { get; set; }
}
这里的目标是使用.ActiveRecords()
等扩展方法替换.Where(RecordStatusTypeId != (int)RecordStatusTypes.Deleted)
类的.ActiveRecords()
我可以使用以下Extension方法完成此操作:
public static IQueryable<T> ActiveRecords<T>(this DbSet<T> entitySet)
where T : class, IRecordStatus
{
return entitySet.Where(e => e.RecordStatusTypeId != (int)RecordStatusTypes.Deleted);
}
*我有DbSet<T>
, IQueryable<T>
, ICollection<T>
和IEnumerable<T>
扩展方法
这适用于像MyDbContext.Entities.Where(e => e.RecordStatusTypeId != (int)RecordStatusTypes.Deleted)
这样的语句,但如果我尝试替换类似的东西,我会收到错误“LINQ to Entities无法识别方法” :
MyDbContext.Entities.Where(e => e.RelatedEntity.Where(re => re.RecordStatusTypeId != (int)RecordStatusTypes.Deleted));
我想做什么:
MyDbContext.Entities.Where(e => e.RelatedEntity.ActiveRecords().Any());
如何更改扩展方法(或添加表达式)以便我可以过滤DbSet
上的活动记录以及linq子句中的相关实体?
看起来您可能已经错误地完成了转换。 应该:
MyDbContext.Entities.Where(e => e.RelatedEntity.Where(re => re.RecordStatusTypeId != (int)RecordStatusTypes.Deleted));
转换为:
MyDbContext.Entities.Where(e => e.RelatedEntity.ActiveRecords().Any());
您将获得异常,因为Queryable.Where
需要一个可以转换为SQL的表达式,并且ActiveRecords
无法转换为SQL。
您需要做的是更新表达式以将对ActiveRecords
的调用扩展为对.Where(e => e.RecordStatusTypeId != (int)RecordStatusTypes.Deleted
的调用.Where(e => e.RecordStatusTypeId != (int)RecordStatusTypes.Deleted
)。
我将提供一个有效的解决方案草案。 它非常特定于您提供的示例。 你可能应该努力使它成为通用的。
以下表达式访问者基本上将对ActiveRecords
的调用更改为对具有适当表达式作为参数的Where
的调用:
public class MyVisitor : ExpressionVisitor
{
protected override Expression VisitMethodCall(MethodCallExpression m)
{
if (m.Method.Name == "ActiveRecords")
{
var entityType = m.Method.GetGenericArguments()[0];
var whereMethod = genericWhereMethod.MakeGenericMethod(entityType);
var param = Expression.Parameter(entityType);
var expressionToPassToWhere =
Expression.NotEqual(
Expression.Property(param, "RecordStatusTypeId"),
Expression.Constant((int)RecordStatusTypes.Deleted));
Expression newExpression =
Expression.Call(
whereMethod,
m.Arguments[0],
Expression.Lambda(
typeof(Func<,>).MakeGenericType(entityType, typeof(bool)),
expressionToPassToWhere,
param));
return newExpression;
}
return base.VisitMethodCall(m);
}
//This is reference to the open version of `Enumerable.Where`
private static MethodInfo genericWhereMethod;
static MyVisitor()
{
genericWhereMethod = typeof (Enumerable).GetMethods(BindingFlags.Public | BindingFlags.Static)
.Where(x => x.Name == "Where" && x.GetGenericArguments().Length == 1)
.Select(x => new {Method = x, Parameters = x.GetParameters()})
.Where(x => x.Parameters.Length == 2 &&
x.Parameters[0].ParameterType.IsGenericType &&
x.Parameters[0].ParameterType.GetGenericTypeDefinition() == typeof (IEnumerable<>) &&
x.Parameters[1].ParameterType.IsGenericType &&
x.Parameters[1].ParameterType.GetGenericTypeDefinition() == typeof (Func<,>))
.Select(x => x.Method)
.Single();
}
}
然后,您可以创建一个特殊的WhereSpecial
方法来访问表达式,然后将其传递给Queryable
的真实Where
方法:
public static class ExtentionMethods
{
public static IQueryable<T> WhereSpecial<T>(this IQueryable<T> queryable, Expression<Func<T,bool>> expression )
{
MyVisitor visitor = new MyVisitor();
var newBody = visitor.Visit(expression.Body);
expression = expression.Update(newBody, expression.Parameters);
return queryable.Where(expression);
}
}
然后你可以像这样使用它:
var result = MyDbContext.Entities.WhereSpecial(e => e.RelatedEntity.ActiveRecords().Any());
尝试使查询IQueryable的主要部分,然后标记在where子句上...
就像是:
IQueryable temp = from x in MyDbContext.Entities
select x;
temp = temp.Where(e => e.RelatedEntity.ActiveRecords().Any());
或评估主要部分然后执行内存中的位置。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.