Dav*_*man 24 c# linq entity-framework expression-trees
我想在执行之前重写LINQ表达式的某些部分.而且我在将重写器注入正确的位置时遇到了问题(实际上是这样).
查看实体框架源(在反射器中)它最终归结为IQueryProvider.Execute
EF中的哪一个通过ObjectContext
提供internal IQueryProvider Provider { get; }
属性与表达式耦合.
所以我创建了一个包装器类(实现IQueryProvider
),在调用Execute时执行Expression重写,然后将其传递给原始的Provider.
问题是,背后的领域Provider
是private ObjectQueryProvider _queryProvider;
.这ObjectQueryProvider
是一个内部密封类,这意味着不可能创建一个提供添加重写的子类.
因此,由于非常紧密耦合的ObjectContext,这种方法让我陷入了死胡同.
如何解决这个问题呢?我看错了方向吗?有没有办法让自己注意到这个ObjectQueryProvider
?
更新:虽然提供的解决方案在您使用存储库模式"包装"ObjectContext时都能正常工作,但是允许直接使用ObjectContext生成的子类的解决方案将更可取.因此保持与Dynamic Data脚手架兼容.
Dav*_*man 15
根据Arthur的回答,我创建了一个工作包装器.
提供的代码片段提供了使用您自己的QueryProvider和IQueryable根包装每个LINQ查询的方法.这意味着您必须控制初始查询的开始(因为您大部分时间都使用任何类型的模式).
这种方法的问题在于它不透明,更理想的情况是在构造函数级别的实体容器中注入一些东西.
我已经创建了一个可编译的实现,让它与实体框架一起工作,并添加了对ObjectQuery.Include方法的支持.可以从MSDN复制表达式访客类.
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
}
Run Code Online (Sandbox Code Playgroud)
存储库中的示例用法:
public IQueryable<User> List()
{
return new QueryTranslator<User>(entities.Users).Include("Department");
}
Run Code Online (Sandbox Code Playgroud)
Art*_*hur 11
我有你需要的源代码 - 但不知道如何附加文件.
这里有一些片段(片段!我必须调整这段代码,所以它可能无法编译):
IQueryable的:
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();
}
IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _provider.ExecuteEnumerable(this._expression).GetEnumerator();
}
public Type ElementType
{
get { return typeof(T); }
}
public Expression Expression
{
get { return _expression; }
}
public IQueryProvider Provider
{
get { return _provider; }
}
}
Run Code Online (Sandbox Code Playgroud)
IQueryProvider:
public class QueryTranslatorProvider<T> : ExpressionTreeTranslator, IQueryProvider
{
IQueryable _source;
public QueryTranslatorProvider(IQueryable source)
{
if (source == null) throw new ArgumentNullException("source");
_source = source;
}
#region IQueryProvider Members
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.FindElementTypes().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);
}
#endregion
#region Visits
protected override MethodCallExpression VisitMethodCall(MethodCallExpression m)
{
return m;
}
protected override Expression VisitUnary(UnaryExpression u)
{
return Expression.MakeUnary(u.NodeType, base.Visit(u.Operand), u.Type.ToImplementationType(), u.Method);
}
#endregion
}
Run Code Online (Sandbox Code Playgroud)
用法(警告:改编代码!可能无法编译):
private Dictionary<Type, object> _table = new Dictionary<Type, object>();
public override IQueryable<T> GetObjectQuery<T>()
{
if (!_table.ContainsKey(type))
{
_table[type] = new QueryTranslator<T>(
_ctx.CreateQuery<T>("[" + typeof(T).Name + "]"));
}
return (IQueryable<T>)_table[type];
}
Run Code Online (Sandbox Code Playgroud)
表达访客/翻译:
http://blogs.msdn.com/mattwar/archive/2007/07/31/linq-building-an-iqueryable-provider-part-ii.aspx
http://msdn.microsoft.com/en-us/library/bb882521.aspx
编辑:添加了FindElementTypes().希望所有方法现在都存在.
/// <summary>
/// Finds all implemented IEnumerables of the given Type
/// </summary>
public static IQueryable<Type> FindIEnumerables(this Type seqType)
{
if (seqType == null || seqType == typeof(object) || seqType == typeof(string))
return new Type[] { }.AsQueryable();
if (seqType.IsArray || seqType == typeof(IEnumerable))
return new Type[] { typeof(IEnumerable) }.AsQueryable();
if (seqType.IsGenericType && seqType.GetGenericArguments().Length == 1 && seqType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
{
return new Type[] { seqType, typeof(IEnumerable) }.AsQueryable();
}
var result = new List<Type>();
foreach (var iface in (seqType.GetInterfaces() ?? new Type[] { }))
{
result.AddRange(FindIEnumerables(iface));
}
return FindIEnumerables(seqType.BaseType).Union(result);
}
/// <summary>
/// Finds all element types provided by a specified sequence type.
/// "Element types" are T for IEnumerable<T> and object for IEnumerable.
/// </summary>
public static IQueryable<Type> FindElementTypes(this Type seqType)
{
return seqType.FindIEnumerables().Select(t => t.IsGenericType ? t.GetGenericArguments().Single() : typeof(object));
}
Run Code Online (Sandbox Code Playgroud)