12

我正在使用實體框架5(使用代碼優先方法)從具有參數的舊式存儲過程填充我的類,並且此工作正常(詳細內容如下)。 我的問題是我想將列的名稱映射到具有不同名稱的屬性(我不喜歡來自Erp的名稱)。 我試圖用一個配置類(比如當我映射到視圖或表我做什麼),以用於不同的名稱屬性指定的列名,這裏是我的結果:實體框架代碼優先 - 爲SqlQuery配置映射

  • 如果我不使用配置類(我沒有將它添加到DbContext的OnModelCreating方法中),那麼EF工作,但只加載與列的名稱完全匹配的屬性(這是我在這種情況下預期的);其他財產爲空;
  • 如果我使用配置類(將它添加到DbContext的OnModelCreating方法中的modelBuilder中),那麼EF會引發一個異常,指出「數據讀取器與指定的'... Item'不兼容。類型'描述'在數據讀取器中沒有相應的列,名稱相同「,這對我來說聽起來很奇怪,因爲在配置中,我指定將屬性描述映射到列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"); 
     } 
    } 

的存儲過程中的列返回數據 「守則」 和「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

我很高興這篇文章。我一直在和我一起奮鬥,我需要的只是'exec'部分。我發現了很多例子,而且沒有'exec'部分。 –

回答

11

我發現這裏:

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

說, 「SqlQuery類方法的設計不採取任何映射到帳戶...」。

他們也說「我們同意讓SqlQuery可以選擇列屬性是很有用的,所以我們將這個問題保持開放並將其放在我們的待辦事項上以備將來考慮。」,所以,如果你有我的同樣的問題,請投票:-)

+2

嗯,這是2013年,它似乎仍然無法正常工作。 –

+1

使2014年... –

+1

2014年10月,仍然沒什麼'。投票在CodePlex上發佈。 –

3

同時,您可以使用此方法。 很少有測試(因爲它適用於我的課程),但如果需要,難以修復... 它需要一個上下文(以檢索映射的自定義類型),它需要一個不同的連接來同時運行一個datareader 。

用法:
List students = Mapper.Map(context,(new SchoolContext())。Database。連接,「從學生中選擇*」);

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

    } 
}