2016-09-29 2 views
2

다음 데이터베이스 테이블 (SQL Server 2005)을 고려하십시오. 나는이 기능을 EF (v6, .net 4.5.1)에서 번역 기능과 함께 사용하고 싶지만, 검색 후 이것이 지원되지 않는 것처럼 보입니다. C#을 구문에 의해 지원되지 않는 속성 Foo와 클래스 Foo을 만들 것동적 C# 구문 오류가 발생하지 않도록하려면

CREATE TABLE Foo 
(
    pk INT NOT NULL PRIMARY KEY, 
    Foo VARCHAR(100) 
) 

사용하여-규칙 매핑. 내가 사용하는 시도 ColumnAttribute :

public partial class Foo 
{ 
    [Key] 
    public virtual int pk {get;set;} 
    [Column("Foo")] 
    public virtual string Name {get;set;} 
} 

이 작동하도록 나타납니다,하지만 그래서 난에 다시 사용할 수있는 초기 페이지 요청이 저장 프로 시저 및 MARS를 통해 데이터의 수병을로드 할 좋아하는 (그리고 일반적인 구조를 사용하십시오 다른 페이지)), 그래서 저장 프로 시저를 호출하고 아래에 (유사한 반사를 통해 ObjectContext.Translate를 호출, 결과 세트를 통해 반복하지만, 이것은 축약 :

var methTranslate = typeof(ObjectContext).GetMethod("Translate", new[] { typeof(DbDataReader), typeof(string), typeof(MergeOption) }); 

foreach (var className in classNames) 
{ 
    // ... 
    var translateGenericMethod = methTranslate.MakeGenericMethod(classType); 
    // ... 
    reader.NextResult(); 
    var enumerable = (IEnumerable)translateGenericMethod.Invoke(ObjectContext, 
     new object[] { reader, entitySet.Name, MergeOption.AppendOnly }); 
} 

multiplethings에서 나는이의 ColumnAttribute 읽었습니다 매핑이 지원되지 않습니다. MSDN에서 :

EF does not take any mapping into account when it creates entities using the Translate method. It will simply match column names in the result set with property names on your classes.

그리고 확실히 충분히, 내가 얻을 오류 :

The data reader is incompatible with the specified 'Namespace.Foo'. A member of the type, 'Name', does not have a corresponding column in the data reader with the same name.

문제는, 내가 매핑에서 어떤 대안이나 지정하는 방법/힌트를 볼 수 없습니다. 클래스 이름을 변경할 수는 있지만 속성 이름보다 바람직하지 않습니다.

또는 Translate을 사용하지 않고 데이터를 동적으로로드하는 다른 방법?

+0

저장 프로 시저 + 동적 데이터 구조 = [Dapper] (https://github.com/StackExchange/dapper-dot-net)을 읽습니다. –

답변

1

조금 까다 롭지 만 할 수 있습니다.

아이디어는 필요한 매핑을 수행하는 사용자 지정 DbDataReader을 구현하고 사용하여 Translate 메서드를 활용하는 것입니다. 성가 시게 모든 추상적 인/의미있는 가상 회원을 무시하고 기본 객체에 위임 - 공상

abstract class DelegatingDbDataReader : DbDataReader 
{ 
    readonly DbDataReader source; 
    public DelegatingDbDataReader(DbDataReader source) 
    { 
     this.source = source; 
    } 
    public override object this[string name] { get { return source[name]; } } 
    public override object this[int ordinal] { get { return source[ordinal]; } } 
    public override int Depth { get { return source.Depth; } } 
    public override int FieldCount { get { return source.FieldCount; } } 
    public override bool HasRows { get { return source.HasRows; } } 
    public override bool IsClosed { get { return source.IsClosed; } } 
    public override int RecordsAffected { get { return source.RecordsAffected; } } 
    public override bool GetBoolean(int ordinal) { return source.GetBoolean(ordinal); } 
    public override byte GetByte(int ordinal) { return source.GetByte(ordinal); } 
    public override long GetBytes(int ordinal, long dataOffset, byte[] buffer, int bufferOffset, int length) { return source.GetBytes(ordinal, dataOffset, buffer, bufferOffset, length); } 
    public override char GetChar(int ordinal) { return source.GetChar(ordinal); } 
    public override long GetChars(int ordinal, long dataOffset, char[] buffer, int bufferOffset, int length) { return source.GetChars(ordinal, dataOffset, buffer, bufferOffset, length); } 
    public override string GetDataTypeName(int ordinal) { return source.GetDataTypeName(ordinal); } 
    public override DateTime GetDateTime(int ordinal) { return source.GetDateTime(ordinal); } 
    public override decimal GetDecimal(int ordinal) { return source.GetDecimal(ordinal); } 
    public override double GetDouble(int ordinal) { return source.GetDouble(ordinal); } 
    public override IEnumerator GetEnumerator() { return source.GetEnumerator(); } 
    public override Type GetFieldType(int ordinal) { return source.GetFieldType(ordinal); } 
    public override float GetFloat(int ordinal) { return source.GetFloat(ordinal); } 
    public override Guid GetGuid(int ordinal) { return source.GetGuid(ordinal); } 
    public override short GetInt16(int ordinal) { return source.GetInt16(ordinal); } 
    public override int GetInt32(int ordinal) { return source.GetInt32(ordinal); } 
    public override long GetInt64(int ordinal) { return source.GetInt64(ordinal); } 
    public override string GetName(int ordinal) { return source.GetName(ordinal); } 
    public override int GetOrdinal(string name) { return source.GetOrdinal(name); } 
    public override string GetString(int ordinal) { return source.GetString(ordinal); } 
    public override object GetValue(int ordinal) { return source.GetValue(ordinal); } 
    public override int GetValues(object[] values) { return source.GetValues(values); } 
    public override bool IsDBNull(int ordinal) { return source.IsDBNull(ordinal); } 
    public override bool NextResult() { return source.NextResult(); } 
    public override bool Read() { return source.Read(); } 
    public override void Close() { source.Close(); } 
    public override T GetFieldValue<T>(int ordinal) { return source.GetFieldValue<T>(ordinal); } 
    public override Task<T> GetFieldValueAsync<T>(int ordinal, CancellationToken cancellationToken) { return source.GetFieldValueAsync<T>(ordinal, cancellationToken); } 
    public override Type GetProviderSpecificFieldType(int ordinal) { return source.GetProviderSpecificFieldType(ordinal); } 
    public override object GetProviderSpecificValue(int ordinal) { return source.GetProviderSpecificValue(ordinal); } 
    public override int GetProviderSpecificValues(object[] values) { return source.GetProviderSpecificValues(values); } 
    public override DataTable GetSchemaTable() { return source.GetSchemaTable(); } 
    public override Stream GetStream(int ordinal) { return source.GetStream(ordinal); } 
    public override TextReader GetTextReader(int ordinal) { return source.GetTextReader(ordinal); } 
    public override Task<bool> IsDBNullAsync(int ordinal, CancellationToken cancellationToken) { return source.IsDBNullAsync(ordinal, cancellationToken); } 
    public override Task<bool> ReadAsync(CancellationToken cancellationToken) { return source.ReadAsync(cancellationToken); } 
    public override int VisibleFieldCount { get { return source.VisibleFieldCount; } } 
} 

아무것도 :

그 일을하기 전에 그냥 기본 DbDataReader에 위임 않는 일반적인 DbDataReader 클래스를 구현하자. 이름 매핑을 수행

이제 독자 :

class MappingDbDataReader : DelegatingDbDataReader 
{ 
    Dictionary<string, string> nameToSourceNameMap; 
    public MappingDbDataReader(DbDataReader source, Dictionary<string, string> nameToSourceNameMap) : base(source) 
    { 
     this.nameToSourceNameMap = nameToSourceNameMap; 
    } 
    private string GetSourceName(string name) 
    { 
     string sourceName; 
     return nameToSourceNameMap.TryGetValue(name, out sourceName) ? sourceName : name; 
    } 
    public override object this[string name] 
    { 
     get { return base[GetSourceName(name)]; } 
    } 
    public override string GetName(int ordinal) 
    { 
     string sourceName = base.GetName(ordinal); 
     return nameToSourceNameMap 
      .Where(item => item.Value.Equals(sourceName, StringComparison.OrdinalIgnoreCase)) 
      .Select(item => item.Key) 
      .FirstOrDefault() ?? sourceName; 
    } 
    public override int GetOrdinal(string name) 
    { 
     return base.GetOrdinal(GetSourceName(name)); 
    } 
} 

다시, 아무것도 공상. 몇 가지 메서드를 재정의하고 열 이름에 이름을 지정하거나 그 반대의 매핑을 수행합니다.

마지막으로, 당신이 무엇을 요구하지 도우미 방법 :

public static class EntityUtils 
{ 
    public static ObjectResult<T> ReadSingleResult<T>(this DbContext dbContext, DbDataReader dbReader) 
     where T : class 
    { 
     var objectContext = ((IObjectContextAdapter)dbContext).ObjectContext; 
     var columnMappings = objectContext.GetPropertyMappings(typeof(T)) 
      .ToDictionary(m => m.Property.Name, m => m.Column.Name); 
     var mappingReader = new MappingDbDataReader(dbReader, columnMappings); 
     return objectContext.Translate<T>(mappingReader); 
    } 

    static IEnumerable<ScalarPropertyMapping> GetPropertyMappings(this ObjectContext objectContext, Type clrEntityType) 
    { 
     var metadata = objectContext.MetadataWorkspace; 

     // Get the part of the model that contains info about the actual CLR types 
     var objectItemCollection = ((ObjectItemCollection)metadata.GetItemCollection(DataSpace.OSpace)); 

     // Get the entity type from the model that maps to the CLR type 
     var entityType = metadata 
       .GetItems<EntityType>(DataSpace.OSpace) 
         .Single(e => objectItemCollection.GetClrType(e) == clrEntityType); 

     // Get the entity set that uses this entity type 
     var entitySet = metadata 
      .GetItems<EntityContainer>(DataSpace.CSpace) 
        .Single() 
        .EntitySets 
        .Single(s => s.ElementType.Name == entityType.Name); 

     // Find the mapping between conceptual and storage model for this entity set 
     var mapping = metadata.GetItems<EntityContainerMapping>(DataSpace.CSSpace) 
         .Single() 
         .EntitySetMappings 
         .Single(s => s.EntitySet == entitySet); 

     // Find the storage property (column) mappings 
     var propertyMappings = mapping 
      .EntityTypeMappings.Single() 
      .Fragments.Single() 
      .PropertyMappings 
      .OfType<ScalarPropertyMapping>(); 


     return propertyMappings; 
    } 

ReadSingleResult 문제의 도우미 메서드입니다. GetPropertyMappings 메서드는 코드 EF6.1 Get Mapping Between Properties and Columns의 일부를 사용하고 있습니다.제공된 예와 유사

샘플 사용 :

var readMethodBase = typeof(EntityUtils).GetMethod("ReadSingleResult", new[] { typeof(DbContext), typeof(DbDataReader) }); 

foreach (var className in classNames) 
{ 
    // ... 
    var readMethod = readMethodBase.MakeGenericMethod(classType); 
    var result = ((IEnumerable)readMethod.Invoke(null, new object[] { dbContext, dbReader })) 
     .Cast<dynamic>() 
     .ToList(); 
    // ... 
    dbReader.NextResult(); 
} 

희망하는 데 도움이됩니다.

+0

이것은 작동하는 것으로 보입니다. 그러나 Translate가 수행하는 한 가지는 DbContext에'result's를 추가하는 것입니다. 이 작업을 명시 적으로 수행해야하고 while (enumarator.MoveNext()) {this.Entry (enumarator.Current) .State = EntityState.Unchanged; };'foreach 루프에서로드하는 데 30 초 이상 걸렸습니다. (해결책으로 표시 하겠지만 어쩌면 잘못된 문제를 해결하려고합니다. – mlhDev

+0

@Matthew 실제로 한 가지 차이점은 내가 더 간단한 'Translate'를 사용한다는 것입니다. overload. 내 샘플에서 커스텀 데이터 리더를 가져 와서 원래 코드에서와 같이'entitySet.Name, MergeOption.AppendOnly' 과부하를 사용할 수있다. –

+0

AH! 생각없이 복사 - 붙여 넣기에 대한 내 잘못. 2.3 초. 감사합니다! – mlhDev

관련 문제