【问题标题】:Entity Framework - IQueryable to DataTable实体框架 - IQueryable 到 DataTable
【发布时间】:2012-08-18 12:04:42
【问题描述】:

我正在尝试将 IQueryable 对象转换为 DataTable。这是我想转换为DataTable 的查询示例:

var query = DbContext.SomeObjectSet.Select(x => new { PropertyName1 = x.ColumnName1, PropertyName2 = x.ColumnName2 });

请注意Select 方法中创建的匿名对象和属性名称:

new { PropertyName1 = x.ColumnName1, PropertyName2 = x.ColumnName2 }

在谷歌搜索这个问题后,我发现了以下代码,它将IQueryable 对象转换为DataTable:

public static DataTable EntityToDatatable(this IQueryable result)
{
    ObjectQuery query = (result as ObjectQuery);

    ObjectContext context = query.Context;

    EntityConnection entityCon = (context.Connection as EntityConnection);

    using (SqlConnection sqlCon = new SqlConnection(entityCon.StoreConnection.ConnectionString))
    {
        using (SqlCommand cmd = new SqlCommand(query.ToTraceString(), sqlCon))
        {
            foreach (var param in query.Parameters)
            {
                cmd.Parameters.AddWithValue(param.Name, param.Value);

            }

            using (SqlDataAdapter dataAdapter = new SqlDataAdapter(cmd))
            {
                using (DataTable dataTable = new DataTable())
                {
                    dataAdapter.Fill(dataTable);

                    return dataTable;

                }

            }

        }

    }

}

上面“有效”的代码和来自ToTraceString()方法的SQL语句如下:

SELECT [Extent1].[ColumnName1] AS [ColumnName1], [Extent1].[ColumnName2] AS [ColumnName2] FROM [dbo].[TableName] AS [Extent1]

问题: SQL 语句的列名(即columnName1 和columnName2)与对象的属性名称(即PropertyName1 和PropertyName2)不对应) 如果在query 上调用ToList() 或AsEnumerable() 方法,这将实现。如果 SQL 语句列的顺序与匿名对象属性的顺序相同,这不会那么糟糕……但是,并非总是如此。某处(我猜在 IQueryable 对象内部)SQL 语句列名和生成的匿名对象属性名之间必须存在映射。

有谁知道如何获得这个映射?

【问题讨论】:

  • 为什么要将结果存储在DataTable?
  • @Eranga 好问题!好吧,对于大多数意图和目的,将 IQueryable 对象转换为 DataTable 是没有意义的。但是,我创建了一个透视数据表的透视类。我已经创建了一个适用于 IEnumerables 的数据透视类,但它在很大程度上依赖于反射。我认为使用数据表可以获得更好的性能。

标签: c#-4.0 entity-framework-4 datatable linq-to-entities iqueryable


【解决方案1】:

我已经设法找到解决问题的方法:

首先,您需要以下代码(来自How does Entity Framework manage mapping query result to anonymous type?),它将我的匿名对象属性的位置映射到 SQL 语句列位置:

public static Int32[] 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.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[]");

    Int32 n = columnMapProperties.Length;

    Int32[] propertyPositions = new Int32[n];

    for (Int32 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");

}

那我可以修改EntityToDatatable方法如下:

public static DataTable EntityToDatatable(this IQueryable query)
{
    SqlConnection sqlConnection = null;
    SqlCommand sqlCommand = null;
    SqlDataAdapter sqlDataAdapter = null;
    DataTable dataTable = null;

    try
    {
        ObjectQuery objectQuery = (query as ObjectQuery);

        ObjectContext objectContext = objectQuery.Context;

        EntityConnection entityConnection = (objectContext.Connection as EntityConnection);

        sqlConnection = new SqlConnection(entityConnection.StoreConnection.ConnectionString);

        sqlCommand = new SqlCommand(objectQuery.ToTraceString(), sqlConnection);

        foreach (var parameter in objectQuery.Parameters)
        {
            sqlCommand.Parameters.AddWithValue(parameter.Name, parameter.Value);

        }

        sqlDataAdapter = new SqlDataAdapter(sqlCommand);

        dataTable = new DataTable();

        sqlDataAdapter.Fill(dataTable);

        // Get the mapping between the object property position and 
        // the SQL statment column position.
        Int32[] propertyPositions = objectQuery.GetPropertyPositions();

        // Create a column name to column position (ordinal) lookup.
        Dictionary<String, Int32> mapColumnNameToColumnPosition = new Dictionary<string, int>();

        // Populate the lookup.
        for (Int32 i = 0; i < propertyPositions.Length; ++i)
        {
            mapColumnNameToColumnPosition.Add(dataTable.Columns[propertyPositions[i]].ColumnName, i);

        }

        // Get the object's property information.
        PropertyInfo[] pi = query.GetType().GetGenericArguments()[0].GetProperties();

        // Iterate through the lookup and change the position of the datatable columns.
        // The order of the datatable columns will now correspond to the order of the object
        // properties.
        foreach (var map in mapColumnNameToColumnPosition)
        {
            // Change the column position.
            dataTable.Columns[map.Key].SetOrdinal(map.Value);

            // Change the column name.
            dataTable.Columns[map.Key].ColumnName = pi[map.Value].Name;

        }

        return dataTable;

    }
    catch (Exception ex)
    {
        // Something went wrong and we're going to raise an exception...we
        // might as well dispose of the datatable if it exists because it's
        // not going to be used.
        if (dataTable != null) dataTable.Dispose();

        throw new Exception("IQueryable to DataTable conversion error.", ex);

    }
    finally
    {
        // Do some cleanup on objects that are no longer needed.

        if (sqlDataAdapter != null) sqlDataAdapter.Dispose();

        if (sqlCommand != null) sqlCommand.Dispose();

        if (sqlConnection != null) sqlConnection.Dispose();

    }

}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-05-06
    • 1970-01-01
    • 2014-04-22
    • 1970-01-01
    • 1970-01-01
    • 2021-05-15
    • 2017-03-12
    • 1970-01-01
    相关资源
    最近更新 更多