从实体框架MetaData获取数据库表名称

Ric*_*ahl 48 .net entity-framework database-metadata

我试图找到一种方法来获取给定实体类型的基础SQL表名称.我已经尝试过使用MetadataWorkspace查询,虽然我可以从对象或存储空间获取大量信息,但我似乎无法弄清楚如何在两者之间进行映射.

所以说我在对象模型中有一个名为Lookup的类型 - 如何在数据库中找到tablename(wws_lookups)?

我可以查询CSpace和SSpace的所有EntityType对象,我可以看到两者都正确列出,但我无法弄清楚如何从CSpace获取SSpace.

有没有办法做到这一点?

Rui*_*mba 38

我使用Nigel的方法(从中提取表名.ToTraceString())但进行了一些修改,因为如果表不在默认的SQL Server模式(dbo.{table-name})中,他的代码将不起作用.

我已经为对象DbContextObjectContext对象创建了扩展方法:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这里有更多细节:
实体框架:从实体获取映射表名


Col*_*lin 25

编辑 由于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);
}
Run Code Online (Sandbox Code Playgroud)

NB有计划改进Metadata API,如果这没有得到我们想要的,那么我们可以看看类型和表之间的EF Code First Mapping


N73*_*73k 7

这里的大多数答案不适用于派生类。这个可以。并为您提供架构。我结合了这里的答案并对其进行了一些改进(通过取出 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;
    }
Run Code Online (Sandbox Code Playgroud)

  • 仅仅为了获得表模式和名称,就长度和复杂性而言,这太可怕了,但与这里的大多数答案相反,我应该承认,对于我在 EF6 中的 EDMX,它运行得非常完美。谢谢。 (3认同)

Nae*_*Nae 7

版本 3.0 和 3.1 中,这是相当简单的

var myClassTableName = _context.Model
  .FindEntityType(typeof(MyClass)).GetTableName();
Run Code Online (Sandbox Code Playgroud)


Ale*_*mes 6

不,遗憾的是,使用元数据API无法获取给定实体的表名.

这是因为Mapping元数据不是公共的,因此无法使用EF的API从C-Space转到S-Space.

如果你真的需要这样做,你总是可以通过解析MSL来自己构建地图.这不适合胆小的人,但它应该是可能的,除非你使用的是QueryViews(这是非常罕见的),在这一点上它是不可能的所有意图和目的(你必须解析ESQL ...唉! )

Alex James

微软.

  • 这个答案对于较新版本的Entity Framework仍然有效吗? (4认同)
  • 这似乎是一个非常愚蠢的限制,因为您实际上可以浏览存储数据.游民.谢谢Alex. (3认同)

小智 6

有一种方法可以使用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)); 
    } 
Run Code Online (Sandbox Code Playgroud)


Eri*_*num 5

如果您在 EF6 中执行 codefirst,您只需将以下内容添加到您的 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;
    }
Run Code Online (Sandbox Code Playgroud)


Jac*_*ler 5

当您使用注释显式告诉 EF 使用哪个表名时,为特殊情况添加另一个答案。例如,如果您有:

[Table("tblCompany")]
public class Company
{
}
Run Code Online (Sandbox Code Playgroud)

您可以轻松访问TableAttribute注释以查找表名:

((System.ComponentModel.DataAnnotations.Schema.TableAttribute)typeof(YourNamespace.BO.Company).GetCustomAttribute(typeof(System.ComponentModel.DataAnnotations.Schema.TableAttribute))).Name
Run Code Online (Sandbox Code Playgroud)

这是tblCompany给定的样本。

作为即用型方法:

using System.Reflection;
using System.ComponentModel.DataAnnotations.Schema;

public static string GetTableName<T>() {
    return ((TableAttribute)typeof(T).GetCustomAttribute(typeof(TableAttribute))).Name;
}
Run Code Online (Sandbox Code Playgroud)

(我知道这对 OP 没有帮助,但鉴于问题的标题,人们最终可能会在这里寻找这个答案。)