代码之家  ›  专栏  ›  技术社区  ›  Amir Popovich

尽可能靠近T-SQL翻译\执行修改实体框架的表达式树

  •  5
  • Amir Popovich  · 技术社区  · 8 年前

    我可以修改 IQueryable 使用 ExpressionVisitor 和其他习惯 Expressions .

    我的问题是使用实体框架(例如OData)的第三方框架,这些框架在内部方法中修改查询,并且在这样做之后,我很难重新修改查询。

    在流程结束时 IQueryable公司 表示表达式树的。实体框架知道如何将表达式树转换为T-SQL并执行它。

    我想修改一下 Expression\IQueryable 尽可能接近执行。

    我能做的最好的方法是什么?

    1 回复  |  直到 8 年前
        1
  •  8
  •   Titian Cernicova-Dragomir    8 年前

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

    实体框架允许我们在查询生成的不同方面指定不同类型的拦截器。每个拦截器都可以修改执行的查询。 拦截器类型包括:

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

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

    3. IDbConfigurationInterceptor DbConfiguration 已加载。

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

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

    IDBComandTreeInterceptor 提供了一种很好的获取和更改命令的方法。AST非常容易理解,实体框架已经为基于现有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());
    

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