bde*_*ere 4 c# entity-framework-core
我有一个ExpressionVisitor我添加到 EF Core 的IQueryable<T>. 除了 Include 方法外,一切正常。可能是因为他们强制您IQueryable<T>.Provider成为EntityQueryProvider.
每当我现在尝试包含时,它会导致多个查询,从而导致错误“在上一个操作完成之前在此上下文中启动了第二个操作。不保证任何实例成员都是线程安全的。”。
我如何连接我的,ExpressionVisitor以便它仍然可以与 EF Core 的 Include 功能一起使用?
我的问题与此类似,除了 EF Core 而不是 EF。
我ExpressionVisitor通过在 DbSet 上调用它来连接我的:
return new Translator<TEntity>(
_dbSet
.AsNoTracking());
Run Code Online (Sandbox Code Playgroud)
这是我的Translator课:
public class Translator<T> : IOrderedQueryable<T>
{
private readonly Expression _expression;
private readonly TranslatorProvider<T> _provider;
public Translator(IQueryable source)
{
_expression = Expression.Constant(this);
_provider = new TranslatorProvider<T>(source);
}
public Translator(IQueryable source, Expression expression)
{
if (expression == null)
{
throw new ArgumentNullException(nameof(expression));
}
_expression = expression;
_provider = new TranslatorProvider<T>(source);
}
public IEnumerator<T> GetEnumerator()
{
return ((IEnumerable<T>)_provider.ExecuteEnumerable(_expression)).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _provider.ExecuteEnumerable(_expression).GetEnumerator();
}
public Type ElementType => typeof(T);
public Expression Expression => _expression;
public IQueryProvider Provider => _provider;
}
Run Code Online (Sandbox Code Playgroud)
这是我的TranslatorProvider<T>课程(我已经去掉了不相关的访问方法来缩短帖子):
public class TranslatorProvider<T> : ExpressionVisitor, IQueryProvider
{
private readonly IQueryable _source;
public TranslatorProvider(IQueryable source)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
_source = source;
}
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
if (expression == null)
{
throw new ArgumentNullException(nameof(expression));
}
return new Translator<TElement>(_source, expression);
}
public IQueryable CreateQuery(Expression expression)
{
if (expression == null)
{
throw new ArgumentNullException(nameof(expression));
}
var elementType = expression.Type.GetGenericArguments().First();
var result = (IQueryable) Activator.CreateInstance(typeof(Translator<>).MakeGenericType(elementType),
_source, expression);
return result;
}
public TResult Execute<TResult>(Expression expression)
{
if (expression == null)
{
throw new ArgumentNullException(nameof(expression));
}
var result = (this as IQueryProvider).Execute(expression);
return (TResult) result;
}
public object Execute(Expression expression)
{
if (expression == null)
{
throw new ArgumentNullException(nameof(expression));
}
var translated = Visit(expression);
return _source.Provider.Execute(translated);
}
internal IEnumerable ExecuteEnumerable(Expression expression)
{
if (expression == null)
{
throw new ArgumentNullException(nameof(expression));
}
var translated = Visit(expression);
return _source.Provider.CreateQuery(translated);
}
protected override Expression VisitConstant(ConstantExpression node)
{
if (node.Type == typeof(Translator<T>))
{
return _source.Expression;
}
else
{
return base.VisitConstant(node);
}
}
}
Run Code Online (Sandbox Code Playgroud)
更新(EF Core 3.x):
内部查询管道基础结构已更改。新的查询表达式预处理扩展点是QueryTranslationPreprocessor类 - Process方法。插入它需要替换IQueryTranslationPreprocessorFactory。例如
using System.Linq.Expressions;
namespace Microsoft.EntityFrameworkCore.Query
{
public class CustomQueryTranslationPreprocessor : RelationalQueryTranslationPreprocessor
{
public CustomQueryTranslationPreprocessor(QueryTranslationPreprocessorDependencies dependencies, RelationalQueryTranslationPreprocessorDependencies relationalDependencies, QueryCompilationContext queryCompilationContext)
: base(dependencies, relationalDependencies, queryCompilationContext) { }
public override Expression Process(Expression query) => base.Process(Preprocess(query));
private Expression Preprocess(Expression query)
{
// query = new YourExpressionVisitor().Visit(query);
return query;
}
}
public class CustomQueryTranslationPreprocessorFactory : IQueryTranslationPreprocessorFactory
{
public CustomQueryTranslationPreprocessorFactory(QueryTranslationPreprocessorDependencies dependencies, RelationalQueryTranslationPreprocessorDependencies relationalDependencies)
{
Dependencies = dependencies;
RelationalDependencies = relationalDependencies;
}
protected QueryTranslationPreprocessorDependencies Dependencies { get; }
protected RelationalQueryTranslationPreprocessorDependencies RelationalDependencies;
public QueryTranslationPreprocessor Create(QueryCompilationContext queryCompilationContext)
=> new CustomQueryTranslationPreprocessor(Dependencies, RelationalDependencies, queryCompilationContext);
}
}
Run Code Online (Sandbox Code Playgroud)
和
optionsBuilder.ReplaceService<IQueryTranslationPreprocessorFactory, CustomQueryTranslationPreprocessorFactory>();
Run Code Online (Sandbox Code Playgroud)
原来的:
显然,自定义查询供应商不适合当前EF核心可查询的管道,因为几种方法(Include,AsNoTracking等)要求供应商要EntityQueryProvider。
在写作(EF核2.1.2)的时间,查询翻译过程中涉及多个服务- ,,等等。它们都是可替换的,但我看到最容易拦截的地方是service -方法。IAsyncQueryProviderIQueryCompilerIQueryModelGeneratorIQueryModelGeneratorParseQuery
因此,忘记自定义IQueryable/IQueryProvider实现,使用以下类并将您的表达式访问者插入Preprocess方法中:
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Query.Internal;
using Remotion.Linq;
using Remotion.Linq.Parsing.ExpressionVisitors.TreeEvaluation;
class CustomQueryModelGenerator : QueryModelGenerator
{
public CustomQueryModelGenerator(INodeTypeProviderFactory nodeTypeProviderFactory, IEvaluatableExpressionFilter evaluatableExpressionFilter, ICurrentDbContext currentDbContext)
: base(nodeTypeProviderFactory, evaluatableExpressionFilter, currentDbContext)
{ }
public override QueryModel ParseQuery(Expression query) => base.ParseQuery(Preprocess(query));
private Expression Preprocess(Expression query)
{
// return new YourExpressionVisitor().Visit(query);
return query;
}
}
Run Code Online (Sandbox Code Playgroud)
并替换派生上下文OnConfiguring覆盖中的相应 EF Core 服务:
optionsBuilder.ReplaceService<IQueryModelGenerator, CustomQueryModelGenerator>();
Run Code Online (Sandbox Code Playgroud)
缺点是这是使用 EF Core“内部”的东西,所以你应该继续监视未来更新中的变化。
| 归档时间: |
|
| 查看次数: |
1193 次 |
| 最近记录: |