For a project I'm working with Entity Framework and I'd like to be able to enumerate all navigational properties for a given object instance (assuming it's an object generated by EF). From there I'd like to get the related Id property for every navigational property.
For example, if I get an instance of the class Person
, I want to be able to find it's navigational properties called Address
and Boss
. For those two navigational properties I want to then "lookup" the related Id properties called AddressId
and BossId
.
I need those Id properties so I can run queries on a different database which does not have the same foreign keys but does have exactly the same Ids.
So far I have figured out a way to get the RelationshipManager for a random object instance generated by EF. And while debugging I can get to the foreign key relations via the Manager's Relationships
property. But I can only get as far as the navigational property name. So I can see there's a FK_Person_Address
which is related to the navigational property called Address
but I can't find the AddressId
.
So my question is, how can I dynamically (with no knowledge of the Person
class' layout) discover the AddressId
property which is related to Address
?
I am aware the Foreign Key relationship might have the Id property on the other side of the relation (Boss
pointing to Person
in stead of Person
having a BossId
). In that case, I'd still like to discover that Boss
has a PersonId
when I'm inspecting an instance of Person
.
This will give you a dictionary with all navigation properties as Key and all related properties as Value (the value might be a property from the other entity)
Add these to your DBContext class and call db.GetForeignKeyProperties<Person>()
The result will be something like:
"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;
}
Here's a method that returns key values of know entity objects:
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));
}
This method uses the underlying ObjectContext
API to obtain ObjectStateEntry
objects that belong to each entity object. The EntityKey
contains the key value(s) of an entity as a key-value pair. (Entities with composite keys have more than one key value.)