gpt4 book ai didi

c# - 使用 EF Core Include 连接 ExpressionVisitor

转载 作者:行者123 更新时间:2023-11-30 15:53:35 33 4
gpt4 key购买 nike

我有一个 ExpressionVisitor我将其添加到 EF Core 的 IQueryable<T> .除 Include 方法外,一切正常。可能是因为他们强制执行您的 IQueryable<T>.Provider成为EntityQueryProvider .

每当我现在尝试包含它时,它都会导致多个查询,进而导致错误“在上一个操作完成之前在此上下文上启动了第二个操作。不保证任何实例成员都是线程安全的。”。

如何连接我的 ExpressionVisitor所以它仍然适用于 EF Core 的包含功能?

我的问题类似于 this one除了 EF Core 而不是 EF。

我连接了我的 ExpressionVisitor通过在 DbSet 上调用它:

        return new Translator<TEntity>(
_dbSet
.AsNoTracking());

这是我的 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;
}

这是我的 TranslatorProvider<T>类(我已经删除了不相关的 Visit 方法来缩短帖子):

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);
}
}
}

最佳答案

更新(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);
}
}

optionsBuilder.ReplaceService<IQueryTranslationPreprocessorFactory, CustomQueryTranslationPreprocessorFactory>();

原文:

显然,自定义查询提供程序不适合当前的 EF Core 可查询管道,因为几种方法(IncludeAsNoTracking 等)要求提供程序是 EntityQueryProvider.

在撰写本文时(EF Core 2.1.2),查询翻译过程涉及多个服务 - IAsyncQueryProviderIQueryCompilerIQueryModelGenerator和更多。它们都是可替换的,但我看到最容易拦截的地方是 IQueryModelGenerator 服务 - ParseQuery 方法。

因此,忘记自定义 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;
}
}

并替换派生上下文中相应的 EF Core 服务 OnConfiguring 覆盖:

optionsBuilder.ReplaceService<IQueryModelGenerator, CustomQueryModelGenerator>();

缺点是这是使用 EF Core“内部”的东西,所以你应该继续监视 future 更新中的变化。

关于c# - 使用 EF Core Include 连接 ExpressionVisitor,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52056354/

33 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com