6

I've created a generic ObjectSet<T> in my generic repository.

What I would like to get is the name of the EntityKey of ObjectSet<T> so that I can use it in the DataContext.GetObjectByKey.

I've searched around and dug deep, but I can't seem to find this value anywhere in the ObjectSet class.

Omar
  • 39,496
  • 45
  • 145
  • 213

7 Answers7

6

I looked a while ago for a nice way to do this and failed to find one. I generally end up building a GetEntityByKey extension method somewhere and within that, contatenating strings to build Entity Keys for TryGetObjectByKey calls. The general idea for building the entity key goes something like this:

internal class Program
{
    private static void Main(string[] args)
    {
        var dc = new AdventureWorksLT2008Entities();
        object c;
        dc.TryGetObjectByKey(GetEntityKey(dc.Customers, 23), out c);
        var customer = c as Customer;
        Console.WriteLine(customer.EmailAddress);
    }

    private static EntityKey GetEntityKey<T>(ObjectSet<T> objectSet, object keyValue) where T : class
    {
        var entitySetName = objectSet.Context.DefaultContainerName + "." + objectSet.EntitySet.Name;
        var keyPropertyName = objectSet.EntitySet.ElementType.KeyMembers[0].ToString();
        var entityKey = new EntityKey(entitySetName, new[] {new EntityKeyMember(keyPropertyName, keyValue)});
        return entityKey;
    }
}

You may be able to do something similar. This example assumes a single field per EntityKey for simplicity - for multiple value keys you would need to do something slightly more sophisticated with ObjectSet<T>.ElementType.KeyMembers and pass all your keys into the EntityKey constructor.

Steve Willcock
  • 26,111
  • 4
  • 43
  • 40
2

Generic:

public class GenericoRepositorio<T> : IGenericoRepositorio<T> where T : class
{
    protected readonly ObjectSet<T> ObjetoSet;
    protected readonly ModeloContainer Contexto;

    public GenericoRepositorio(ModeloContainer contexto)
    {
        Contexto = contexto;
        ObjetoSet = Contexto.CreateObjectSet<T>();
    }

    public T Carregar(int id)
    {
        object objeto;
        Contexto.TryGetObjectByKey(GetEntityKey(ObjetoSet, id), out objeto);
        return (T)objeto;
    }

    private static EntityKey GetEntityKey<T>(ObjectSet<T> objectSet, object keyValue) where T : class
    {
        var entitySetName = objectSet.Context.DefaultContainerName + "." + objectSet.EntitySet.Name;
        var keyPropertyName = objectSet.EntitySet.ElementType.KeyMembers[0].ToString();
        var entityKey = new EntityKey(entitySetName, new[] { new EntityKeyMember(keyPropertyName, keyValue) });
        return entityKey;
    }
}
Edmilson Lani
  • 153
  • 2
  • 6
1

See this post that I made regarding getting the EntitySetName. For my repository, I create a property that gets the entity set name for the specific class name to do exactly what you are trying to do.

Community
  • 1
  • 1
TheCloudlessSky
  • 18,608
  • 15
  • 75
  • 116
1

This should give you all the generic arguments (the types) for the ObjectSet:

objectSet.GetType().GetGenericArguments().First()
Vaccano
  • 78,325
  • 149
  • 468
  • 850
0

var objContext = ((IObjectContextAdapter)this.context).ObjectContext;

var objSet = objContext.CreateObjectSet<T>();

var entityKey = objContext.CreateEntityKey(objSet.EntitySet.Name, entityToUpdate);

Object foundEntity;

var exits = objContext.TryGetObjectByKey(entityKey, out foundEntity);

if (exits && this.dbset.Local != null && this.dbset.Local.Contains(foundEntity) &&this.dbset.Local.Any())                    
{
  if (entityKey.EntityKeyValues != null && entityKey.EntityKeyValues.Any())                       
  {       
    DbEntityEntry<T> entry = this.context.Entry(this.dbset.Find(entityKey.EntityKeyValues.FirstOrDefault().Value));
                        entry.CurrentValues.SetValues(entityToUpdate);
   }
}

this.context.SaveChanges();
leo
  • 1
  • 1
0

Tested with EF 6.

It will return an array of objects for each primary key value for the given DbEntityEntry.

Their maybe edge cases where this does not work - but for my simple needs works great.

Hope this helps someone else.

object[] GetPrimaryKeyValue(DbEntityEntry entry)
{
    List<object> key = new List<object>();

    var objectStateEntry = ((IObjectContextAdapter)this).ObjectContext.ObjectStateManager.GetObjectStateEntry(entry.Entity);
    if (objectStateEntry.EntityKey.EntityKeyValues != null && objectStateEntry.EntityKey.EntityKeyValues.Length==1)
    {
        key.Add(objectStateEntry.EntityKey.EntityKeyValues[0].Value);
    }
    else
    {
        if (objectStateEntry.EntitySet.ElementType.KeyMembers.Any())
        {
            foreach (var keyMember in objectStateEntry.EntitySet.ElementType.KeyMembers)
            {
                if (entry.CurrentValues.PropertyNames.Contains(keyMember.Name))
                {
                    var memberValue = entry.CurrentValues[keyMember.Name];
                    if (memberValue != null)
                    {
                        key.Add(memberValue);
                    }
                }
            }
        }
    }
    return key.ToArray();
}
Phil
  • 2,315
  • 2
  • 18
  • 26
0

I had a tough time trying to do almost the same thing, getting the primary key name and value at runtime when the type is unknown. I was just get trying to implement an auditing scheme for deletes, and every solution i find involves superfluous code that I dont really understand. The EntityKey is not available from a DbContext, which is also confusing and annoying. The last 5 lines may save you 5 hours and 1 yr of baldness. I am not attempting this for Inserts, so if you do, you need to inspect those values carefully as they may be 0 or null.

foreach(var entry in ChangeTracker.Entries<IAuditable>())
{
...

case EntityState.Deleted:

var oc = ((IObjectContextAdapter)this).ObjectContext;  //this is a DbContext
EntityKey ek = oc.ObjectStateManager.GetObjectStateEntry(entry.Entity).EntityKey;
var tablename = ek.EntitySetName;
var primaryKeyField = ek.EntityKeyValues[0].Key;     //assumes only 1 primary key
var primaryKeyValue = ek.EntityKeyValues[0].Value;
Bill
  • 2,382
  • 2
  • 24
  • 27
  • What if you don't have any ChangeTracker.Entries? – ProfK Mar 12 '13 at 20:10
  • In my case I was hooked into the SaveChanges event and I did not disable change tracking. If your change tracking is disabled, then the marked answer should help you. If your change tracking is not disabled, and you have a handle on an entity, I would think you could sub that in my 2nd line where I put '(entry.Entity)'. – Bill Mar 13 '13 at 14:04