您使用的 include 方法调用 QueryableExtensions.Include(source, path1) 方法,该方法将表达式转换为字符串路径。
这就是 include 方法的作用:
public static IQueryable<T> Include<T, TProperty>(this IQueryable<T> source, Expression<Func<T, TProperty>> path)
{
Check.NotNull<IQueryable<T>>(source, "source");
Check.NotNull<Expression<Func<T, TProperty>>>(path, "path");
string path1;
if (!DbHelpers.TryParsePath(path.Body, out path1) || path1 == null)
throw new ArgumentException(Strings.DbExtensions_InvalidIncludePathExpression, "path");
return QueryableExtensions.Include<T>(source, path1);
}
因此,您的表达式如下所示(检查表达式中的“Include”或“IncludeSpan”方法):
value(System.Data.Entity.Core.Objects.ObjectQuery`1[TEntity]).MergeAs(AppendOnly)
.IncludeSpan(value(System.Data.Entity.Core.Objects.Span))
您应该挂接 VisitMethodCall 来添加您的表达式:
internal class InjectConditionVisitor<T> : ExpressionVisitor
{
private Expression<Func<T, bool>> queryCondition;
protected override Expression VisitMethodCall(MethodCallExpression node)
{
Expression expression = node;
if (node.Method.Name == "Include" || node.Method.Name == "IncludeSpan")
{
// DO something here! Let just add an OrderBy for fun
// LAMBDA: x => x.[PropertyName]
var parameter = Expression.Parameter(typeof(T), "x");
Expression property = Expression.Property(parameter, "ColumnInt");
var lambda = Expression.Lambda(property, parameter);
// EXPRESSION: expression.[OrderMethod](x => x.[PropertyName])
var orderByMethod = typeof(Queryable).GetMethods().First(x => x.Name == "OrderBy" && x.GetParameters().Length == 2);
var orderByMethodGeneric = orderByMethod.MakeGenericMethod(typeof(T), property.Type);
expression = Expression.Call(null, orderByMethodGeneric, new[] { expression, Expression.Quote(lambda) });
}
else
{
expression = base.VisitMethodCall(node);
}
return expression;
}
}
David Fowl 的 QueryInterceptor 项目不支持“包含”。实体框架尝试使用反射查找“包含”方法,如果未找到则返回当前查询(就是这种情况)。
免责声明: 我是该项目的所有者EF+ http://entityframework-plus.net/.
我添加了一个 QueryInterceptor 功能,支持“包含”来回答您的问题。由于尚未添加单元测试,该功能尚不可用,但您可以下载并尝试源代码:查询拦截器源码 https://github.com/zzzprojects/EntityFramework-Plus/tree/master/src/Z.EntityFramework.Plus.EF6/QueryInterceptor
如果您有问题,请直接联系我(在我的 GitHub 主页底部发送电子邮件),否则这将开始偏离主题。
请注意,“包含”方法通过隐藏一些先前的表达式来修改表达式。因此,有时很难理解幕后到底发生了什么。
我的项目还包含查询过滤器功能,我认为它具有更大的灵活性。
EDIT:从更新的必需中添加工作示例
这是您可以根据您的要求使用的起始代码:
public IQueryable<TEntity> GetAll()
{
var conditionVisitor = new InjectConditionVisitor<TEntity>("Versions", db.Set<TEntity>.Provider, x => x.Where(y => !y.IsDeleted));
return db.Set<TEntity>().Where(e => !e.IsDeleted).InterceptWith(conditionVisitor);
}
var project = await ProjectStore.GetAll().Include(p => p.Versions).SingleOrDefaultAsync(p => p.Id == projectId);
internal class InjectConditionVisitor<T> : ExpressionVisitor
{
private readonly string NavigationString;
private readonly IQueryProvider Provider;
private readonly Func<IQueryable<T>, IQueryable<T>> QueryCondition;
public InjectConditionVisitor(string navigationString, IQueryProvider provder , Func<IQueryable<T>, IQueryable<T>> queryCondition)
{
NavigationString = navigationString;
Provider = provder;
QueryCondition = queryCondition;
}
protected override Expression VisitMethodCall(MethodCallExpression node)
{
Expression expression = node;
bool isIncludeSpanValid = false;
if (node.Method.Name == "IncludeSpan")
{
var spanValue = (node.Arguments[0] as ConstantExpression).Value;
// The System.Data.Entity.Core.Objects.Span class and SpanList is internal, let play with reflection!
var spanListProperty = spanValue.GetType().GetProperty("SpanList", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
var spanList = (IEnumerable)spanListProperty.GetValue(spanValue);
foreach (var span in spanList)
{
var spanNavigationsField = span.GetType().GetField("Navigations", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
var spanNavigation = (List<string>)spanNavigationsField.GetValue(span);
if (spanNavigation.Contains(NavigationString))
{
isIncludeSpanValid = true;
break;
}
}
}
if ((node.Method.Name == "Include" && (node.Arguments[0] as ConstantExpression).Value.ToString() == NavigationString)
|| isIncludeSpanValid)
{
// CREATE a query from current expression
var query = Provider.CreateQuery<T>(expression);
// APPLY the query condition
query = QueryCondition(query);
// CHANGE the query expression
expression = query.Expression;
}
else
{
expression = base.VisitMethodCall(node);
}
return expression;
}
}
EDIT:回答子问题
Include 和 IncludeSpan 之间的区别
据我了解
IncludeSpan:当原始查询尚未通过 LINQ 方法修改时出现。
Include:当原始查询已被 LINQ 方法修改时出现(您不再看到以前的表达式)
-- Expression: {value(System.Data.Entity.Core.Objects.ObjectQuery`1[Z.Test.EntityFramework.Plus.Association_Multi_OneToMany_Left]).MergeAs(AppendOnly).IncludeSpan(value(System.Data.Entity.Core.Objects.Span))}
var q = ctx.Association_Multi_OneToMany_Lefts.Include(x => x.Right1s).Include(x => x.Right2s);
-- Expression: {value(System.Data.Entity.Core.Objects.ObjectQuery`1[Z.Test.EntityFramework.Plus.Association_Multi_OneToMany_Left]).Include("Right2s")}
var q = ctx.Association_Multi_OneToMany_Lefts.Include(x => x.Right1s).Where(x => x.ColumnInt > 10).Include(x => x.Right2s);
如何包含和过滤相关实体
包含不允许您过滤相关实体。您可以在这篇文章中找到 2 个解决方案:EF。如何在模型中仅包含一些子结果? https://stackoverflow.com/questions/34963319/ef-how-to-include-only-some-sub-results-in-a-model/34971161#34971161
- 一种涉及使用投影
- 其中之一涉及使用我的库中的 EF+ Query IncludeFilter