c# - 修改 Entity Framework 的表达式树,尽可能接近 T-SQL translation\execution

标签 c# entity-framework expression odata iqueryable

我已经能够使用 ExpressionVisitor 和其他自定义 Expressions 修改 IQueryable 表达式。

我的问题是使用 Entity Framework 的第三方框架(例如 OData)在内部方法中修改查询,并且在修改后我很难重新修改查询的地方。

在流程的最后,有一个代表表达式树的IQueryable。 Entity Framework 知道如何将该表达式树翻译成 T-SQL 并执行它。

我希望修改 Expression\IQueryable 尽可能接近执行。

最好的方法是什么?

最佳答案

您可以使用 Entity Framework 拦截。这允许您在执行之前拦截所有查询(包括导航属性查询)

Entity Framework 允许我们在查询生成的不同方面指定不同类型的拦截器。每个拦截器都可以修改执行的查询。 拦截器类型有:

  1. IDbCommandInterceptor 执行查询时将调用此拦截器的方法。查询将已转换为 SQL 并设置参数。

  2. IDbCommandTreeInterceptor 创建命令树时将调用此拦截器的方法。命令树是命令的 AST 表示。生成了两个命令树,一个用概念模型(DataSpace.CSpace)表示,这个命令树将更接近 LINQ 查询,另一个用存储模型(DataSpace.SSpace)

  3. IDbConfigurationInterceptor 此拦截器的方法在加载 DbConfiguration 时调用。

  4. IDbConnectionInterceptor 当建立连接和发生事务时调用此拦截器的方法。

  5. IDbTransactionInterceptor 当提交或回滚事务时调用此拦截器的方法。

IDbCommandTreeInterceptor 提供了一种获取命令并更改它的好方法。 AST 非常易于理解, Entity Framework 已经提供了基于现有命令 AST 创建新命令 AST 的基础设施(命令 AST 是一个不可变结构,因此我们不能只更改现有命令)。

使用示例:

class CustomExpressionVisitor : DefaultExpressionVisitor
{
    // Override method to mutate the query 
}
class TestInterceptor : IDbCommandTreeInterceptor
{
    public void TreeCreated(DbCommandTreeInterceptionContext interceptionContext)
    {
        if (interceptionContext.Result.DataSpace == DataSpace.CSpace)
        {
            // We only process query command trees 
            // (there can be others such as insert, update or delete
            var queryCommand = interceptionContext.Result as DbQueryCommandTree;
            if (queryCommand != null)
            {
                // A bit of logging to see the original tree
                Console.WriteLine(queryCommand.DataSpace);
                Console.WriteLine(queryCommand);

                // We call the accept method on the command expression with our new visitor. 
                // This method will return our new command expression with the changes the 
                // visitor has made to it
                var newQuery = queryCommand.Query.Accept(new CustomExpressionVisitor());
                // We create a new command with our new command expression and tell 
                // EF to use it as the result of the query
                interceptionContext.Result = new DbQueryCommandTree
                (
                     queryCommand.MetadataWorkspace,
                     queryCommand.DataSpace,
                     newQuery
                 );
                // A bit of logging to see the new command tree
                Console.WriteLine(interceptionContext.Result);
            }

        }

    }
}

// In code before using any EF context.
// Interceptors are registered globally.
DbInterception.Add(new TestInterceptor());

注意:查询计划已缓存,因此在第一次遇到查询并缓存时不会调用拦截(您指定的结果将被缓存,而不是原始结果)。因此,这可以安全地用于不依赖于上下文的更改(例如:用户、请求、语言)。

关于c# - 修改 Entity Framework 的表达式树,尽可能接近 T-SQL translation\execution,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48378418/

相关文章:

c# - 在使用 SQL 查询的单元测试中模拟 IDocumentQuery

c# - Java 增强的 for 循环 VS .NET foreach 循环

c# - .Net (C#) 中 ISNULL() 的等效方法是什么?

c# - 在没有 key : Unable to track an instance of type 'MyEntity' because it does not have a primary key 的情况下初始化 DbSet 时出错

sql - Entity Framework : How to select specific fields from a singular Navigation Property?

Java 表达式的非法开始

latex - 将 LaTeX 表达式转换为中缀表达式

c# - 弱参照理解

c# - 为什么 Entity Framework 在删除时不重新创建我的 localdb?

c# - 如何获取 Linq 表达式的值