13

我正在使用 Entity Framework 5(使用 Code First 方法)从带有参数的遗留存储过程中填充我的一个类,这工作正常(详细信息如下)。我的问题是我想将列的名称映射到具有不同名称的属性(我不喜欢来自 Erp 的名称)。我尝试使用配置类(就像我映射到视图或表时所做的那样)来为具有不同名称的属性指定列名,这是我的结果:

  • 如果我不使用配置类(我没有在 DbContext 的 OnModelCreating 方法中添加它),那么 EF 可以工作,但只加载与列名称完全匹配的属性(这就是我所期望的案子); 其他属性为空;
  • 如果我使用配置类(将其添加到 DbContext 的 OnModelCreating 方法中的 modelBuilder 中),则 EF 会引发异常,指出“数据读取器与指定的 '...Item' 不兼容。类型的成员,' Description',在数据读取器中没有同名的对应列”,这对我来说听起来很奇怪,因为在配置中我指定属性 Description 映射到列 ItemDescription。

为什么配置会影响我的结果,但其规范不用于映射列?是否有另一种方法可以使用 SqlQuery 指定此映射?

以下是详细信息:

我的 POCO 课程:

public class Item
    {
        public String Id { get; set; }
        public String Description { get; set; }
    }

配置类:

public class ItemConfiguration : EntityTypeConfiguration<Item>
    {
        public ItemConfiguration()
        {
            HasKey(x => new { x.Id });
            Property(x => x.Id).HasColumnName("Code");
            Property(x => x.Description).HasColumnName("ItemDescription");
        }
    }

存储过程返回带有“代码”和“项目描述”列的数据;我这样称呼它:

var par = new SqlParameter();
par.ParameterName = "@my_par";
par.Direction = ParameterDirection.Input;
par.SqlDbType = SqlDbType.VarChar;
par.Size = 20;
par.Value = ...;

var data = _context.Database.SqlQuery<Item>("exec spItem @my_par", par);

并以此将配置添加到上下文中:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
      modelBuilder.Configurations.Add(new ItemConfiguration());
}

谢谢!

4

4 回答 4

11

我在这里找到:

http://entityframework.codeplex.com/workitem/233?PendingVoteId=233

“SqlQuery 方法旨在不考虑任何映射......”。

他们还说:“我们同意让 SqlQuery 尊重 Column 属性的选项很有用,因此我们将这个问题保持开放并将其放在我们的积压工作中以供将来考虑。”,所以,如果你有同样的问题,请投票:-)

于 2013-02-07T10:56:36.733 回答
3

同时,您可以使用此方法。很少有测试(因为它适用于我的课程),但如果需要修复也不难......它需要一个上下文(检索映射的自定义类型)并且它需要一个不同的连接来同时在其上运行数据读取器。

用法:
列出学生 = Mapper.Map(context, (new SchoolContext()).Database.Connection, "Select * from Students");

public static class Mapper
{
    /// <summary>
    /// Maps the result of a query into entities.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="context">The context.</param>
    /// <param name="queryConnection">The connection to run the query. Must be different from the one on the context.</param>
    /// <param name="sqlQuery">The SQL query.</param>
    /// <returns>An entity list</returns>
    /// <exception cref="System.ArgumentNullException">
    /// context
    /// or
    /// queryConnection
    /// or
    /// sqlQuery
    /// </exception>
    public static List<T> Map<T>(DbContext context, DbConnection queryConnection, string sqlQuery) where T:new()
    {
        if (context == null) 
            throw new ArgumentNullException("context");
        if (queryConnection == null)
            throw new ArgumentNullException("queryConnection");
        if (sqlQuery == null) 
            throw new ArgumentNullException("sqlQuery");

        var connectionState = queryConnection.State;

        if (connectionState != ConnectionState.Open)
            queryConnection.Open();

        DbCommand command = queryConnection.CreateCommand();
        command.CommandText = sqlQuery;
        DbDataReader reader = command.ExecuteReader();

        List<T> entities = new List<T>();

        while (reader.Read())
        {
            entities.Add(InternalMap<T>(context, reader));
        }

        if (connectionState != ConnectionState.Open)
            queryConnection.Close();

        return entities;

    }

    private static T InternalMap<T>(DbContext context, DbDataReader reader) where T: new()
    {

        T entityObject = new T();

        InternalMapEntity(context, reader, entityObject);

        return entityObject;
    }

    private static void InternalMapEntity(DbContext context, DbDataReader reader, object entityObject)
    {

        ObjectContext objectContext = ((IObjectContextAdapter)context).ObjectContext;
        var metadataWorkspace = ((EntityConnection)objectContext.Connection).GetMetadataWorkspace();

        IEnumerable<EntitySetMapping> entitySetMappingCollection = metadataWorkspace.GetItems<EntityContainerMapping>(DataSpace.CSSpace).Single().EntitySetMappings;
        IEnumerable<AssociationSetMapping> associationSetMappingCollection = metadataWorkspace.GetItems<EntityContainerMapping>(DataSpace.CSSpace).Single().AssociationSetMappings;

        var entitySetMappings = entitySetMappingCollection.First(o => o.EntityTypeMappings.Select(e => e.EntityType.Name).Contains(entityObject.GetType().Name));

        var entityTypeMapping = entitySetMappings.EntityTypeMappings[0];
        string tableName = entityTypeMapping.EntitySetMapping.EntitySet.Name;
        Console.WriteLine(tableName);

        MappingFragment mappingFragment = entityTypeMapping.Fragments[0];

        foreach (PropertyMapping propertyMapping in mappingFragment.PropertyMappings)
        {
            object value = Convert.ChangeType(reader[((ScalarPropertyMapping) propertyMapping).Column.Name], propertyMapping.Property.PrimitiveType.ClrEquivalentType);
            entityObject.GetType().GetProperty(propertyMapping.Property.Name).SetValue(entityObject, value, null);
            Console.WriteLine("{0} {1} {2}", propertyMapping.Property.Name, ((ScalarPropertyMapping)propertyMapping).Column, value);
        }

        foreach (var navigationProperty in entityTypeMapping.EntityType.NavigationProperties)
        {
            PropertyInfo propertyInfo = entityObject.GetType().GetProperty(navigationProperty.Name);

            AssociationSetMapping associationSetMapping = associationSetMappingCollection.First(a => a.AssociationSet.ElementType.FullName == navigationProperty.RelationshipType.FullName);

            // associationSetMapping.AssociationTypeMapping.MappingFragment.PropertyMappings contains two elements one for direct and one for inverse relationship
            EndPropertyMapping propertyMappings = associationSetMapping.AssociationTypeMapping.MappingFragment.PropertyMappings.Cast<EndPropertyMapping>().First(p => p.AssociationEnd.Name.EndsWith("_Target"));

            object[] key = propertyMappings.PropertyMappings.Select(c => reader[c.Column.Name]).ToArray();
            object value = context.Set(propertyInfo.PropertyType).Find(key);
            propertyInfo.SetValue(entityObject, value, null);
        }

    }
}
于 2015-06-11T10:07:14.077 回答
0

我刚刚编写了下面的扩展方法,将 sql 查询转换为名为 sql 的属性,然后查询数据。

希望有用

public static class DbSetExtensions
    {
        public static DbSqlQuery<TEntity> SqlColumnQuery<TEntity>(this DbSet<TEntity> dbSet, string sqlQuery)
            where TEntity : class
        {
            var context = GetContext(dbSet);
            return dbSet.SqlQuery(MapQueryToColumns(sqlQuery, context, typeof(TEntity)));
        }

        public static DbContext GetContext<TEntity>(this DbSet<TEntity> dbSet)
            where TEntity : class
        {
            object internalSet = dbSet
                .GetType()
                .GetField("_internalSet", BindingFlags.NonPublic | BindingFlags.Instance)
                .GetValue(dbSet);
            object internalContext = internalSet
                .GetType()
                .BaseType
                .GetField("_internalContext", BindingFlags.NonPublic | BindingFlags.Instance)
                .GetValue(internalSet);
            return (DbContext)internalContext
                .GetType()
                .GetProperty("Owner", BindingFlags.Instance | BindingFlags.Public)
                .GetValue(internalContext, null);
        }



        private static string MapQueryToColumns(string sqlQuery , DbContext context, Type entityType)
        {
            ObjectContext objectContext = ((IObjectContextAdapter)context).ObjectContext;
            var metadataWorkspace = ((EntityConnection)objectContext.Connection).GetMetadataWorkspace();

            IEnumerable<EntitySetMapping> entitySetMappingCollection = metadataWorkspace.GetItems<EntityContainerMapping>(DataSpace.CSSpace).Single().EntitySetMappings;
            //IEnumerable<AssociationSetMapping> associationSetMappingCollection = metadataWorkspace.GetItems<EntityContainerMapping>(DataSpace.CSSpace).Single().AssociationSetMappings;

            var entitySetMappings = entitySetMappingCollection.First(o => o.EntityTypeMappings.Select(e => e.EntityType.Name).Contains(entityType.Name));

            var entityTypeMapping = entitySetMappings.EntityTypeMappings[0];
            string tableName = entityTypeMapping.EntitySetMapping.EntitySet.Name;


            MappingFragment mappingFragment = entityTypeMapping.Fragments[0];

            List<string> propertyMappings = new List<string>();
            foreach (PropertyMapping propertyMapping in mappingFragment.PropertyMappings)
            {
                propertyMappings.Add(string.Format("{0} {1}", ((ScalarPropertyMapping)propertyMapping).Column.Name, propertyMapping.Property.Name));
            }
            var joinFields = string.Join(",",propertyMappings.ToArray());



            return string.Format("SELECT {0} FROM ({1})", joinFields, sqlQuery);
        }
    }
于 2019-06-30T11:24:02.537 回答
0

经过几年使用 bubi 的方法并实现了一些代码,我决定在这里发布我们的改进。请注意,我不会发布对其他命名空间的引用。只需根据您的需要进行调整。

无论如何,我希望它可以帮助某人。

使用系统;
使用 System.Collections.Generic;
使用 System.Collections.Immutable;
使用 System.Data;
使用 System.Data.Common;
使用 System.Data.Entity.Core.EntityClient;
使用 System.Data.Entity.Core.Mapping;
使用 System.Data.Entity.Core.Metadata.Edm;
使用 System.Data.Entity.Infrastructure;
使用 System.Diagnostics;
使用 System.Linq;
使用 System.Linq.Expressions;


    公共抽象部分类 BaseService
        其中 TEntity : EntityDefault
    {
        私有常量 int MAX_ITEMS_PER_PREDICATE = 500;

        ///
        /// Lista imutável Competitiono todos os predicates, por tipo da entidade, a serem buscados no banco de dados。
        ///
        私有 ImmutableDictionary> 谓词 { 获取;放; }

        私有 ImmutableDictionary PredicatesCount { get; 放; }

        私有 ImmutableDictionary> LoadedPredicates { get; 放; }

        ///
        /// Lista imutável 竞争作为 entidades,que são propriedades de navegação,já buscadas no banco de dados。
        ///
        私有 ImmutableList NavigationEntities { 获取;放; }

        ///
        /// Lista imutável opinion todas as propriedades de navegação
        ///
        私有 ImmutableList NavigationProperties { get; 放; }

        ///
        /// 将查询结果映射到实体中。
        ///
        ///
        /// SQL 查询。
        /// 传递给过程的参数列表
        ///
        /// 当查询为空或为空时,它可能返回空。
        /// 一个实体列表
        ///
        /// 语境
        /// 或者
        /// 查询连接
        /// 或者
        /// sql查询
        ///
        公共列表 SqlQuery(字符串查询,字典参数,参数 KeyValuePair[] 选项)其中 T:EntityDefault
        {
            DbConnection queryConnection = null;

            尝试
            {
                InitOrResetSqlQueryVariables();

                如果(查询.HasntValue())
                {
                    抛出新的 ArgumentNullException(nameof(query));
                }

                queryConnection = Db.Database.Connection;

                var connectionState = queryConnection.State;

                如果(连接状态!=连接状态。打开)
                {
                    查询连接.Open();
                }

                var command = queryConnection.CreateCommand();
                command.CommandType = CommandType.StoredProcedure;
                command.CommandText = 查询;

                如果(参数!= null)
                {
                    command.AddParameters(参数);
                }

                var reader = command.ExecuteReader();

                var 实体 = 新列表();

                而(读者。读())
                {
                    entity.Add(MapEntity(reader));
                }

                LoadNavigationProperties(实体,选项);

                返回实体;
            }
            最后
            {
                InitOrResetSqlQueryVariables();

                if (Db.BaseDb.AutoCloseConnection && queryConnection != null)
                {
                    if (queryConnection.State != ConnectionState.Closed)
                    {
                        查询连接.关闭();
                    }

                    queryConnection.Dispose();
                }
            }
        }

        public List SqlQuery(string query, List parameters, params KeyValuePair[] options) where T : EntityDefault
        {
            DbConnection queryConnection = null;

            尝试
            {
                InitOrResetSqlQueryVariables();

                如果(查询.HasntValue())
                {
                    抛出新的 ArgumentNullException(nameof(query));
                }

                queryConnection = Db.Database.Connection;

                var connectionState = queryConnection.State;

                如果(连接状态!=连接状态。打开)
                {
                    查询连接.Open();
                }

                var command = queryConnection.CreateCommand();
                command.CommandType = CommandType.StoredProcedure;
                command.CommandText = 查询;

                如果(参数!= null)
                {
                    command.Parameters.AddRange(parameters.ToArray());
                }

                var reader = command.ExecuteReader();

                var 实体 = 新列表();

                而(读者。读())
                {
                    entity.Add(MapEntity(reader));
                }

                LoadNavigationProperties(实体,选项);

                返回实体;
            }
            最后
            {
                InitOrResetSqlQueryVariables();

                if (Db.BaseDb.AutoCloseConnection && queryConnection != null)
                {
                    if (queryConnection.State != ConnectionState.Closed)
                    {
                        查询连接.关闭();
                    }

                    queryConnection.Dispose();
                }
            }
        }

        私人 T MapEntity(IDataRecord 阅读器)
        {
            var entityObject = Activator.CreateInstance();

            MapEntity(reader, entityObject);

            返回实体对象;
        }

        private void MapEntity(IDataRecord reader, object entityObject)
        {
            var objectContext = ((IObjectContextAdapter)Db).ObjectContext;
            var metadataWorkspace = ((EntityConnection)objectContext.Connection).GetMetadataWorkspace();

            var entitySetMappingCollection =
                metadataWorkspace.GetItems(DataSpace.CSSpace).Single().EntitySetMappings;

            var associationSetMappingCollection =
                metadataWorkspace.GetItems(DataSpace.CSSpace)
                    。单身的()
                    .AssociationSetMappings.ToList();

            var entitySetMappings =
                entitySetMappingCollection.First(
                    o => o.EntityTypeMappings.Select(e => e.EntityType.Name).Contains(entityObject.GetType().Name));

            var entityTypeMapping = entitySetMappings.EntityTypeMappings[0];
            var tableName = entityTypeMapping.EntitySetMapping.EntitySet.Name;
            Debug.WriteLine(tableName);

            var mappingFragment = entityTypeMapping.Fragments[0];

            // 映射实体本身的属性
            foreach(mappingFragment.PropertyMappings 中的 var propertyMapping)
            {
                var valueBeforCasting = reader[((ScalarPropertyMapping)propertyMapping).Column.Name];

                var value = valueBeforCasting 是 DBNull
                    ? 空值
                    : propertyMapping.Property.IsEnumType
                        ? Convert.ChangeType(valueBeforCasting,
                            类型(int))
                        : Convert.ChangeType(valueBeforCasting,
                            propertyMapping.Property.PrimitiveType.ClrEquivalentType);

                entityObject.GetType()
                    .GetProperty(propertyMapping.Property.Name)
                    .SetValue(entityObject, value, null);

                Debug.WriteLine("{0} {1} {2}", propertyMapping.Property.Name,
                    ((ScalarPropertyMapping)propertyMapping).Column, value);
            }

            if (NavigationProperties.Count == 0)
            {
                NavigationProperties = NavigationProperties.AddRange(entityTypeMapping.EntityType.NavigationProperties);
            }

            // 映射相关的导航属性
            foreach (在 NavigationProperties 中的 var navigationProperty)
            {
                var propertyInfo = entityObject.GetType().GetProperty(navigationProperty.Name);

                // 待办事项:Por Marco em 26/11/2015
                /*
                 * Verificar em QueryOptions (que neste momento não é passada para esta rotina) se foi solicitado Eager Loading desta navigationProperty。
                 * Caso negativo executar um "继续;"
                 *
                 * Isso ajudará a evitar Consultas desnecessárias ao banco de dados。
                */

                var propertyType = propertyInfo.PropertyType;

                var 关联集映射 =
                    关联SetMappingCollection.First(
                        a => a.AssociationSet.ElementType.FullName == navigationProperty.RelationshipType.FullName);

                // AssociationSetMapping.AssociationTypeMapping.MappingFragment.PropertyMappings 包含两个元素,一个用于直接关系,一个用于反向关系
                var 属性映射 =
                    AssociationSetMapping.AssociationTypeMapping.MappingFragment.PropertyMappings
                        .Cast().First(p => p.AssociationEnd.Name.EndsWith("_Target"));

                var key = propertyMappings.PropertyMappings.Select(c => reader[c.Column.Name]).ToArray();

                if (!key.Any() || key[0] 为 DBNull)
                    继续;

                ///////////////////////////////////////// ///////////////////////////////////////// ///////////////////////
                // Monta o PredicateBuilder que será utilizado para trazer todas as entidades associadas solicitadas

                var outerPredicate = typeof(PredicateBuilder).InvokeStaticGenericMethod(propertyType, "False");

                if (!Predicates.ContainsKey(propertyType))
                {
                    var predicatesList = new List { outerPredicate };
                    谓词 = Predicates.Add(propertyType, predicatesList);

                    LoadedPredicates = LoadedPredicates.Add(propertyType, new List());
                    PredicatesCount = PredicatesCount.Add(propertyType, 0);
                }

                var loadedPredicates = LoadedPredicates[propertyType];
                if (loadedPredicates.All(p => p != Convert.ToInt32(key[0])))
                {
                    loadPredicates.Add(Convert.ToInt32(key[0]));

                    BuildPredicate(propertyType, outerPredicate, Convert.ToInt32(key[0]));
                }
                ///////////////////////////////////////// ///////////////////////////////////////// ////////////////////////

                // Seta o Id como helper para a rotina LoadAssociatedEntities
                var value = Activator.CreateInstance(propertyType);
                var idProperty = propertyType.GetProperty("Id");
                idProperty.SetValue(value, key[0]);

                propertyInfo.SetValue(entityObject, value, null);
            }
        }

        private void BuildPredicate(Type propertyType, object outerPredicate, int pkValue)
        {
            var 参数 = Expression.Parameter(propertyType, "p");
            var property = Expression.Property(parameter, "Id");
            var valueToCompare = Expression.Constant(pkValue);
            var equalsExpression = Expression.Equal(property, valueToCompare);

            var funcType = typeof(Func).MakeGenericType(propertyType, typeof(bool));
            var lambdaExpression = Expression.Lambda(funcType, equalsExpression, 参数);

            var predicateList = Predicates[propertyType];
            var predicatesCount = PredicatesCount[propertyType];

            if (predicatesCount % MAX_ITEMS_PER_PREDICATE == 0)
            {
                predicateList.Add(outerPredicate);
            }

            var predicate = predicateList.Last();

            predicate = typeof(PredicateBuilder).InvokeStaticGenericMethod(propertyType, "Or", predicate, lambdaExpression);

            predicateList[predicateList.Count - 1] = 谓词;

            谓词计数++;
            PredicatesCount = PredicatesCount.Replace(propertyType, predicatesCount);
        }

        ///
        /// Carrega 通过 EagerLoading 作为 entidades associadas solicitadas
        ///
        /// 实体默认的具体说明
        /// Lista de entidades que irão ter as entidades associadas carregadas
        /// Array de Eager Loadings a serem carregados
        私人无效LoadNavigationProperties(IReadOnlyList实体,
            参数 KeyValuePair[] eagerLoadings) 其中 T : EntityDefault
        {
            foreach(谓词中的 var predicateItem)
            {
                var newEagerLoadings = 新列表>();

                var newOptions =
                    急切加载
                        .Where(p => p.Key == QueryOptions.DefineInclude || p.Key == QueryOptions.DefineIncludes)
                        .ToList();

                var predicateWhere = predicateItem;

                // Loop em todas as propriedades de navegação de T que sejam do mesmo tipo do predicate.Key
                // Esse loop terá alimentado newEagerLoadings com os valores adequados。
                前锋 (
                    var navigationProperty in
                    NavigationProperties.Where(
                        p => 实体[0].GetType().GetProperty(p.Name).PropertyType == predicateWhere.Key))
                {
                    新选项 =
                        newOptions.Where(p => p.Value.ToString().StartsWith(navigationProperty.Name)).ToList();

                    if (!newOptions.Any())
                        继续;

                    // ReSharper 禁用一次 LoopCanBeConvertedToQuery
                    foreach(newOptions 中的 var 选项)
                    {
                        if (!option.Value.ToString().Contains("."))
                        {
                            继续;
                        }

                        var newOption = Pairing.Of(option.Key,
                            option.Value.ToString()
                                .RemovePrefix(navigationProperty.Name + ".")
                                .RemovePrefix(navigationProperty.Name));

                        if (newOption.HasntValue() || newOption.Value.ToString().IsNullOrEmpty())
                        {
                            继续;
                        }

                        newEagerLoadings.Add(newOption);
                    }
                }

                var predicateList = predicateItem.Value;
                var funcType = predicateItem.Value.First().InvokeMethod("Compile", true).GetType();

                var newInstanceOfThis = GetInstanceOfService(funcType.GenericTypeArguments[0], Db);

                foreach(predicateList 中的 var 谓词)
                {
                    // A fim detentar evitar bugs de StackOverflow
                    GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);

                    var expandPredicate = typeof(Extensions).InvokeStaticGenericMethod(funcType, "Expand", predicate);

                    var selectResponse = (IEnumerable)newInstanceOfThis.InvokeGenericMethod(predicateItem.Key,
                        “很多”,expandedPredicate,newEagerLoadings.ToArray());

                    var listOfItems = selectResponse.ToList();

                    // Obtém o retorno

                    // 执行一个查询 e preenche PredicateEntities
                    NavigationEntities = NavigationEntities.AddRange(listOfItems);
                }
            }

            // 循环 nas entidades para atribuir as entidades associadas
            foreach(实体中的 var 实体)
            {
                // Loop nas propriedades de navegação, para listar as entidades associadas
                foreach (在 NavigationProperties 中的 var navigationProperty)
                {
                    // navigationProperty é a entidade associada que será atribuída 实体

                    var propertyInfo = entity.GetType().GetProperty(navigationProperty.Name);
                    var propertyType = propertyInfo.PropertyType;

                    var propertyValue = propertyInfo.GetValue(entity);

                    if (propertyValue == null)
                    {
                        继续;
                    }

                    var idPropertyInfo = propertyType.GetProperty("Id");
                    var keyValue = idPropertyInfo.GetValue(propertyValue);

                    如果(键值 == 空)
                    {
                        继续;
                    }

                    var key = Convert.ToInt32(keyValue);

                    // Pega a lista de entidades associadas que sejam do mesmo tipo da propriedade de navegação
                    var associatedEntitiesOfSameType = NavigationEntities.Where(p => p.GetType() == propertyType)
                        .ToList();

                    if (!associatedEntitiesOfSameType.Any())
                    {
                        // O usuário não solicitou EagerLoading dessa navigationProperty

                        继续;
                    }

                    // Busca a entidade associada pelo Id, alimentado em "InternalMapEntity"
                    var 关联实体实例 =
                        associatedEntitiesOfSameType.FirstOrDefault(
                            p => Convert.ToInt32(idPropertyInfo.GetValue(p)) == key);

                    如果(关联实体实例 == 空)
                        继续; // Não 本地化。Removida do banco de dados?

                    // Atribui a entidade associada a "entity"
                    propertyInfo.SetValue(entity, associatedEntityInstance);
                }
            }
        }
    }
于 2019-08-15T10:41:44.610 回答