我想在执行之前重写 LINQ 表达式的某些部分。我在将重写器注入正确的位置时遇到问题(实际上根本没有)。
查看实体框架源代码(在反射器中),它最终归结为IQueryProvider.Execute
在 EF 中,它通过以下方式耦合到表达式ObjectContext
提供internal IQueryProvider Provider { get; }
财产。
所以我创建了一个包装类(实现IQueryProvider
) 在调用 Execute 时进行表达式重写,然后将其传递给原始提供程序。
问题是,后面的字段Provider
is private ObjectQueryProvider _queryProvider;
. This ObjectQueryProvider
is an 内部密封等级,这意味着不可能创建提供添加重写的子类。
由于 ObjectContext 耦合非常紧密,这种方法让我陷入了死胡同。
如何解决这个问题呢?我看错方向了吗?有没有办法让我自己注射到这个ObjectQueryProvider
?
Update:虽然当您使用存储库模式“包装”ObjectContext 时提供的解决方案都可以工作,但允许直接使用从 ObjectContext 生成的子类的解决方案将是更好的选择。特此保持与动态数据脚手架的兼容性。
根据亚瑟的回答,我创建了一个工作包装器。
提供的代码片段提供了一种使用您自己的 QueryProvider 和 IQueryable 根包装每个 LINQ 查询的方法。这意味着您必须控制初始查询的启动(因为您大部分时间都会使用任何类型的模式)。
这种方法的问题是它不透明,更理想的情况是在构造函数级别向实体容器中注入一些东西。
我创建了一个可编译的实现,让它与实体框架一起使用,并添加了对 ObjectQuery.Include 方法的支持。表达式访问者类可以从复制MSDN http://msdn.microsoft.com/en-us/library/bb882521.aspx.
public class QueryTranslator<T> : IOrderedQueryable<T>
{
private Expression expression = null;
private QueryTranslatorProvider<T> provider = null;
public QueryTranslator(IQueryable source)
{
expression = Expression.Constant(this);
provider = new QueryTranslatorProvider<T>(source);
}
public QueryTranslator(IQueryable source, Expression e)
{
if (e == null) throw new ArgumentNullException("e");
expression = e;
provider = new QueryTranslatorProvider<T>(source);
}
public IEnumerator<T> GetEnumerator()
{
return ((IEnumerable<T>)provider.ExecuteEnumerable(this.expression)).GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return provider.ExecuteEnumerable(this.expression).GetEnumerator();
}
public QueryTranslator<T> Include(String path)
{
ObjectQuery<T> possibleObjectQuery = provider.source as ObjectQuery<T>;
if (possibleObjectQuery != null)
{
return new QueryTranslator<T>(possibleObjectQuery.Include(path));
}
else
{
throw new InvalidOperationException("The Include should only happen at the beginning of a LINQ expression");
}
}
public Type ElementType
{
get { return typeof(T); }
}
public Expression Expression
{
get { return expression; }
}
public IQueryProvider Provider
{
get { return provider; }
}
}
public class QueryTranslatorProvider<T> : ExpressionVisitor, IQueryProvider
{
internal IQueryable source;
public QueryTranslatorProvider(IQueryable source)
{
if (source == null) throw new ArgumentNullException("source");
this.source = source;
}
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
if (expression == null) throw new ArgumentNullException("expression");
return new QueryTranslator<TElement>(source, expression) as IQueryable<TElement>;
}
public IQueryable CreateQuery(Expression expression)
{
if (expression == null) throw new ArgumentNullException("expression");
Type elementType = expression.Type.GetGenericArguments().First();
IQueryable result = (IQueryable)Activator.CreateInstance(typeof(QueryTranslator<>).MakeGenericType(elementType),
new object[] { source, expression });
return result;
}
public TResult Execute<TResult>(Expression expression)
{
if (expression == null) throw new ArgumentNullException("expression");
object result = (this as IQueryProvider).Execute(expression);
return (TResult)result;
}
public object Execute(Expression expression)
{
if (expression == null) throw new ArgumentNullException("expression");
Expression translated = this.Visit(expression);
return source.Provider.Execute(translated);
}
internal IEnumerable ExecuteEnumerable(Expression expression)
{
if (expression == null) throw new ArgumentNullException("expression");
Expression translated = this.Visit(expression);
return source.Provider.CreateQuery(translated);
}
#region Visitors
protected override Expression VisitConstant(ConstantExpression c)
{
// fix up the Expression tree to work with EF again
if (c.Type == typeof(QueryTranslator<T>))
{
return source.Expression;
}
else
{
return base.VisitConstant(c);
}
}
#endregion
}
您的存储库中的示例用法:
public IQueryable<User> List()
{
return new QueryTranslator<User>(entities.Users).Include("Department");
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)