How do I find which property of a class is the primary key of the Entity Framework Code First entity POCO?
Please note开发者_如何学Go string matching for Id / class name + "Id" is a bad option. There must be some way to dig out the convention used by Entity Framework and reliably getting the key property.
Thanks in advance.
You can ask mapping metadata to get names of key properties (there can be more then one):
ObjectContext objectContext = ((IObjectContextAdapter)dbContext).ObjectContext;
ObjectSet<YourEntity> set = objectContext.CreateObjectSet<YourEntity>();
IEnumerable<string> keyNames = set.EntitySet.ElementType
.KeyMembers
.Select(k => k.Name);
Once you have key names you can use reflection to access their values.
As you can see the approach reverts back to ObjectContext API because DbContext API is only for simple scenarios where you don't bother with such details like mapping metadata.
In case it helps anyone, I needed to be able to do this without the knowing the type beforehand (so I couldn't easily do CreateObjectSet<YourEntity>()
because I didn't know YourEntity
), so I was able to adapt @Ladislav 's solution into the following:
// variable "type" is a System.Type passed in as a method parameter
ObjectContext objectContext = ((IObjectContextAdapter)this.context).ObjectContext;
IEnumerable<string> retval = (IEnumerable<string>)objectContext.MetadataWorkspace
.GetType(type.Name, type.Namespace, System.Data.Entity.Core.Metadata.Edm.DataSpace.CSpace)
.MetadataProperties
.Where(mp => mp.Name == "KeyMembers")
.First()
.Value;
Seems kind of odd that MetadataWorkspace.GetType
requires strings of the type name and namespace, instead of a System.Type, but that's the best I could find.
In EF 6.1 there is a Db()
extension method that makes this easier.
- Nuget: https://www.nuget.org/packages/EntityFramework.MappingAPI/ (v6.1.0.9)
- Namespace:
EntityFramework.MappingAPI.Extensions.MappingApiExtensions
Example:
public static IEnumerable<string> GetPrimaryKeyPropertyNames(DbContext db, Type entityType)
{
return db.Db(entityType).Pks.Select(x => x.PropertyName);
}
I had problem with both the above approaches because of Table Per Type inheritance. My working version (based on @S'pht'Kr's solution but using DataSpace.OSpace not DataSpace.CSpace for this reason) is below:
protected IEnumerable<string> GetKeyPropertyNames()
{
var objectContext = ((System.Data.Entity.Infrastructure.IObjectContextAdapter) this.Context).ObjectContext;
return GetKeyPropertyNames(typeof (TEntity), objectContext.MetadataWorkspace);
}
private static IEnumerable<string> GetKeyPropertyNames(Type type, MetadataWorkspace workspace)
{
EdmType edmType;
if (workspace.TryGetType(type.Name, type.Namespace, DataSpace.OSpace, out edmType))
{
return edmType.MetadataProperties.Where(mp => mp.Name == "KeyMembers")
.SelectMany(mp => mp.Value as ReadOnlyMetadataCollection<EdmMember>)
.OfType<EdmProperty>().Select(edmProperty => edmProperty.Name);
}
return null;
}
Like user rashleighp, I also wanted a variant of user Ladislav Mrnka's answer that only needs to know the type at runtime instead of needing to know the type at compile time. Also like user rashleighp, the solution of user S'pht'Kr didn't work for me, but his solution did work. Below, I contribute to this conversation by providing a simpler version of his answer that worked for me. However, I just learned about the solution by user anjdreas and that is the one that I will be using.
// variable "type" is a System.Type passed in as a method parameter
((IObjectContextAdapter)context)
.ObjectContext
.MetadataWorkspace
.GetItem<EntityType>(type.FullName, DataSpace.OSpace)
.KeyProperties
.Select(p => p.Name);
精彩评论