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;
}
}
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
这里的大多数答案不适用于派生类。这个可以。并为您提供架构。我结合了这里的答案并对其进行了一些改进(通过取出 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)
在实体框架核心版本 3.0 和 3.1 中,这是相当简单的:
var myClassTableName = _context.Model
.FindEntityType(typeof(MyClass)).GetTableName();
Run Code Online (Sandbox Code Playgroud)
不,遗憾的是,使用元数据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));
}
Run Code Online (Sandbox Code Playgroud)
如果您在 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)
当您使用注释显式告诉 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 没有帮助,但鉴于问题的标题,人们最终可能会在这里寻找这个答案。)
| 归档时间: |
|
| 查看次数: |
57202 次 |
| 最近记录: |