2016-09-29 5 views
2

次のデータベーステーブル(SQL Server 2005)を検討してください。私はEF(v6、.net 4.5.1)でTranslate関数を使っていますが、検索した後はこれがサポートされていないようです。 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

これはうまくいくように見えますが、翻訳が実行していたことの1つはDbContextに 'result'sを追加することでした。これを明示的に行う必要があり、 'while(enumarator.MoveNext()){this.Entry(enumarator.Current).State = EntityState.Unchanged; }; 'foreachループには、読み込みに30秒以上かかりました。(私はこれを解決策としてマークしますが、間違った問題を達成しようとしています。 – mlhDev

+0

@Matthew実際には1つの違いは、私のサンプルからカスタムデータリーダーを取り出し、元のコードのように 'entitySet.Name、MergeOption.AppendOnly'でオーバーロードを使用することができます。 –

+0

AH!思っていないコピー&ペーストのための私のせいです。 2.3秒に。ありがとう! – mlhDev

関連する問題