Получить имя таблицы базы данных из метаданных Entity Framework
Я пытаюсь найти способ получить имя базовой таблицы SQL для данного типа сущности. Я экспериментировал с запросами MetadataWorkspace, и хотя я могу получить много информации от объекта или пространства хранения, я не могу понять, как сопоставить их.
Допустим, у меня есть тип в объектной модели с именем Lookup - как мне найти имя таблицы (wws_lookups) в базе данных?
Я могу запросить все объекты EntityType для CSpace и SSpace, и я могу видеть оба списка правильно, но я не могу понять, как получить SSpace из CSpace.
Есть какой-либо способ сделать это?
26 ответов
Я использую подход Найджела (извлечение имени таблицы из .ToTraceString()
), но с некоторыми изменениями, потому что его код не будет работать, если таблица не входит в схему SQL Server по умолчанию (dbo.{table-name}
).
Я создал методы расширения для DbContext
а также ObjectContext
объекты:
public static class ContextExtensions
{
public static string GetTableName<T>(this DbContext context) where T : class
{
ObjectContext objectContext = ((IObjectContextAdapter) context).ObjectContext;
return objectContext.GetTableName<T>();
}
public static string GetTableName<T>(this ObjectContext context) where T : class
{
string sql = context.CreateObjectSet<T>().ToTraceString();
Regex regex = new Regex(@"FROM\s+(?<table>.+)\s+AS");
Match match = regex.Match(sql);
string table = match.Groups["table"].Value;
return table;
}
}
Подробнее здесь:
Entity Framework: получить имя сопоставленной таблицы от объекта
РЕДАКТИРОВАТЬ Этот ответ теперь устарел из-за новой функции в EF 6.1: сопоставление между типами таблиц. Иди туда первым!
У меня была проблема с другими ответами, потому что у меня есть производный тип. Я получил этот метод (внутри моего контекстного класса), чтобы работать - у меня только один уровень наследования в моей модели на данный момент
private readonly static Dictionary<Type, EntitySetBase> _mappingCache
= new Dictionary<Type, EntitySetBase>();
private ObjectContext _ObjectContext
{
get { return (this as IObjectContextAdapter).ObjectContext; }
}
private EntitySetBase GetEntitySet(Type type)
{
if (_mappingCache.ContainsKey(type))
return _mappingCache[type];
type = GetObjectType(type);
string baseTypeName = type.BaseType.Name;
string typeName = type.Name;
ObjectContext octx = _ObjectContext;
var es = octx.MetadataWorkspace
.GetItemCollection(DataSpace.SSpace)
.GetItems<EntityContainer>()
.SelectMany(c => c.BaseEntitySets
.Where(e => e.Name == typeName
|| e.Name == baseTypeName))
.FirstOrDefault();
if (es == null)
throw new ArgumentException("Entity type not found in GetEntitySet", typeName);
// Put es in cache.
_mappingCache.Add(type, es);
return es;
}
internal String GetTableName(Type type)
{
EntitySetBase es = GetEntitySet(type);
//if you are using EF6
return String.Format("[{0}].[{1}]", es.Schema, es.Table);
//if you have a version prior to EF6
//return string.Format( "[{0}].[{1}]",
// es.MetadataProperties["Schema"].Value,
// es.MetadataProperties["Table"].Value );
}
internal Type GetObjectType(Type type)
{
return System.Data.Entity.Core.Objects.ObjectContext.GetObjectType(type);
}
NB. Существуют планы по улучшению API метаданных, и если это не дает того, чего мы хотим, мы можем взглянуть на EF Code First Mapping между типами и таблицами.
Добавление другого ответа для особого случая, когда вы используете аннотации, чтобы явно указать EF, какое имя таблицы использовать. Например, если у вас есть:
[Table("tblCompany")]
public class Company
{
}
Вы можете легко получить доступ к TableAttribute
аннотация для поиска имени таблицы:
((System.ComponentModel.DataAnnotations.Schema.TableAttribute)typeof(YourNamespace.BO.Company).GetCustomAttribute(typeof(System.ComponentModel.DataAnnotations.Schema.TableAttribute))).Name
который tblCompany
для данного образца.
Как готовый метод:
using System.Reflection;
using System.ComponentModel.DataAnnotations.Schema;
public static string GetTableName<T>() {
return ((TableAttribute)typeof(T).GetCustomAttribute(typeof(TableAttribute))).Name;
}
(Я знаю, что это не поможет ОП, но, учитывая название вопроса, здесь могут оказаться люди, которые могут искать этот ответ.)
Большинство ответов здесь не работают с производными классами. Этот делает. И дает вам схему тоже. Я объединил здесь ответы и немного улучшил их (вытащив такие вещи, как First() и Single() и преобразовав их в такие, как Where() и SelectMany(), и вернув имя схемы).
Это работает с EF 6.1+
// This can return multiple values because it is possible to have one entity correspond to multiple tables when doing entity splitting.
public static IEnumerable<string> GetTableName<T>(this DbContext context)
{
var type = typeof(T);
var metadata = ((IObjectContextAdapter)context).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) == type);
// Get the entity set that uses this entity type
var entitySet = metadata.GetItems(DataSpace.CSpace).Where(x => x.BuiltInTypeKind == BuiltInTypeKind.EntityType).Cast<EntityType>().Single(x => x.Name == entityType.Name);
// Find the mapping between conceptual and storage model for this entity set
var entitySetMappings = metadata.GetItems<EntityContainerMapping>(DataSpace.CSSpace).Single().EntitySetMappings.ToList();
// Find the storage entity sets (tables) that the entity is mapped
//EntitySet table;
var fragments = new List<MappingFragment>();
var mappings = entitySetMappings.Where(x => x.EntitySet.Name == entitySet.Name);
//if (mappings.Count() > 0)
//return mappings.SelectMany(m => m.EntityTypeMappings.SelectMany(em => em.Fragments)).ToList();
fragments.AddRange(mappings.SelectMany(m => m.EntityTypeMappings.SelectMany(em => em.Fragments)));
fragments.AddRange(entitySetMappings.Where(x => x.EntityTypeMappings.Where(y => y.EntityType != null).Any(y => y.EntityType.Name == entitySet.Name))
.SelectMany(m => m.EntityTypeMappings.Where(x => x.EntityType != null && x.EntityType.Name == entityType.Name).SelectMany(x => x.Fragments)));
//if (mapping != null)
//return mapping.EntityTypeMappings.Where(x => x.EntityType != null).Single(x => x.EntityType.Name == entityType.Name).Fragments;
fragments.AddRange(entitySetMappings.Where(x => x.EntityTypeMappings.Any(y => y.IsOfEntityTypes.Any(z => z.Name == entitySet.Name)))
.SelectMany(m => m.EntityTypeMappings.Where(x => x.IsOfEntityTypes.Any(y => y.Name == entitySet.Name)).SelectMany(x => x.Fragments)));
//var fragments = getFragments();
// Return the table name from the storage entity set
var tableNames = fragments.Select(f =>
{
var schemaName = f.StoreEntitySet.Schema;
var tableName = (string)f.StoreEntitySet.MetadataProperties["Table"].Value ?? f.StoreEntitySet.Name;
var name = $"[{schemaName}].[{tableName}]";
return name;
}).Distinct().ToList();
return tableNames;
}
В версиях entity-framework-core 3.0 и 3.1 это довольно тривиально:
var myClassTableName = _context.Model.FindEntityType(typeof(MyClass)).GetTableName();
Нет, к сожалению, невозможно использовать API-интерфейсы метаданных, чтобы получить имя таблицы для данного объекта.
Это связано с тем, что метаданные Mapping не являются общедоступными, поэтому невозможно перейти из C-Space в S-Space с помощью API EF.
Если вам действительно нужно это сделать, вы всегда можете составить карту самостоятельно, проанализировав MSL. Это не для слабонервных, но это должно быть возможно, если вы не используете QueryViews (которые невероятно редки), и в этот момент для всех целей и задач это невозможно (вам придется анализировать ESQL... аааа!)
Алекс Джеймс
Microsoft.
Есть способ удалить данные с помощью EF без необходимости сначала загружать их. Я описал их чуть более подробно: http://nigelfindlater.blogspot.com/2010/04/how-to-delete-objects-in-ef4-without.html
Хитрость заключается в преобразовании IQueriable в ObjectQuery и использовании метода ToTraceString. Затем отредактируйте полученную строку sql. Это работает, но вы должны быть осторожны, потому что вы обходите механизмы, которые EF имеет для поддержания зависимостей и ограничений. Но по соображениям производительности я думаю, что это нормально делать....
повеселись...
Найджел...
private string GetClause<TEntity>(IQueryable<TEntity> clause) where TEntity : class
{
string snippet = "FROM [dbo].[";
string sql = ((ObjectQuery<TEntity>)clause).ToTraceString();
string sqlFirstPart = sql.Substring(sql.IndexOf(snippet));
sqlFirstPart = sqlFirstPart.Replace("AS [Extent1]", "");
sqlFirstPart = sqlFirstPart.Replace("[Extent1].", "");
return sqlFirstPart;
}
public void DeleteAll<TEntity>(IQueryable<TEntity> clause) where TEntity : class
{
string sqlClause = GetClause<TEntity>(clause);
this.context.ExecuteStoreCommand(string.Format(CultureInfo.InvariantCulture, "DELETE {0}", sqlClause));
}
Если вы делаете codefirst в EF6, вы можете просто добавить что-то вроде следующего в ваш класс dbcontext.
public string GetTableName(Type entityType)
{
var sql = Set(entityType).ToString();
var regex = new Regex(@"FROM \[dbo\]\.\[(?<table>.*)\] AS");
var match = regex.Match(sql);
return match.Groups["table"].Value;
}
Используя EF Core с сервером SQL, это так же просто, как:
_context.Model.FindEntityType(YOUR_VAR_TYPE).SqlServer().TableName
Если вы используете шаблон T4 для классов POCO, вы можете получить его, изменив шаблон T4. Смотрите фрагмент:
<#
////////////////////////////////////////////////////////////////////////////////
region.Begin("Custom Properties");
string xPath = "//*[@TypeName='" + entity.FullName + "']";
XmlDocument doc = new XmlDocument();
doc.Load(inputFile);
XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("edmx", "http://schemas.microsoft.com/ado/2008/10/edmx");
XmlNode item;
XmlElement root = doc.DocumentElement;
item = root.SelectSingleNode(xPath);
#>
//<#= xPath #>
//<#= entity.FullName #>
//<#= (item == null).ToString() #>
<# if (item != null) #>
// Table Name from database
public string TableName { get { return "<#= item.ChildNodes[0].Attributes["StoreEntitySet"].Value #>"; } }
<#
region.End();
////////////////////////////////////////////////////////////////////////////////
Вот что я смог придумать, используя LINQ to XML. Код также получает сопоставления для имен столбцов.
var d = XDocument.Load("MyModel.edmx");
XNamespace n = "http://schemas.microsoft.com/ado/2008/09/mapping/cs";
var l = (from etm in d.Descendants()
where etm.Name == n + "EntityTypeMapping"
let s = etm.Attribute("TypeName").Value
select new
{
Name = s.Remove(0, s.IndexOf(".") + 1).Replace(")", ""),
Table = etm.Element(n + "MappingFragment").Attribute("StoreEntitySet").Value,
Properties = (from sp in etm.Descendants(n + "ScalarProperty")
select new
{
Name = sp.Attribute("Name").Value,
Column = sp.Attribute("ColumnName").Value
}).ToArray()
}).ToArray();
Лучшим способом является использование StoreItemCollection из метаданных. Этот парень уже представил пример его использования: Получить таблицы и отношения
EF 6.1, сначала код:
public static string GetTableName<T>(this DbContext context) where T : class
{
ObjectContext objectContext = ((IObjectContextAdapter)context).ObjectContext;
return objectContext.GetTableName(typeof(T));
}
public static string GetTableName(this DbContext context, Type t)
{
ObjectContext objectContext = ((IObjectContextAdapter)context).ObjectContext;
return objectContext.GetTableName(t);
}
private static readonly Dictionary<Type,string> TableNames = new Dictionary<Type, string>();
public static string GetTableName(this ObjectContext context, Type t)
{
string result;
if (!TableNames.TryGetValue(t, out result))
{
lock (TableNames)
{
if (!TableNames.TryGetValue(t, out result))
{
string entityName = t.Name;
ReadOnlyCollection<EntityContainerMapping> storageMetadata = context.MetadataWorkspace.GetItems<EntityContainerMapping>(DataSpace.CSSpace);
foreach (EntityContainerMapping ecm in storageMetadata)
{
EntitySet entitySet;
if (ecm.StoreEntityContainer.TryGetEntitySetByName(entityName, true, out entitySet))
{
if (String.IsNullOrEmpty(entitySet.Schema))
{
result = entitySet.Table;
break;
}
//we must recognize if we are under SQL Server Compact version, which does not support multiple schemas
//SQL server compact does not support schemas, entity framework sets entitySet.Schema set to "dbo", anyway
//the System.Data.Entity.Infrastructure.TableExistenceChecker.GetTableName() returns only table name
//schema is (not) added by the overrides of the method AnyModelTableExistsInDatabase
//the SqlCeTableExistenceChecker has the knowledge that there is no metadata schema needed
//the SqlTableExistenceChecker has the knowledge that there is metadata with schema, which should be added to the table names
var entityConnection = (System.Data.Entity.Core.EntityClient.EntityConnection) context.Connection;
DbConnection storeConnection = entityConnection.StoreConnection;
if (storeConnection != null && "SqlCeConnection".Equals(storeConnection.GetType().Name, StringComparison.OrdinalIgnoreCase))
{
result = entitySet.Table;
break;
}
result = entitySet.Schema + "." + entitySet.Table;
break;
}
}
TableNames.Add(t,result);
}
}
}
return result;
}
Возможный обходной путь (не большой, но альтернативы нет...):
var sql = Context.EntitySetName.ToTraceString();
... а затем проанализируйте SQL, который должен быть довольно простым.
Вот еще один способ найти имя таблицы. Это немного странно, но работает. VB:
For Each Table In northwind.MetadataWorkspace.GetItemCollection(New System.Data.Metadata.Edm.DataSpace)
'adds table name to a list of strings all table names in EF have the project namespace in front of it.'
If Table.ToString.Contains("namespace of project") then
'using substring to remove project namespace from the table name.'
TableNames.Add(Table.ToString.Substring("length of namespace name"))
End If
Next
Вы можете попробовать расширение MappingAPI: https://efmappingapi.codeplex.com/
Это действительно легко использовать
context.Db<YourEntityType>().TableName
If you want just a quick and dirty way to get the table name (and you don't need it in your application's code), you can look at the XML generated for your Model.edmx. Find your entity in the edmx:Mappings section and the line: MappingFragment StoreEntitySet="YourTableName" will give you the table's actual name.
Используя EF5 и немного отражения, нужно сделать что-то вроде следующего:
using System;
using System.Collections;
using System.Data.Entity.Infrastructure;
using System.Data.Metadata.Edm;
using System.Linq;
using System.Reflection;
namespace EFHelpers {
public class EFMetadataMappingHelper {
public static string GetTableName(MetadataWorkspace metadata, DbEntityEntry entry) {
var entityType = entry.Entity.GetType();
var objectType = getObjectType(metadata, entityType);
var conceptualSet = getConceptualSet(metadata, objectType);
var storeSet = getStoreSet(metadata, conceptualSet);
var tableName = findTableName(storeSet);
return tableName;
}
private static EntitySet getStoreSet(MetadataWorkspace metadata, EntitySetBase entitySet) {
var csSpace = metadata.GetItems(DataSpace.CSSpace).Single();
var flags = BindingFlags.NonPublic | BindingFlags.Instance;
var entitySetMaps = (ICollection)csSpace.GetType().GetProperty("EntitySetMaps", flags).GetValue(csSpace, null);
object mapping = null;
foreach (var map in entitySetMaps) {
var set = map.GetType().GetProperty("Set", flags).GetValue(map, null);
if (entitySet == set) {
mapping = map;
break;
}
}
var m_typeMappings = ((ICollection)mapping.GetType().BaseType.GetField("m_typeMappings", flags).GetValue(mapping)).OfType<object>().Single();
var m_fragments = ((ICollection)m_typeMappings.GetType().BaseType.GetField("m_fragments", flags).GetValue(m_typeMappings)).OfType<object>().Single();
var storeSet = (EntitySet) m_fragments.GetType().GetProperty("TableSet", flags).GetValue(m_fragments, null);
return storeSet;
}
private static string findTableName(EntitySet storeSet) {
string tableName = null;
MetadataProperty tableProperty;
storeSet.MetadataProperties.TryGetValue("Table", true, out tableProperty);
if (tableProperty == null || tableProperty.Value == null)
storeSet.MetadataProperties.TryGetValue("http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator:Table", true, out tableProperty);
if (tableProperty != null)
tableName = tableProperty.Value as string;
if (tableName == null)
tableName = storeSet.Name;
return tableName;
}
private static EntityType getObjectType(MetadataWorkspace metadata, Type entityType) {
var objectItemCollection = (ObjectItemCollection)metadata.GetItemCollection(DataSpace.OSpace);
var edmEntityType = metadata
.GetItems<EntityType>(DataSpace.OSpace)
.First(e => objectItemCollection.GetClrType(e) == entityType);
return edmEntityType;
}
private static EntitySetBase getConceptualSet(MetadataWorkspace metadata, EntityType entityType) {
var entitySetBase = metadata
.GetItems<EntityContainer>(DataSpace.CSpace)
.SelectMany(a => a.BaseEntitySets)
.Where(s => s.ElementType.Name == entityType.Name)
.FirstOrDefault();
return entitySetBase;
}
}
}
Назовите это так:
public string GetTableName(DbContext db, DbEntityEntry entry) {
var metadata = ((IObjectContextAdapter)db).ObjectContext.MetadataWorkspace;
return EFMetadataMappingHelper.GetTableName(metadata, entry);
}
На самом деле я столкнулся с той же проблемой, и я создал абстрактный фрагмент кода, который дает вам два Dictionary<string,List<string>>
($ Table_name,$columns_name_list). Первый имеет базу данных Таблица + список имен столбцов, второй имеет локальные объекты EF + свойства
Конечно, вы можете добавить больше проверок на тип данных, между прочим, это заставит вас писать безумно сложный код.
Прибыль и убыток
PS Извините за сжатый стиль, я лямбда-фанатик
using (EFModelContext efmc = new EFModelContext("appConfigConnectionName"))
{
string schemaName = "dbo";
string sql = @"select o.name + '.' + c.name
from sys.all_objects o
inner join sys.schemas s on s.schema_id = o.schema_id
inner join sys.all_columns c on c.object_id = o.object_id
where Rtrim(Ltrim(o.type)) in ('U') and s.name = @p0";
Dictionary<string, List<string>> dbTableColumns = new Dictionary<string, List<string>>();
efmc.Database.SqlQuery<string>(sql, schemaName).Select(tc =>
{
string[] splitted = System.Text.RegularExpressions.Regex.Split(tc, "[.]");
return new { TableName = splitted[0], ColumnName = splitted[1] };
}).GroupBy(k => k.TableName, k => k.ColumnName).ToList().ForEach(ig => dbTableColumns.Add(ig.Key, ig.ToList()));
Dictionary<string, List<string>> efTableColumns = new Dictionary<string, List<string>>();
efTableColumns = ((IObjectContextAdapter)uc).ObjectContext.MetadataWorkspace
.GetItems(DataSpace.SSpace).OfType<EntityType>()
.ToDictionary( eft => eft.MetadataProperties
.First(mp => mp.Name == "TableName").Value.ToString(),
eft => eft.Properties.Select(p => p.Name).ToList());
}
В случае использования Entity Framework Core 2.0+ вы можете легко получить реляционные метаданные, используя предоставленные API.
foreach (var entityType in dbContext.Model.GetEntityTypes())
{
var tableName = entityType.Relational().TableName;
foreach (var propertyType in entityType.GetProperties())
{
var columnName = propertyType.Relational().ColumnName;
}
}
Для использования метода Relational() должен быть установлен пакет Nuget Microsoft.EntityFrameworkCore.Relational.
Вы можете использовать метод расширения, чтобы получить имя таблицы из вашего класса сущности.
В этом случае вам не нужно получать имя таблицы, оно получает его внутри метода расширения
public static string GetTableName<T>(this T entity) where T : class
{
var object_context = GetObjectContext(entity);
if (object_context == null || object_context.TransactionHandler == null)
return null;
var dbcontext=object_context.TransactionHandler.DbContext;
var query= dbcontext.Set(entity.GetType()).ToString();
var reg = new Regex(@"FROM \[dbo\]\.\[(?<table>.*)\] AS");
var match = reg.Match(query);
var tableName= match.Groups["table"].Value;
return tableName;
}
private static ObjectContext GetObjectContext(object entity)
{
var field = entity.GetType().GetField("_entityWrapper");
if (field == null)
return null;
var val= field.GetValue(entity);
var property = val.GetType().GetProperty("Context");
var context = (ObjectContext)property.GetValue(val, null);
return context;
}
Вот где мы достигаем
DbContext
:
var object_context = GetObjectContext(entity)
Применение:
var tableName = entity.GetTableName();
Вот версия, предполагающая, что у вас есть контекст и есть выделенная сущность в памяти, для которой вам нужно найти реальное имя таблицы.
открытый статический класс ObjectContextExtentions { открытая статическая строка TableNameFor (этот контекст ObjectContext, запись ObjectStateEntry) { var generic = context.GetType (). GetProperties (). ToList (). First (p => p.Name == entry.EntityKey.EntitySetName); var objectset = generic.GetValue (context, null); var method = objectset.GetType (). GetMethod ("ToTraceString"); var sql = (String) method.Invoke (objectset, null); var match = Regex.Match (sql, @ "FROM \ s + \ [dbo \] \. \ [(?[^ \]] +) \]", RegexOptions.Multiline); if (match.Success) { return match.Groups ["TableName"]. Значение; } throw new ArgumentException("Невозможно найти имя таблицы."); } }
Алекс прав - это печальное ограничение в API метаданных. Я должен просто загрузить MSL как XML-документ и выполнить поиск объектов S-пространства, когда я обрабатываю свою модель C-пространства.
Для EF6 смешивание / сжатие кода из других ответов здесь и вокруг (VB, извините):
Public Function getDBTableName(data As myDataModel, ByVal entity As Object) As String
Dim context = CType(data, IObjectContextAdapter).ObjectContext
Dim sName As String = entity.GetType.BaseType.Name 'use BaseType to avoid proxy names'
Dim map = context.MetadataWorkspace.GetItems(Of EntityContainerMapping)(DataSpace.CSSpace).FirstOrDefault
Return (From esmap In map.EntitySetMappings
Select esmap.EntityTypeMappings.First(
Function(etm)
etm.EntityType.Name = sName
).Fragments.First.StoreEntitySet.Name).FirstOrDefault
'TODO: use less .first everywhere but filter the correct ones'
End Function
Это работает для db-first.
Относительно легко понять следующий файл.edmx.
Если вы используете классы модели с[Table("table_name")]
аннотация, вы можете использовать это. Если тип не имеет[Table]
аннотация возвращает имя класса, поскольку это оно по умолчанию.
using System.ComponentModel.DataAnnotations.Schema;
...
private string GetTableName(Type type)
{
if (Attribute.IsDefined(type, typeof(TableAttribute)))
{
var attr = (TableAttribute)Attribute.GetCustomAttribute(type, typeof(TableAttribute));
return attr.Name;
}
else return type.Name;
}
Копирование моего ответа на другой вопрос здесь.
Если кто-то еще смотрит, вот как я это сделал. Это метод расширения для DBContext, который принимает тип и возвращает имена физических столбцов и их свойства.
При этом используется контекст объекта для получения списка физических столбцов, а затем используется свойство метаданных "PreferredName" для сопоставления каждого столбца с его свойством.
Поскольку он использует контекст объекта, он инициирует соединение с базой данных, поэтому первый запуск будет медленным в зависимости от сложности контекста.
public static IDictionary<String, PropertyInfo> GetTableColumns(this DbContext ctx, Type entityType)
{
ObjectContext octx = (ctx as IObjectContextAdapter).ObjectContext;
EntityType storageEntityType = octx.MetadataWorkspace.GetItems(DataSpace.SSpace)
.Where(x => x.BuiltInTypeKind == BuiltInTypeKind.EntityType).OfType<EntityType>()
.Single(x => x.Name == entityType.Name);
var columnNames = storageEntityType.Properties.ToDictionary(x => x.Name,
y => y.MetadataProperties.FirstOrDefault(x => x.Name == "PreferredName")?.Value as string ?? y.Name);
return storageEntityType.Properties.Select((elm, index) =>
new {elm.Name, Property = entityType.GetProperty(columnNames[elm.Name])})
.ToDictionary(x => x.Name, x => x.Property);
}
Чтобы использовать его, просто создайте вспомогательный статический класс и добавьте вышеуказанную функцию; тогда это так же просто, как звонить
var tabCols = context.GetTableColumns(typeof(EntityType));