【问题标题】:How to get table name from DbContext at runtime如何在运行时从 DbContext 获取表名
【发布时间】:2011-11-26 02:13:20
【问题描述】:
我试图在运行时从 DbContext 获取数据库表名,我发现 posts 谈到在 EF 4.x 中无法获取 SSpace 项目(以及表名)默认情况下还没有用于这些操作的公共 API。
但我尝试对此进行一些测试,并且我能够在运行时使用调试器从 DbContext 获取表名
此语法由 Visual Studio 生成
((System.Data.Entity.DbContext)(context)).System.Data.Entity.Infrastructure.IObjectContextAdapter.ObjectContext.MetadataWorkspace._itemsSSpace
我做了一些修改,使它在代码中可用,并屈服于这个
var objContext = (context as IObjectContextAdapter).ObjectContext;
var metaData = objContext.MetadataWorkspace;
var items = metaData.GetItems(DataSpace.SSpace);
虽然它遵循相同的目标,但 items 抛出异常 The space 'SSpace' has no associated collection.
- 首先:为什么会出现这个错误。
- 二:有什么办法可以得到这个SSpcae项,或者表名??
【问题讨论】:
标签:
entity-framework
entity-framework-4
entity-framework-4.1
【解决方案1】:
这是运行时的一种方式。
public static List<string> GetTableNames()
{
List<string> tableNameList = new List<string>();
// use DBContext to get ObjectContext
DatabaseContext db = new DatabaseContext();
IObjectContextAdapter adapter = db as IObjectContextAdapter;
System.Data.Objects.ObjectContext objectContext = adapter.ObjectContext;
ReadOnlyCollection<EntityType> allTypes = objectContext.MetadataWorkspace.GetItems<EntityType>(DataSpace.CSpace);
foreach (EntityType item in allTypes)
{
// Create full assembly name
string typeName = "original.poco.namespace." + item.Name;
Type type = Type.GetType(typeName);
// wrap into a function
string sql = db.Set(type).ToString();
Regex regex = new Regex(@"FROM \[dbo\]\.\[(?<table>.*)\] AS");
Match match = regex.Match(sql);
tableNameList.Add( match.Groups["table"].Value);
}
return tableNameList;
}
【解决方案2】:
您会收到该错误,因为在您执行某些操作(执行查询)以使 EF 需要存储集合之前,不会填充存储集合。但即使你拥有它,它对你也没有好处。您可以通过这种方式获取表名列表,但到实体的映射位于 CSSpace 项目集合中,这是完全不可访问的,因为它使用 EF 运行时内部的类型。如果您有实体 A 和 B 以及表 C 和 D,即使您知道您有直接的一对一映射,您也无法确定 A 是映射到 C 还是映射到 D。
【解决方案3】:
这是今天为 Microsoft 的 Northwind 示例数据库开发的一些代码,演示了如何从实体数据模型中获取表列表,然后显示在 DataGridView 中选择的表中的前 x 行。
按原样运行以下代码:
- 创建一个 Windows 窗体应用程序
- 为 Northwind DB 添加实体数据模型,将其命名为“NorthwindModel”
- 添加两个组合框和一个 datagridview(tablesComboBox、topXComboBox 和 dataGridViewForOutput)
- 将项目添加到 topXComboBox(全部、前 5、前 10 等(必须是 5 的倍数才能使代码工作)
- 将表单名称更改为 mainForm
- 突出显示表单文件中的所有代码并替换为以下内容
using System;
using System.Data.Entity;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
namespace EntityFrameworkBrowser
{
public partial class mainForm : Form
{
public mainForm()
{
InitializeComponent();
}
private void mainForm_Load(object sender, EventArgs e)
{
Type type = typeof(NorthwindModelEntities);
var query = type.GetProperties().Where(p => p.GetMethod.ToString().ToLower().Contains(".dbset")).Select(m => m.Name);
tablesComboBox.DataSource = query.ToList();
topXComboBox.SelectedIndex = 1;
}
private async void BindTableData()
{
// Ensure the form has been initialised
if (topXComboBox.SelectedIndex.Equals(-1))
{
return;
}
// Get the DB context
NorthwindModelEntities dbContext = new NorthwindModelEntities();
// Get a reference to the type of the model
Type type = typeof(NorthwindModelEntities);
// Get the table name selected by the user in the combo box
string tableName = tablesComboBox.SelectedItem.ToString();
// Get a reference to the DbSet from the model
var prop = type.GetProperty(tableName);
// Get a reference to the getter for the DbSet
MethodInfo get = prop.GetMethod;
// Invoke the getter for the DbSet
object tableContent = get.Invoke(dbContext, null);
// Create a query that will return all records from the selected table
IQueryable query = (IQueryable)tableContent;
// Find out how many records the user has requested, All, Top5, Top 10, etc
int count = topXComboBox.SelectedIndex * 5;
// If a value other than all (selected index 0) has been selected the query needs to be refactored
if (count != 0)
{
// Get the element type for the DbSet from the entity data model
Type returnType = query.ElementType;
// Get a reference to the 'Take' extension method
MethodInfo takeMethod = (MethodInfo)typeof(Queryable).GetMethod("Take");
// Make a generic version of the 'Take' method
MethodInfo m = takeMethod.MakeGenericMethod(returnType);
// Refactor the query to take the top X records based on the user combo box selection
query = (IQueryable)m.Invoke(null, new object[] { query, count });
}
// Execute the query and bind the results to the data grid view
dataGridViewForOutput.DataSource = await query.ToListAsync();
}
private void tablesComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
BindTableData();
}
private void topXComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
BindTableData();
}
}
}
希望 cmets 应该解释发生了什么,你不会猜到的。
希望它对某人有所帮助,因为它需要一些锻炼
:0)
【解决方案4】:
我到处都在使用这些类;应该证明是有帮助的。与 EF4 或 5 一起使用。要让 ObjectContext 从 DbContext 传递到顶级 EntityNavigationList.List,请使用 ((IObjectContextAdapter)myDbContext).ObjectContext。表名的简单列表将是 EntityNavigationList.List 中每个项目的 FromEntity。
public class NavigationItem
{
public string FromEntity { get; set; }
public string ToEntity { get; set; }
public RelationshipMultiplicity FromMultiplicity { get; set; }
public RelationshipMultiplicity ToMultiplicity { get; set; }
public string ForeignKeyColumn { get; set; }
public string PrimaryKeyColumn { get; set; }
public EntitySetBase EntitySet { get; set; }
/// <summary>
/// Entity type may be "Formula"; entity set may be "Formulae"
/// </summary>
public string EntitySetName { get { return EntitySet == null ? string.Empty : EntitySet.Name; } }
}
public static class EntityNavigationList
{
private static List<NavigationItem> _navItems;
public static List<NavigationItem> List( ObjectContext context )
{
if ( _navItems == null )
{
InitializeNavigationItems( context );
}
return _navItems;
}
/// <summary>
/// Create a list of all navigation items in the model
/// </summary>
private static void InitializeNavigationItems( ObjectContext context )
{
var entityMetadata = context.MetadataWorkspace.GetItems( DataSpace.CSpace );
var entitySetMetadata = context.MetadataWorkspace.GetEntityContainer( context.DefaultContainerName, DataSpace.CSpace ).BaseEntitySets;
var query = from meta in entityMetadata
.Where( m => m.BuiltInTypeKind == BuiltInTypeKind.EntityType )
from p in ( meta as EntityType ).NavigationProperties
let foreignKey = ( p.ToEndMember.DeclaringType as AssociationType ).ReferentialConstraints.FirstOrDefault()
let primaryKey = ( meta as EntityType ).KeyMembers
select new NavigationItem
{
FromEntity = p.FromEndMember.Name,
ToEntity = p.ToEndMember.Name,
FromMultiplicity = p.FromEndMember.RelationshipMultiplicity,
ToMultiplicity = p.ToEndMember.RelationshipMultiplicity,
ForeignKeyColumn = foreignKey == null ? string.Empty : foreignKey.ToProperties.First().ToString(),
PrimaryKeyColumn = primaryKey == null ? string.Empty : primaryKey.First().Name,
/// We need the corresponding entity set so we can get it's name (formula belongs to entity set "formulae")
EntitySet = ( from moMeta in entitySetMetadata
where moMeta.ElementType.Name == p.FromEndMember.Name
select moMeta ).FirstOrDefault()
};
_navItems = query.ToList();
}
}