37

How do I find which property of a class is the primary key of the Entity Framework Code First entity POCO?

Please note 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.

5 Answers 5

63

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.

Sign up to request clarification or add additional context in comments.

3 Comments

Thx. marked as answer. I do like to comment that It's a better idea to add an interface to your context class (eg, IYourDataContext) and inherit the IObjectContextAdapter to that IYourDataContext. That way you don't have to cast it.
Given this is 5 old now, is there a way to do this without heavy reflection? The context of my question is an attempt to implement a Find method without tracking, such as SingleOrDefault() but in a base class that cannot assume to know the entity's key name. Thanks guys.
Instead of finding the primary key, is there a way to figure out which properties have (unique) indexes on them?
28

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.

2 Comments

Thanx, You saved my life!
Instead of finding the primary key, is there a way to figure out which properties have (unique) indexes on them?
14

In EF 6.1 there is a Db() extension method that makes this easier.

Example:

public static IEnumerable<string> GetPrimaryKeyPropertyNames(DbContext db, Type entityType)
{
    return db.Db(entityType).Pks.Select(x => x.PropertyName);
}

6 Comments

How's its performance though? Heavy with underlying reflection?
I have no idea, EntityFramework is to my knowledge based on a lot of reflection to begin with.
The linked package is obsolete and is no longer listed on NuGet
Thanks for pointing that out, it's likely obsoleted by a newer version of EF.
@anjdreas Or it's because it now costs $700.
|
9

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;
        }

Comments

4

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);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.