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})中,他的代码将不起作用.
我已经为对象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;
    }
}
这里有更多细节:
实体框架:从实体获取映射表名
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);
}
NB有计划改进Metadata API,如果这没有得到我们想要的,那么我们可以看看类型和表之间的EF Code First Mapping
这里的大多数答案不适用于派生类。这个可以。并为您提供架构。我结合了这里的答案并对其进行了一些改进(通过取出 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;
    }
在实体框架核心版本 3.0 和 3.1 中,这是相当简单的:
var myClassTableName = _context.Model
  .FindEntityType(typeof(MyClass)).GetTableName();
不,遗憾的是,使用元数据API无法获取给定实体的表名.
这是因为Mapping元数据不是公共的,因此无法使用EF的API从C-Space转到S-Space.
如果你真的需要这样做,你总是可以通过解析MSL来自己构建地图.这不适合胆小的人,但它应该是可能的,除非你使用的是QueryViews(这是非常罕见的),在这一点上它是不可能的所有意图和目的(你必须解析ESQL ...唉! )
Alex James
微软.
小智 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)); 
    } 
如果您在 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;
    }
当您使用注释显式告诉 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 没有帮助,但鉴于问题的标题,人们最终可能会在这里寻找这个答案。)
| 归档时间: | 
 | 
| 查看次数: | 57202 次 | 
| 最近记录: |