如何找到与导航 属性 相关的 ID 属性 或属性?

How can I find the Id property or properties related to a navigational property?

对于我正在使用 Entity Framework 的项目,我希望能够枚举给定对象实例的所有导航属性(假设它是由 EF 生成的对象)。从那里我想为每个导航 属性.

获取相关的 Id 属性

例如,如果我获得 class Person 的实例,我希望能够找到它的导航属性 AddressBoss。对于这两个导航属性,我想 "lookup" 相关的 Id 属性称为 AddressIdBossId.

我需要这些 Id 属性,这样我就可以 运行 在不同的数据库上查询,该数据库没有相同的外键但具有完全相同的 ID。

到目前为止,我已经想出了一种方法来为 EF 生成的随机对象实例获取 RelationshipManager。在调试时,我可以通过 Manager 的 Relationships 属性 获取外键关系。但我只能得到导航 属性 名称。所以我可以看到有一个 FK_Person_Address 与名为 Address 的导航 属性 有关,但我找不到 AddressId.

所以我的问题是,我如何动态地(不知道 Person class' 布局)发现 AddressId 属性 与 Address?

我知道外键关系可能在关系的另一端有 ID 属性(Boss 指向 Person 而不是 Person一个 BossId)。在那种情况下,我仍然想在检查 Person.

的实例时发现 Boss 有一个 PersonId

这里有一个方法可以returns知道实体对象的键值:

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 包含实体的键值作为键值对。 (具有复合键的实体具有多个键值。)

这将为您提供一个字典,其中所有导航属性作为键,所有相关属性作为值(值可能是来自其他实体的 属性)

将这些添加到您的 DBContext class 并调用 db.GetForeignKeyProperties<Person>()

结果将类似于:

"Address" - "AddressID"

"Boss" - "Person.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;
}