有點棘手,但可行。
這個想法是利用Translate
方法,通過實現和使用執行所需映射的自定義DbDataReader
。
在這之前,讓我們實現一個通用DbDataReader
類,它只是委託給底層DbDataReader
:
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; } }
}
沒什麼特別 - 煩人重寫所有抽象/有意義的虛擬成員和委託給底層對象。
現在執行名稱映射讀者:
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
是有問題的helper方法。 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();
}
希望有所幫助。
讀取存儲過程+動態數據結構= [Dapper](https://github.com/StackExchange/dapper-dot-net)。 –