EF ToTraceString Порядок столбцов результата генерации SQL - PullRequest
2 голосов
/ 17 февраля 2012

У меня вопрос: смогу ли я предсказать или выбрать точный порядок столбцов, возвращаемых генерацией SQL через ToTraceString().

Я использую 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<Row> где Row - это тип с такими же свойствами, что и столбцы таблицы

Я решил сделать прямую вставку в таблицу, потому что не вижу смысла получать перечисляемый ToList () на веб-сервере, просто отправлять его обратно в SQL через какую-то массовую вставку (которую EF не поддерживает; момент ...) Мой запрос возвращает значительное количество строк, и я не хочу использовать хранимые процедуры.

Надеюсь, у меня есть смысл ... спасибо

Ответы [ 2 ]

0 голосов
/ 31 октября 2013

У меня была эта проблема, но ответ здесь все еще требовал изрядного количества работы, чтобы начать работу.Я использовал часть Как Entity Framework управляет результатом запроса сопоставления анонимному типу? , чтобы получить заказ и вернуть имена, затем простой анализ для извлечения имен полей.

Я сделалметод расширения, который объединяет все воедино:

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");
}
0 голосов
/ 21 сентября 2012

Принятый ответ на этот вопрос содержит две ссылки, которые описывают, как выяснить, в каком порядке различные свойства типа сущности появляются в SQL, сгенерированном ToTraceString (). Используя эту информацию, вы можете выполнить простой анализ / реструктуризацию необработанного 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...