动态转换以避免C#语法错误

请考虑以下数据库表(SQL Server 2005)。 我想在EF(v6,.net 4.5.1)中使用它与翻译function,但搜索后似乎不支持。

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

使用按惯例映射,该映射将使用C#语法不支持的属性Foo创建类Foo 。 我尝试使用ColumnAttribute

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

这似乎工作,但我想通过存储过程和MARS使初始页面请求加载数据(并使用通用结构,以便我可以在其他页面上重用它),所以我调用存储过程并循环通过结果集,通过reflection调用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 }); } 

从我读过的很多内容来看,不支持ColumnAttribute映射。 来自MSDN :

EF在使用Translate方法创建实体时不会考虑任何映射。 它只是将结果集中的列名与类上的属性名匹配。

果然,我得到了错误:

数据读取器与指定的“Namespace.Foo”不兼容。 类型为“名称”的成员在数据读取器中没有具有相同名称的相应列。

问题是,我没有看到任何替代或方式来指定/提示映射。 我可以更改类名,但这比属性名称更不可取。

任何变通方法,或任何其他方式动态加载数据而不使用Translate

有点棘手,但可行。

我们的想法是通过实现和使用执行所需映射的自定义DbDataReader来利用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(int ordinal) { return source.GetFieldValue(ordinal); } public override Task GetFieldValueAsync(int ordinal, CancellationToken cancellationToken) { return source.GetFieldValueAsync(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 IsDBNullAsync(int ordinal, CancellationToken cancellationToken) { return source.IsDBNullAsync(ordinal, cancellationToken); } public override Task ReadAsync(CancellationToken cancellationToken) { return source.ReadAsync(cancellationToken); } public override int VisibleFieldCount { get { return source.VisibleFieldCount; } } } 

没什么好看的 – 烦人地覆盖所有抽象/有意义的虚拟成员并委托给底层对象。

现在执行名称映射的读者:

 class MappingDbDataReader : DelegatingDbDataReader { Dictionary nameToSourceNameMap; public MappingDbDataReader(DbDataReader source, Dictionary 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 ReadSingleResult(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(mappingReader); } static IEnumerable 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(DataSpace.OSpace) .Single(e => objectItemCollection.GetClrType(e) == clrEntityType); // Get the entity set that uses this entity type var entitySet = metadata .GetItems(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(DataSpace.CSSpace) .Single() .EntitySetMappings .Single(s => s.EntitySet == entitySet); // Find the storage property (column) mappings var propertyMappings = mapping .EntityTypeMappings.Single() .Fragments.Single() .PropertyMappings .OfType(); return propertyMappings; } 

ReadSingleResult是有问题的辅助方法。 GetPropertyMappings方法使用EF6.1获取属性和列之间的映射的部分代码。

示例用法类似于提供的示例:

 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() .ToList(); // ... dbReader.NextResult(); } 

希望有所帮助。