c# - Entity Framework 如何管理映射查询结果到匿名类型?

标签 c# entity-framework entity-framework-4 linq-to-entities

考虑以下示例 LINQ to entity 查询

from history in entities.foreignuserhistory
select new { history.displayname, login=history.username, history.foreignuserid }

ToTraceString() 返回字符串如下:

SELECT "Extent1"."foreignuserid" AS "foreignuserid",
   "Extent1"."displayname"       AS "displayname",
   "Extent1"."username"          AS "username"
FROM "integration"."foreignuserhistory" AS "Extent1"

我的问题是列的顺序与查询的顺序不同,并且不采用示例中的 login 之类的别名。 Entity Framework 在哪里存储匿名类型的映射信息?

背景:我将使用 LINQ to entity 开发带有选择操作的插入以进行大规模操作。

更新: 除了未知的列到属性映射算法外,使用 select 插入并不难。可以使用元数据获取目标 ObjectSet 的表名和列名,构建 INSERT INTO tableName (column_name1, …) sql 语句字符串,然后附加一些 ObjectQuery.ToTraceString 选择语句。然后使用 ((EntityConnection)ObjectContext.Connection).StoreConnection 创建一个包含结果文本的 DbCommand 并从 ObjectQuery 填充命令的参数。所以问题是在插入和选择的记录中找到匹配的列顺序。

最佳答案

这是我的解决方案,一直到私有(private)和内部。它通过反射进入缓存的查询计划,该计划将在 ToTraceString 调用或查询执行后存在,以获取所谓的 _columnMap。列映射包含 ScalarColumnMap 对象,这些对象按照匿名对象的属性顺序排列,并使用 ColumnPos 属性指向相应的列位置。

using System;
using System.Data.Objects;
using System.Reflection;

static class EFQueryUtils
{
    public static int[] GetPropertyPositions(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.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.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.EntityClient.EntityCommandDefinition");

        // get private readonly IColumnMapGenerator EntityCommandDefinition._columnMapGenerator;
        // of actual type private sealed class
        //      System.Data.EntityClient.EntityCommandDefinition.ConstantColumnMapGenerator
        object columnMapGenerator = GetField(commandDefinition, "_columnMapGenerator");
        AssertNonNullAndOfType(columnMapGenerator, "System.Data.EntityClient.EntityCommandDefinition+ConstantColumnMapGenerator");

        // 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.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.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.Query.InternalTrees.ColumnMap[]");

        int n = columnMapProperties.Length;
        int[] propertyPositions = new int[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.Query.InternalTrees.ScalarColumnMap");

            //string colName = (string)GetProp(column, "Name");
            // can be used for more advanced bingings

            // get internal int ScalarColumnMap.ColumnPos;
            object columnPositionOfAProperty = GetProperty(column, "ColumnPos");
            AssertNonNullAndOfType(columnPositionOfAProperty, "System.Int32");

            propertyPositions[i] = (int)columnPositionOfAProperty;
        }
        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");
    }
}

我认为可以放宽一些断言来检查包含必要属性的基本类型而不是确切类型。

有没有不用反射(reflection)的解决方案?

关于c# - Entity Framework 如何管理映射查询结果到匿名类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7808607/

相关文章:

c# - 接口(interface)可以在 C# 中有静态变量吗

asp.net-mvc-3 - StructureMap HttpContextScoped 是必要的吗?

entity-framework-4 - Entity Framework 中的主/外键

c# - 我应该如何删除 DbSet 中的所有元素?

entity-framework - Entity Framework 4 和 SQL Compact 4 : How to generate database?

c# - Microsoft.Test.CommandLineParsing 这是哪个 dll

c# - 使用 UniqueIdentifier 列类型和 [DatabaseGenerate(DatabaseGenerateOption.Identity)] 作为模型的键在 EF Code First 中不起作用

c# - WPF DataGridRow IsSelected 触发器

c# - 如何使用 EF 根据第一个表中的记录列表从第二个表中获取多个记录

mysql - 如何显示实体和数据库表结构之间的差异