对于一个项目,我正在使用实体框架,我希望能够枚举给定对象实例的所有导航属性(假设它是由EF生成的对象)。从那里,我想获得相关的Id属性为每个导航属性。
例如,如果我得到一个类Person
的实例,我希望能够找到它的导航属性Address
和Boss
。对于这两个导航属性,我想"查找"相关的Id属性AddressId
和BossId
。
我需要那些Id属性,这样我就可以在不同的数据库上运行查询,该数据库没有相同的外键,但确实具有完全相同的Id。
到目前为止,我已经找到了一种方法来获取由EF生成的随机对象实例的RelationshipManager。在调试时,我可以通过管理器的Relationships
属性获得外键关系。但我只能得到导航属性名。我可以看到这里有一个FK_Person_Address
它和导航属性Address
有关但是我找不到AddressId
所以我的问题是,我如何动态地(不知道Person
类的布局)发现与Address
相关的AddressId
属性?
我知道外键关系可能在关系的另一边具有Id属性(Boss
指向Person
,而不是Person
具有BossId
)。在这种情况下,当我检查Person
的实例时,我仍然想发现Boss
有PersonId
。
这将为您提供一个字典,其中所有导航属性为Key,所有相关属性为Value(该值可能是来自其他实体的属性)
将这些添加到DBContext类中并调用db.GetForeignKeyProperties<Person>()
结果类似于:
Address - AddressID
"老板"-"人。"BossID "
public Dictionary<string,string> GetForeignKeyProperties<DBType>()
{
EntityType table = GetTableEntityType<DBType>();
Dictionary<string, string> foreignKeys = new Dictionary<string, string>();
foreach (NavigationProperty np in table.NavigationProperties)
{
var association = (np.ToEndMember.DeclaringType as AssociationType);
var constraint = association.ReferentialConstraints.FirstOrDefault();
if (constraint != null && constraint.ToRole.GetEntityType() == table)
foreignKeys.Add(np.Name, constraint.ToProperties.First().Name);
if (constraint != null && constraint.FromRole.GetEntityType() == table)
foreignKeys.Add(np.Name, constraint.ToProperties.First().DeclaringType.Name+"."+constraint.ToProperties.First().Name);
}
return foreignKeys;
}
private EntityType GetTableEntityType<DBType>()
{
return GetTableEntityType(typeof(DBType));
}
private EntityType GetTableEntityType(Type DBType)
{
ObjectContext objContext = ((IObjectContextAdapter)this).ObjectContext;
MetadataWorkspace workspace = objContext.MetadataWorkspace;
EntityType table = workspace.GetEdmSpaceType((StructuralType)workspace.GetItem<EntityType>(DBType.FullName, DataSpace.OSpace)) as EntityType;
return table;
}
下面是一个返回已知实体对象的键值的方法:
IEnumerable<IDictionary<string,object>> GetKeyValues<T>(DbContext db,
IEnumerable<T> entities)
where T : class
{
var oc = ((IObjectContextAdapter)db).ObjectContext;
return entities.Select (e => oc.ObjectStateManager.GetObjectStateEntry(e))
.Select(objectStateEntry => objectStateEntry.EntityKey)
.Select(ek => ek.EntityKeyValues
.ToDictionary (x => x.Key, y => y.Value));
}
该方法使用底层ObjectContext
API获取属于每个实体对象的ObjectStateEntry
对象。EntityKey
以键值对的形式包含实体的键值。(具有组合键的实体有多个键值)