14

The answers I'm seeing here are for ObjectContext. Is there a property to determine an entity's primary key names when using DbContext?

Ah.. one of those times that I wish Entity Framework is open source! I can glean this primary key name information from .Find method :-)

Ladislav Mrnka
  • 360,892
  • 59
  • 660
  • 670
Green Lantern
  • 858
  • 10
  • 21

5 Answers5

26

You cannot use DbContext for that - DbContext API is just dumb wrapper with only most needed functionality. For everything more complex you must convert DbContext back to ObjectContext and use it. Try something like this:

Extract key names:

public static string[] GetEntityKeyNames<TEntity>(this DbContext context) where TEntity : class
{
  if (context == null)
    throw new ArgumentNullException("context");

  var set = ((IObjectContextAdapter)context).ObjectContext.CreateObjectSet<TEntity>();
  var entitySet = set.EntitySet;
  return entitySet.ElementType.KeyMembers.Select(k => k.Name).ToArray();
}

Here's a method that will extract the key values of an entity:

public static IEnumerable<object> GetEntityKeys<TEntity>(this DbContext context, TEntity entity)
  where TEntity : class
{
  if (context == null)
    throw new NullReferenceException("context");

  var type = typeof(TEntity);

  var set = ((IObjectContextAdapter)context).ObjectContext.CreateObjectSet<TEntity>();
  var entitySet = set.EntitySet;
  var keys = entitySet.ElementType.KeyMembers;
  var props = keys.Select(k => type.GetProperty(k.Name));
  return props.Select(p => p.GetValue(entity));
}
Shimmy Weitzhandler
  • 101,809
  • 122
  • 424
  • 632
Ladislav Mrnka
  • 360,892
  • 59
  • 660
  • 670
  • 2
    I don't think this will work if TEntity is derived class. Is there a way to do this without CreateObjectSet ? – Andrej K Jul 03 '13 at 21:24
  • 2
    In case anyone else tries this (EF 6) and receives a message saying: The type 'TEntity' must be a reference type in order to use it as parameter... it requires a `where TEntity : class` constraint. CreateObjectSet signature and def: http://msdn.microsoft.com/en-us/library/dn236948%28v=vs.113%29.aspx – Adam Plocher Mar 27 '14 at 22:47
  • Should we dispose the created ObjectSet ? – Guillaume Aug 18 '14 at 16:14
  • @Guillaume, no, it's not an `IDisposable`. – Shimmy Weitzhandler Mar 30 '15 at 06:08
4

The solution proposed by Ladislav Mrnka won't work for derived entities as You can't create an object set for a derived type. You'll see this error :

ArgumentException: There are no EntitySets defined for the specified entity type ... If ... is a derived type, use the base type instead. Parameter name: TEntity

Here is my solution avoiding creating an object set :

public string[] GetEntityKeyNames<TEntity>(DbContext context)
{
    if (context == null)
    {
        throw new ArgumentNullException("context");
    }
    var objectContext = ((IObjectContextAdapter)Context).ObjectContext;
    //We must use the namespace of the context and the type name of the entity
    string entityTypeName = context.GetType().Namespace + '.' + typeof(TEntity).Name;
    var entityType = objectContext.MetadataWorkspace.GetItem<EntityType>(entityTypeName, DataSpace.CSpace);
    return entityType.KeyProperties.Select(k => k.Name).ToArray();
}
Community
  • 1
  • 1
Guillaume
  • 12,824
  • 3
  • 40
  • 48
1

You could find primary key value from EntityKey class (http://msdn.microsoft.com/en-us/library/system.data.entitykey.aspx).

and you could find EntityKey object from DbContext like so :

ObjectContext context = ((IObjectContextAdapter)dbContext).ObjectContext;
EntityKey key = context.ObjectStateManager.GetObjectStateEntry(model).EntityKey;
Agus Syahputra
  • 436
  • 4
  • 12
1

Here is what I did to make sure that I got the key. It is essentially the same as @Agus Syahputra 's answer with one important difference. I've added the entire answer below.

Note: I've tested this only on EF6 and I'm not sure if this works with earlier versions of EF.

//I'm currently inside savechanges of my dbcontext
//if you're typing this code outside your dbcontext, replace this with your dbcontext    
var objectStateEntry = ((IObjectContextAdapter)this).ObjectContext.ObjectStateManager.GetObjectStateEntry(entry.Entity);
    string keyName = objectStateEntry.EntityKey.EntityKeyValues[0].Key;
1

you can get to ObjectContext because DbContext mostly wraps ObjectContext...

see
http://msdn.microsoft.com/en-us/library/gg696590%28v=vs.103%29.aspx
http://msdn.microsoft.com/en-us/library/system.data.entity.dbcontext%28v=vs.103%29.aspx
http://msdn.microsoft.com/en-us/library/dd283139.aspx

Yahia
  • 69,653
  • 9
  • 115
  • 144
  • I already know that (`(db as IObjectContextAdapter).ObjectContext`), I'm trying to find out how to get the primary key names of an entity – Green Lantern Aug 02 '11 at 04:17
  • Here's the old approach, it hits the database however http://stackoverflow.com/questions/2958921/entity-framework-4-how-to-find-the-primary-key/2959046#2959046 – Green Lantern Aug 02 '11 at 04:20