Получить имя таблицы базы данных из метаданных Entity Framework

Я пытаюсь найти способ получить имя базовой таблицы SQL для данного типа сущности. Я экспериментировал с запросами MetadataWorkspace, и, хотя я могу получить много информации от объекта или пространства хранения, я не могу понять, как их сопоставить.

Скажем, у меня есть тип в объектной модели под названием «Поиск» - как мне найти имя таблицы (wws_lookups) в базе данных?

Я могу запросить все объекты EntityType для CSpace и SSpace, и я вижу, что оба они указаны правильно, но я не могу понять, как получить SSpace из CSpace.

Есть какой-либо способ сделать это?


person Rick Strahl    schedule 13.12.2009    source источник
comment
Следуйте за ответом в другом месте: stackoverflow.com/a/27216884/819843   -  person Motlicek Petr    schedule 30.11.2014


Ответы (25)


Я использую подход Найджела (извлечение имени таблицы из .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: получить имя сопоставленной таблицы из объекта

person Rui Jarimba    schedule 18.03.2012
comment
Когда я пробую, ваш ответ возвращает пустую строку. И регулярное выражение, предоставленное @Dabblernl, вызывает ошибку: parsing "FROM\s+[?(?<table>[A-Za-z0-9]*)]?\s+AS" - Too many )'s. Мой sql выглядит так SELECT [Extent1].[ID] AS [ID], [Extent1].[Text] AS [Text] FROM [dbo].[Questions] AS [Extent1] - person Colin; 23.09.2013
comment
Замена int startIndex = sql.IndexOf("FROM ") + 5; int endIndex = sql.IndexOf(" AS", startIndex); return sql.Substring(startIndex, endIndex - startIndex); на регулярное выражение дает мне [dbo].[Questions] - что соответствует моим целям - person Colin; 23.09.2013
comment
Но метод не работает, если передать ему производный тип: There are no EntitySets defined for the specified entity type 'Models.ReplacementCost'. If 'Models.ReplacementCost' is a derived type, use the base type instead - person Colin; 23.09.2013
comment
@Colin: вы правы, на данный момент не работает, если у вас следующий формат: schema.table. Я постараюсь исправить регулярное выражение как можно скорее - person Rui Jarimba; 23.09.2013
comment
@Colin: исправлено регулярное выражение для включения схемы. Что касается производных типов - боюсь, я не могу вам помочь с этой конкретной проблемой :( - person Rui Jarimba; 23.09.2013
comment
@RuiJarimba Как насчет этого, пожалуйста: string s = typeof (T) .ToString (); вернуть s.Substring (s.LastIndexOf ('.') + 1); - person Lei Yang; 03.01.2014
comment
@LeiYang: Не очень хорошая идея, IMO, потому что имя типа может не совпадать с именем таблицы. Кроме того, он не принимает во внимание схему таблицы, например. dbo.Foo - person Rui Jarimba; 03.01.2014
comment
Хорошее решение, но не работает для производных типов. Он выдает ошибку, предлагая вместо этого использовать базовый тип, что не поможет. Я расширил для него dbset, используя ваше решение public static string GetTableName<T>(this DbSet<T> dbSet) where T : class { string sql = dbSet.ToString(); Regex regex = new Regex("FROM (?<table>.*) AS"); Match match = regex.Match(sql); string table = match.Groups["table"].Value; return table; } - person Sean Missingham; 08.06.2017
comment
Привет, @SeanMissingham, имейте в виду, что моему ответу 5 лет :-) Я предлагаю вам опубликовать свое решение в качестве нового ответа, это может быть кому-то полезно. - person Rui Jarimba; 08.06.2017

ИЗМЕНИТЬ. Этот ответ теперь устарел из-за новой функции в EF 6.1: сопоставление типов таблиц. Иди туда первым!

У меня возникла проблема с другими ответами, потому что у меня есть производный тип. У меня есть этот метод (внутри моего класса контекста) для работы - на данный момент у меня только один уровень наследования в моей модели

private readonly static Dictionary<Type, EntitySetBase> _mappingCache 
       = new Dictionary<Type, EntitySetBase>();

private EntitySetBase GetEntitySet(Type type)
{
    //If it's a proxy, get the entity type associated with it
    type = ObjectContext.GetObjectType(type);

    if (_mappingCache.ContainsKey(type))
        return _mappingCache[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 Between Type & Таблицы

person Colin    schedule 23.09.2013
comment
Я независимо наткнулся на это, прочитав множество ответов, таких как Алекса Джеймса, на различные варианты этого вопроса в Интернете. Просто они не могут пройти сопоставление, чтобы увидеть, что единственное требование - имя таблицы? Или есть какой-то серьезный недостаток или ограничение, которое делает этот код неработоспособным, и его следует считать неправильным? Во всяком случае, это подтверждает мой ответ, спасибо, Колин. - person Marc L.; 02.10.2013
comment
Я не вижу свойств схемы и таблицы - person Alan Macdonald; 17.10.2013
comment
@AlanMacdonald Извините, я не понял, что они доступны только в EF6 entityframework.codeplex.com/SourceControl/latest#src/ - person Colin; 17.10.2013
comment
@AlanMacdonald Виктор Зихла взял мой код и изменил его, чтобы использовать кеш. Похоже, его версия будет работать на EF5, но он удалил проверку для BaseType.Name, поэтому, возможно, вы можете использовать комбинацию из 2. wiktorzychla.com/2013/10/soft-delete-pattern-for-entity.html - person Colin; 17.10.2013
comment
@AlanMacdonald см. Мое редактирование. Теперь включает кеширование Виктора - person Colin; 17.10.2013
comment
Я думаю, это работает только тогда, когда имя таблицы совпадает с именем объекта. Что, если вы сопоставите сущность User с таблицей T_User? - person Jowen; 24.12.2013
comment
@Jowen, нет, в этом случае тоже работает. Вы это проверяли? - person Colin; 24.12.2013
comment
Мне не хватает метода GetObjectType (type) ?! - person Jowen; 24.12.2013
comment
Я воспользовался методом Ромиллера (из ссылки) и он отлично сработал! :) - person JoaoPauloPaschoal; 14.01.2019

Большинство ответов здесь не работают с производными классами. Этот делает. И также дает вам схему. Я объединил ответы здесь и улучшил это немного (вынув такие вещи, как 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;
    }
person N73k    schedule 01.10.2018
comment
Это чудовищно с точки зрения длины и сложности только для того, чтобы получить схему и имя таблицы, но против большинства ответов здесь я должен признать, что для моего EDMX в EF6 он РАБОТАЕТ ОТЛИЧНО. Спасибо. - person AFract; 25.10.2018

Нет, к сожалению, с помощью API метаданных невозможно получить имя таблицы для данной сущности.

Это связано с тем, что метаданные сопоставления не являются общедоступными, поэтому нет возможности перейти из C-Space в S-Space с помощью API EF.

Если вам действительно нужно это сделать, вы всегда можете построить карту самостоятельно, проанализировав MSL. Это не для слабонервных, но это должно быть возможно, если вы не используете QueryViews (которые невероятно редки), в этот момент это практически невозможно (вам придется разбирать ESQL ... ах!)

Алекс Джеймс

Microsoft.

person Alex James    schedule 13.12.2009
comment
Это кажется действительно глупым ограничением, учитывая, что вы можете просматривать данные хранилища. Облом. Спасибо, Алекс. - person Rick Strahl; 13.12.2009
comment
Этот ответ по-прежнему актуален для более новых версий Entity Framework? - person ken2k; 11.05.2012
comment
сегодня столкнулся с этим ограничением, действительно глупое бизнес-решение! - person Elisabeth; 24.10.2017

Есть способ удалить данные с помощью 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)); 
    } 
person Nigel Findlater    schedule 29.04.2010
comment
Я пробовал ваше решение в LinqPad, но откуда вы взяли ObjectQuery? Это из пакета NUGET? В предоставленной вами ссылке я не нашел намеков на это. Пожалуйста помоги. - person Matt; 02.03.2021

Если вы выполняете код сначала в 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;
    }
person Eric Bynum    schedule 17.01.2015
comment
Это, пожалуй, самое элегантное и простое решение, которое я когда-либо видел. Хотел бы я проголосовать больше. - person caliche2000; 12.02.2021

Если вы используете шаблон 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();

////////////////////////////////////////////////////////////////////////////////
person Shayne Boyer    schedule 03.06.2011
comment
Спасибо, вы спасли мне день :) - person Hieu Nguyen Trung; 18.08.2017

Добавление еще одного ответа для особого случая, когда вы используете аннотации, чтобы явно указать 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;
}

(Я знаю, что это не поможет OP, но, учитывая название вопроса, здесь могут оказаться люди, которые могут искать этот ответ.)

person Jack Miller    schedule 19.11.2018
comment
Идеально, именно то, что я искал! Спасибо! - person user1106591; 05.06.2020

Возможный обходной путь (не лучший, но и альтернативы ...):

var sql = Context.EntitySetName.ToTraceString();

... затем проанализируйте SQL, который должен быть довольно простым.

person Craig Stuntz    schedule 14.12.2009

Вот что я смог придумать, используя 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();
person Jon Miller    schedule 05.05.2012

Лучше использовать StoreItemCollection из метаданных. Этот парень уже привел пример его использования: Получить таблицы и отношения

person King Mikey    schedule 12.11.2012

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;
    }
person Motlicek Petr    schedule 30.11.2014
comment
Пожалуйста, не повторяйте ответы. Если на два вопроса можно ответить одним и тем же ответом, вы должны отметить один из них как повторяющийся. - person Gert Arnold; 30.11.2014
comment
Да, для этого вам нужно больше репутации, но, НАВСЕГДА, вы всегда можете пометить вопрос для внимания модератора. Оставить ссылку на другой ответ (как и вы) - хорошая альтернатива. Это может побудить других пользователей отметить дубликат. SO просто немного опасается повторяющегося контента, потому что это может вызвать отвлечение (любой может редактировать любой ответ ...). - person Gert Arnold; 01.12.2014

Использовать EF Core с SQL-сервером так же просто, как:

_context.Model.FindEntityType(YOUR_VAR_TYPE).SqlServer().TableName
person user3413723    schedule 22.11.2018
comment
Postgres с использованием Npgsql _context.Model.FindEntityType (YOUR_VAR_TYPE) .Npgsql (). TableName Package: (Npgsql.EntityFrameworkCore.PostgreSQL) - person Rodrigo Caballero; 06.08.2020

Вот еще один способ найти имя таблицы. Это немного странно, но работает. 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
person mike    schedule 20.12.2010

Вы можете попробовать расширение MappingAPI: https://efmappingapi.codeplex.com/

Действительно легко использовать

context.Db<YourEntityType>().TableName
person maxlego    schedule 24.02.2014

В entity-framework-core версий 3.0 и 3.1 это скорее trivial:

var myClassTableName = _context.Model.FindEntityType(typeof(MyClass)).GetTableName();
person Nae    schedule 27.09.2020

Вот версия, предполагающая, что у вас есть контекст и выбранный объект в памяти, для которого вам нужно найти настоящее имя таблицы.

    
    public static class ObjectContextExtentions
    {
        public static string TableNameFor(this ObjectContext context, ObjectStateEntry entry)
        {
            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\]\.\[(?<TableName>[^\]]+)\]", RegexOptions.Multiline);
            if (match.Success)
            {
                return match.Groups["TableName"].Value;
            }

            throw new ArgumentException("Unable to find Table name.");
        } 
    }
person Leblanc Meneses    schedule 06.02.2011

На самом деле я столкнулся с той же проблемой и создал абстрактный фрагмент кода, который дает вам два _1 _ ($ table_name, $ columns_name_list). Первый имеет базу данных Таблица + список имен столбцов, второй имеет локальные свойства EF Entities +

Конечно, вы можете добавить больше проверок по типу данных, кстати, имхо, что заставит вас написать безумно сложный код.

P&L

P.S. Простите за сжатый стиль, я фанатик лямбда

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());
}
person sinsalabim    schedule 21.12.2016

В случае использования 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;
    }
}

У вас должен быть установлен пакет Microsoft.EntityFrameworkCore.Relational Nuget, чтобы можно было использовать метод Relational ().

person Ehsan Mirsaeedi    schedule 24.10.2018

Если вам нужен только быстрый и грязный способ получить имя таблицы (и он вам не нужен в коде вашего приложения), вы можете посмотреть XML, сгенерированный для вашего Model.edmx. Найдите свою сущность в разделе edmx: Mappings, и строка: MappingFragment StoreEntitySet = "YourTableName" даст вам фактическое имя таблицы.

person B. Gaither    schedule 25.04.2019

Вы можете использовать метод расширения, чтобы получить имя таблицы из вашего класса сущности
В этом случае вам не понадобится DbContext для получения имени таблицы, он получает его внутри метода расширения

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();
person Abolfazl    schedule 20.04.2021

Алекс прав - это печальное ограничение в Metadata API. Мне нужно просто загрузить MSL как XML-документ и выполнять поиск сущностей S-пространства, когда я обрабатываю свою модель C-пространства.

person codekaizen    schedule 13.12.2009

Используя 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);
}
person Fábio Augusto Pandolfo    schedule 14.05.2015

Копирую здесь свой ответ на другой вопрос.

Если кто еще ищет, вот как я это сделал. Это метод расширения для 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));
person Mahmoud Hanafy    schedule 18.09.2017
comment
Вопрос был в том, как получить имя таблицы, а не столбцов. Кроме того, я извлек начало вашего кода (до .Single), и, по-видимому, он не работает, если имя типа объекта отличается от имени таблицы. - person AFract; 25.10.2018

Для 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.

person Ivan Ferrer Villa    schedule 30.09.2015