我正在使用Entity Framework 5(使用Code First方法)从带有参数的遗留存储过程填充我的类,这很正常(详情如下)。 我的问题是我想将列的名称映射到具有不同名称的属性(我不喜欢来自Erp的名称)。 我尝试使用Configuration类(就像我映射到视图或表时所做的那样)来为具有不同名称的属性指定列名,这是我的结果:
为什么配置会影响我的结果,但其规范不用于映射列?有没有其他方法可以使用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");
}
}
存储过程返回带有“Code”和“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());
}
谢谢!
答案 0 :(得分:11)
我在这里找到了:
http://entityframework.codeplex.com/workitem/233?PendingVoteId=233
“SqlQuery方法的设计不考虑任何映射......”。
他们也说“我们同意选择使SqlQuery荣誉列属性是有用的,所以我们将这个问题保持打开并将其放在我们的待办事项上,以备将来考虑。”,所以,如果你有我的话同样的问题,请投票: - )
答案 1 :(得分:3)
同时,您可以使用此方法。 几乎没有测试(因为它适用于我的课程)但是如果需要的话也很难修复...... 它需要一个上下文(检索映射的自定义类型),它需要一个不同的连接来同时在其上运行一个datareader。
用法:
列出students = Mapper.Map(context,(new SchoolContext())。Database.Connection,&#34; Select * from Students&#34;);
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);
}
}
}
答案 2 :(得分: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);
}
}
答案 3 :(得分:0)
在使用bubi的方法并实现了一些代码之后,我决定在这里发布我们的改进。请注意,这里有我不会发布的其他名称空间的引用。只要适应您的需求即可。
无论如何,我希望它能对某人有所帮助。
using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Data; using System.Data.Common; using System.Data.Entity.Core.EntityClient; using System.Data.Entity.Core.Mapping; using System.Data.Entity.Core.Metadata.Edm; using System.Data.Entity.Infrastructure; using System.Diagnostics; using System.Linq; using System.Linq.Expressions; public abstract partial class BaseService where TEntity : EntityDefault { private const int MAX_ITEMS_PER_PREDICATE = 500; /// /// Lista imutável contendo todos os predicates, por tipo da entidade, a serem buscados no banco de dados. /// private ImmutableDictionary> Predicates { get; set; } private ImmutableDictionary PredicatesCount { get; set; } private ImmutableDictionary> LoadedPredicates { get; set; } /// /// Lista imutável contendo as entidades, que são propriedades de navegação, já buscadas no banco de dados. /// private ImmutableList NavigationEntities { get; set; } /// /// Lista imutável contendo todas as propriedades de navegação /// private ImmutableList NavigationProperties { get; set; } /// /// Maps the result of a query into entities. /// /// /// The SQL query. /// List of parameters to be passed to the procedure /// /// It might return null when query is null or empty. /// An entity list /// /// context /// or /// queryConnection /// or /// sqlQuery /// public List SqlQuery(string query, Dictionary parameters, params KeyValuePair[] options) where T : EntityDefault { DbConnection queryConnection = null; try { InitOrResetSqlQueryVariables(); if (query.HasntValue()) { throw new ArgumentNullException(nameof(query)); } queryConnection = Db.Database.Connection; var connectionState = queryConnection.State; if (connectionState != ConnectionState.Open) { queryConnection.Open(); } var command = queryConnection.CreateCommand(); command.CommandType = CommandType.StoredProcedure; command.CommandText = query; if (parameters != null) { command.AddParameters(parameters); } var reader = command.ExecuteReader(); var entities = new List(); while (reader.Read()) { entities.Add(MapEntity(reader)); } LoadNavigationProperties(entities, options); return entities; } finally { InitOrResetSqlQueryVariables(); if (Db.BaseDb.AutoCloseConnection && queryConnection != null) { if (queryConnection.State != ConnectionState.Closed) { queryConnection.Close(); } queryConnection.Dispose(); } } } public List SqlQuery(string query, List parameters, params KeyValuePair[] options) where T : EntityDefault { DbConnection queryConnection = null; try { InitOrResetSqlQueryVariables(); if (query.HasntValue()) { throw new ArgumentNullException(nameof(query)); } queryConnection = Db.Database.Connection; var connectionState = queryConnection.State; if (connectionState != ConnectionState.Open) { queryConnection.Open(); } var command = queryConnection.CreateCommand(); command.CommandType = CommandType.StoredProcedure; command.CommandText = query; if (parameters != null) { command.Parameters.AddRange(parameters.ToArray()); } var reader = command.ExecuteReader(); var entities = new List(); while (reader.Read()) { entities.Add(MapEntity(reader)); } LoadNavigationProperties(entities, options); return entities; } finally { InitOrResetSqlQueryVariables(); if (Db.BaseDb.AutoCloseConnection && queryConnection != null) { if (queryConnection.State != ConnectionState.Closed) { queryConnection.Close(); } queryConnection.Dispose(); } } } private T MapEntity(IDataRecord reader) { var entityObject = Activator.CreateInstance(); MapEntity(reader, entityObject); return 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) .Single() .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]; // Maps the properties of the entity itself foreach (var propertyMapping in mappingFragment.PropertyMappings) { var valueBeforCasting = reader[((ScalarPropertyMapping)propertyMapping).Column.Name]; var value = valueBeforCasting is DBNull ? null : propertyMapping.Property.IsEnumType ? Convert.ChangeType(valueBeforCasting, typeof(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); } // Maps the associated navigational properties foreach (var navigationProperty in NavigationProperties) { var propertyInfo = entityObject.GetType().GetProperty(navigationProperty.Name); // TODO: 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 "continue;" * * Isso ajudará a evitar consultas desnecessárias ao banco de dados. */ var propertyType = propertyInfo.PropertyType; var 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 var propertyMappings = 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] is DBNull) continue; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 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 = 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]))) { loadedPredicates.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 parameter = 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, parameter); 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] = predicate; predicatesCount++; PredicatesCount = PredicatesCount.Replace(propertyType, predicatesCount); } /// /// Carrega as entidades associadas solicitadas via EagerLoading /// /// Tipo específico de EntityDefault /// Lista de entidades que irão ter as entidades associadas carregadas /// Array de Eager Loadings a serem carregados private void LoadNavigationProperties(IReadOnlyList entities, params KeyValuePair[] eagerLoadings) where T : EntityDefault { foreach (var predicateItem in Predicates) { var newEagerLoadings = new List>(); var newOptions = eagerLoadings .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. foreach ( var navigationProperty in NavigationProperties.Where( p => entities[0].GetType().GetProperty(p.Name).PropertyType == predicateWhere.Key)) { newOptions = newOptions.Where(p => p.Value.ToString().StartsWith(navigationProperty.Name)).ToList(); if (!newOptions.Any()) continue; // ReSharper disable once LoopCanBeConvertedToQuery foreach (var option in newOptions) { if (!option.Value.ToString().Contains(".")) { continue; } var newOption = Pairing.Of(option.Key, option.Value.ToString() .RemovePrefix(navigationProperty.Name + ".") .RemovePrefix(navigationProperty.Name)); if (newOption.HasntValue() || newOption.Value.ToString().IsNullOrEmpty()) { continue; } newEagerLoadings.Add(newOption); } } var predicateList = predicateItem.Value; var funcType = predicateItem.Value.First().InvokeMethod("Compile", true).GetType(); var newInstanceOfThis = GetInstanceOfService(funcType.GenericTypeArguments[0], Db); foreach (var predicate in predicateList) { // A fim de tentar evitar bugs de StackOverflow GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); var expandedPredicate = typeof(Extensions).InvokeStaticGenericMethod(funcType, "Expand", predicate); var selectResponse = (IEnumerable)newInstanceOfThis.InvokeGenericMethod(predicateItem.Key, "Many", expandedPredicate, newEagerLoadings.ToArray()); var listOfItems = selectResponse.ToList(); // Obtém o retorno // Executa a query e preenche PredicateEntities NavigationEntities = NavigationEntities.AddRange(listOfItems); } } // Loop nas entidades para atribuir as entidades associadas foreach (var entity in entities) { // Loop nas propriedades de navegação, para listar as entidades associadas foreach (var navigationProperty in NavigationProperties) { // navigationProperty é a entidade associada que será atribuída a entity var propertyInfo = entity.GetType().GetProperty(navigationProperty.Name); var propertyType = propertyInfo.PropertyType; var propertyValue = propertyInfo.GetValue(entity); if (propertyValue == null) { continue; } var idPropertyInfo = propertyType.GetProperty("Id"); var keyValue = idPropertyInfo.GetValue(propertyValue); if (keyValue == null) { continue; } 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 continue; } // Busca a entidade associada pelo Id, alimentado em "InternalMapEntity" var associatedEntityInstance = associatedEntitiesOfSameType.FirstOrDefault( p => Convert.ToInt32(idPropertyInfo.GetValue(p)) == key); if (associatedEntityInstance == null) continue; // Não localizada. Removida do banco de dados? // Atribui a entidade associada a "entity" propertyInfo.SetValue(entity, associatedEntityInstance); } } } }