Given an entity, I would like to retrieve the primary keys of the related entities in a generic way. For instance: for a Customer, I would like to have a list of ID's of its Orders.
Current signature of my attempt:
GetPrimaryKeysOfRelatedEntities(DbContext db, object entity)
I'm able to retrieve the PK of an entity, and I'm able to retrieve navigation properties of an entity... but I can't get the PK of the navigation properties of an entity.
I'm missing a small link here!
Here's my code to get the Keys of entities, which doesn't work for navigation properties
private static IEnumerable<string> GetEntityType(DbContext db, Type entityType)
{
entityType = ObjectContext.GetObjectType(entityType);
var metadataWorkspace = ((IObjectContextAdapter)db).ObjectContext.MetadataWorkspace;
var objectItemCollection = (ObjectItemCollection)metadataWorkspace.GetItemCollection(DataSpace.OSpace);
ReadOnlyCollection<EntityType> entityTypes = metadataWorkspace.GetItems<EntityType>(DataSpace.OSpace);
if (entityTypes == null)
{
throw new InvalidOperationException();
}
var ospaceType = entityTypes.SingleOrDefault(t => objectItemCollection.GetClrType(t) == entityType);
if (ospaceType == null)
{
throw new ArgumentException(
string.Format("The type '{0}' is not mapped as an entity type.", entityType.Name), "entityType");
}
return ospaceType.KeyMembers.Select(k => k.Name);
}
This code uses EntityType, and I propably should use something else, but I'm not sure what.
I can't see the problem, you have a great piece of code which can get primary keys of an entity by its type.
You can use a reflection to enumerate properties of your entity type and call your code for type of these properties. For ex like this:
var entityType = entity.GetType(); // or another type source
foreach (var prop in type.GetProperties())
{
if (prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericArguments().Any(x => x.Assembly == type.Assembly))
{
var navPropType = prop.PropertyType.GetGenericArguments().First(x => x.Assembly == type.Assembly);
var keysForThisNavPropType = GetEntityType(db, navPropType);
}
else if (prop.PropertyType.Assembly == type.Assembly)
{
var keysForThisNavPropType = GetEntityType(db, prop.PropertyType);
}
}
As you can see the criteria for finding nav properties is that its containing assambly is the same as your primary type.
Edit
Ok, try this:
// we need DbContext vaule
var db = YOUR_DB_CONTEXT; // assing db context here
// So we can get ObjectContext instance
var ctx = ((IObjectContextAdapter) db).ObjectContext;
// we need some entity to check
object entity = someYourEntity; // assign your entity here
// let's get its type
var type = entity.GetType();
// helper function to get set name
Func<Type, ObjectContext, string> getEntitySetByObjectType = (t, context) =>
{
var container =
context.MetadataWorkspace.GetEntityContainer(context.DefaultContainerName, DataSpace.CSpace);
var entitySet =
container.BaseEntitySets.First(item => item.ElementType.Name.Equals(t.Name));
return container.Name + "." + entitySet.Name;
};
// go through the entity's properties
foreach (var prop in type.GetProperties())
{
// nav properties which are collections
if (prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericArguments().Any(x => x.Assembly == type.Assembly))
{
var val = (IEnumerable)prop.GetValue(entity);
if (val != null)
{
// get value and check if it is not null
string setName = null;
// go through collection values
foreach (var obj in val)
{
if (setName == null)
setName = getEntitySetByObjectType(obj.GetType(), ctx);
// get primary key values
var entityKey = ctx.CreateEntityKey(setName, obj);
Console.WriteLine(entityKey);
}
}
}
// nav props which are single objects
else if (prop.PropertyType.Assembly == type.Assembly)
{
// get value and check if it is not null
var val = prop.GetValue(entity);
if (val != null)
{
// get primary key values
var entityKey = ctx.CreateEntityKey(getEntitySetByObjectType(prop.PropertyType, ctx), val);
Console.WriteLine(entityKey);
}
}
}
CreateEntityKey method returns object of EntityKey class (http://msdn.microsoft.com/ru-ru/library/system.data.entitykey(v=vs.110).aspx) which has EntityKeyValues property which is an array of EntityKeyMember. EntityKeyMember has properties Key and Value which are exactly primary key name and value.