【问题标题】:EF ToTraceString SQL generation result columns orderEF ToTraceString SQL 生成结果列顺序
【发布时间】:2013-10-31 11:16:23
【问题描述】:

我的问题是我是否可以预测或选择通过ToTraceString() 返回的 sql 生成列的确切顺序。

我将ToTraceString() 用于 IQueryable 以获取结果 SQL 命令,然后将结果直接插入到数据库表中。

所以,我有点需要生成的 SQL 与我的表结构保持一致...

string insertQuery = string.Format("INSERT INTO {0} {1}", sqlTableName ((System.Data.Objects.ObjectQuery<TRow>)results).ToTraceString());
Context.ExecuteStoreCommand(string.Format("TRUNCATE TABLE {0}", sqlTableName));
Context.ExecuteStoreCommand(insertQuery);

results = IQueryable&lt;Row&gt; 其中 Row 是与表列具有相同属性的类型

我选择直接插入到表中,因为我认为在网络服务器上获取 ToList() 可枚举没有意义,只是通过某种批量插入将其发送回 SQL(EF 不支持;不支持此刻......)我的查询返回了大量的行,我不想使用存储过程。

希望我说得通……谢谢

【问题讨论】:

    标签: c# sql-server linq entity-framework


    【解决方案1】:

    我遇到了这个问题,但这里的答案仍然需要大量的工作才能开始。我使用How does Entity Framework manage mapping query result to anonymous type?的部分来获取订单并返回名称,然后进行简单的解析以提取字段名称。

    我做了一个扩展方法,把所有东西都放在一起:

    public static string ToWrappedString(this ObjectQuery query, out ObjectParameterCollection parameters)
    {
        var trace = query.ToTraceString();
        parameters = query.Parameters;
        var positions = query.GetPropertyPositions();
    
        // the query should be SELECT\n
        //  Column AS NNN
        //  FROM
        // so we regex this out
        var regex = new Regex("^SELECT(?<columns>.*?)FROM", RegexOptions.Multiline);
        var result = regex.Match(trace.Replace(Environment.NewLine, ""));
        var cols = result.Groups["columns"];
    
        // then we have the columns so split to get each
        const string As = " AS ";
        var colNames = cols.Value.Split(',').Select(a => a.Substring(a.IndexOf(As, StringComparison.InvariantCulture) + As.Length)).ToArray();
    
    
        var wrapped = "SELECT " + String.Join(Environment.NewLine + ", ", colNames.Select((a, i) => string.Format("{0}{1} [{2}]", a, As, positions[i]))) + " FROM (" + trace
                      + ") WrappedQuery ";
        return wrapped;
    }
    

    这是来自其他链接的代码,已更新为 EF6 内部,并按列顺序而不是索引返回名称。

    public static string[] GetPropertyPositions(this ObjectQuery query)
    {
        // get private ObjectQueryState ObjectQuery._state;
        // of actual type internal class
        //      System.Data.Objects.ELinq.ELinqQueryState
        object queryState = GetProperty(query, "QueryState");
        AssertNonNullAndOfType(queryState, "System.Data.Entity.Core.Objects.ELinq.ELinqQueryState");
    
        // get protected ObjectQueryExecutionPlan ObjectQueryState._cachedPlan;
        // of actual type internal sealed class
        //      System.Data.Objects.Internal.ObjectQueryExecutionPlan
        object plan = GetField(queryState, "_cachedPlan");
        AssertNonNullAndOfType(plan, "System.Data.Entity.Core.Objects.Internal.ObjectQueryExecutionPlan");
    
        // get internal readonly DbCommandDefinition ObjectQueryExecutionPlan.CommandDefinition;
        // of actual type internal sealed class
        //      System.Data.EntityClient.EntityCommandDefinition
        object commandDefinition = GetField(plan, "CommandDefinition");
        AssertNonNullAndOfType(commandDefinition, "System.Data.Entity.Core.EntityClient.Internal.EntityCommandDefinition");
    
        // get private readonly IColumnMapGenerator EntityCommandDefinition._columnMapGenerator;
        // of actual type private sealed class
        //      System.Data.EntityClient.EntityCommandDefinition.ConstantColumnMapGenerator
        var columnMapGeneratorArray = GetField(commandDefinition, "_columnMapGenerators") as object[];
        AssertNonNullAndOfType(columnMapGeneratorArray, "System.Data.Entity.Core.EntityClient.Internal.EntityCommandDefinition+IColumnMapGenerator[]");
    
        var columnMapGenerator = columnMapGeneratorArray[0];
    
        // get private readonly ColumnMap ConstantColumnMapGenerator._columnMap;
        // of actual type internal class
        //      System.Data.Query.InternalTrees.SimpleCollectionColumnMap
        object columnMap = GetField(columnMapGenerator, "_columnMap");
        AssertNonNullAndOfType(columnMap, "System.Data.Entity.Core.Query.InternalTrees.SimpleCollectionColumnMap");
    
        // get internal ColumnMap CollectionColumnMap.Element;
        // of actual type internal class
        //      System.Data.Query.InternalTrees.RecordColumnMap
        object columnMapElement = GetProperty(columnMap, "Element");
        AssertNonNullAndOfType(columnMapElement, "System.Data.Entity.Core.Query.InternalTrees.RecordColumnMap");
    
        // get internal ColumnMap[] StructuredColumnMap.Properties;
        // array of internal abstract class
        //      System.Data.Query.InternalTrees.ColumnMap
        Array columnMapProperties = GetProperty(columnMapElement, "Properties") as Array;
        AssertNonNullAndOfType(columnMapProperties, "System.Data.Entity.Core.Query.InternalTrees.ColumnMap[]");
    
        int n = columnMapProperties.Length;
        string[] propertyPositions = new string[n];
        for (int i = 0; i < n; ++i)
        {
            // get value at index i in array
            // of actual type internal class
            //      System.Data.Query.InternalTrees.ScalarColumnMap
            object column = columnMapProperties.GetValue(i);
            AssertNonNullAndOfType(column, "System.Data.Entity.Core.Query.InternalTrees.ScalarColumnMap");
    
            string colName = (string)GetProperty(column, "Name");
            // can be used for more advanced bingings
    
            // get internal int ScalarColumnMap.ColumnPos;
            object columnPositionOfAProperty = GetProperty(column, "ColumnPos");
            AssertNonNullAndOfType(columnPositionOfAProperty, "System.Int32");
    
            propertyPositions[(int)columnPositionOfAProperty] = colName;
        }
        return propertyPositions;
    }
    
    static object GetProperty(object obj, string propName)
    {
        PropertyInfo prop = obj.GetType().GetProperty(propName, BindingFlags.NonPublic | BindingFlags.Instance);
        if (prop == null) throw EFChangedException();
        return prop.GetValue(obj, new object[0]);
    }
    
    static object GetField(object obj, string fieldName)
    {
        FieldInfo field = obj.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
        if (field == null) throw EFChangedException();
        return field.GetValue(obj);
    }
    
    static void AssertNonNullAndOfType(object obj, string fullName)
    {
        if (obj == null) throw EFChangedException();
        string typeFullName = obj.GetType().FullName;
        if (typeFullName != fullName) throw EFChangedException();
    }
    
    static InvalidOperationException EFChangedException()
    {
        return new InvalidOperationException("Entity Framework internals has changed, please review and fix reflection code");
    }
    

    【讨论】:

      【解决方案2】:

      this question 的已接受答案包含两个链接,这些链接描述了如何确定实体类型的各种属性在 ToTraceString() 生成的 SQL 中出现的顺序。有了这些信息,您可以对原始 SQL 进行一些简单的解析/重构,以将 EF 使用的奇怪列名(例如 C1、C2 等)替换为属性列名。然后,您可以将生成的 SQL 包装在一个子查询中,该子查询按您想要的顺序选择相关列:

      SELECT prop1, prop2
      FROM
      (
          // the result of ToTraceString(), with EF's generated column names replaced by the property names of the query type
      ) x
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多