2 votes

EF ToTraceString Ordre des colonnes de résultat de la génération SQL

Ma question est de savoir si je peux prédire ou choisir l'ordre exact des colonnes que la génération sql va générer. ToTraceString() retours.

J'utilise ToTraceString() pour un IQueryable afin d'obtenir la commande SQL résultante, puis d'insérer les résultats directement dans une table de base de données.

Donc, j'ai besoin que le SQL généré soit cohérent avec la structure de ma table...

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> où Row est un type ayant les mêmes propriétés que les colonnes de la table.

J'ai choisi de faire une insertion directe dans une table parce que je ne vois pas l'intérêt d'obtenir une énumération ToList() sur le serveur web, juste pour la renvoyer à SQL par une sorte d'insertion en masse (que EF ne supporte pas pour le moment...) Ma requête renvoie un nombre considérable de lignes et je ne veux pas utiliser de procédures stockées.

J'espère que je suis compréhensible...merci

0voto

ChaseMedallion Points 6064

La réponse acceptée à cette question contient deux liens qui décrivent comment déterminer l'ordre dans lequel les différentes propriétés du type d'entité apparaissent dans le SQL généré par ToTraceString(). Avec cette information, vous pouvez faire une analyse/restructuration simple du SQL brut pour remplacer les noms de colonnes bizarres utilisés par EF (par exemple C1, C2, etc.) par les noms de colonnes des propriétés. Ensuite, vous pouvez envelopper le SQL généré dans une sous-requête qui sélectionne les colonnes pertinentes dans l'ordre que vous voulez :

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

0voto

ocdi Points 189

J'ai eu ce problème, mais la solution proposée ici a nécessité une bonne dose de travail pour y parvenir. J'ai utilisé la partie de Comment Entity Framework gère-t-il le mappage du résultat de la requête au type anonyme ? pour obtenir l'ordre et retourner les noms, puis un simple parsing pour extraire les noms des champs.

J'ai créé une méthode d'extension qui rassemble tout :

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

C'est le code de l'autre lien, mis à jour pour les internes d'EF6 et retournant le nom dans l'ordre des colonnes plutôt que des index.

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X