EntityFramework Get object by ID?
Asked Answered
M

8

35

Is it possible with Generics to get an object from my EntityFramework without knowing the type?

I'm thinking of something along the lines of:

public T GetObjectByID<T>(int id)
{
   return (from i in myDatabase.T where i.ID == id select i);
}

Is that doable? Could I use Reflection to somehow take T.GetType().Name and use that for the table?

EDIT
Another hangup, is that not all tables available to me use "ID" as their unique column name.

Mychael answered 27/5, 2011 at 18:54 Comment(3)
This can help you: #5166797Polite
T isn't necessarily the entity type in this case. It could be a supertype, e.g., System.Object, or a proxy subtype. You haven't given enough information in your signature to solve the problem in all cases.Uther
That links has the same problem, it is using MVC/DbSets and I am using ObjectContext.Mychael
M
3

Finally solved the issue with this:
http://pastebin.com/kjXUKBNS

To call the code I use this:

// Get the id of the object we are saving
PropertyInfo prop = GetProperty<TEntity>(entity, entity.EntityKey.EntityKeyValues[0].Key);
string entityID = prop.GetValue(entity, null).ToString();

// Get the current version of this object
var originalEntity = GetEntity<TEntity>(PropertyEquals, entityID);

This makes the assumption that the Primary Key you are searching on is the first one in the list of Primary Keys.

Mychael answered 28/5, 2011 at 2:5 Comment(3)
That is pretty ugly solution because it uses reflection and the solution doesn't correspond with your question - why do you extract value if your initial example knew the Id? And again did you even check links provided by others? @Tallman linked better solution for this problem in one of his comments.Polite
Yucks solutions are all for things like DbSets which I am not using. I haven't tried the answers posted after mine yet and it's possible that one of them will work better. My solution was thrown together through trial and error while learning so I'm sure that it could be done better. My initial example was just a general overview. I needed something that was entirely generic otherwise I would be writing code for each of the hundreds of EntityObjects in our EntityFramework.Mychael
Am I missing something? It looks like you have an overload for GetProperty that takes two parameters that isn't included in the link.Gripe
T
38

I think the Find() method may be able to do what you're looking for (DbSet.Find Method).

var someEntity = dbSet.Find(keyValue);
Tallman answered 27/5, 2011 at 18:56 Comment(4)
I'm using ObjectContext and EntityObjects. I wish I had .Find but I don't.Mychael
That also requires me to provide the string name of my Primary Key. So basically I don't think this will work.Mychael
I had a related question #6019211 which may help you out.Tallman
Your answer at that question uses DbContext and we are using ObjectContextMychael
P
36

You can define interface implemented by all your entities:

public interface IEntity
{
    int Id { get; }
}

and method to retrieve your entity:

public T GetObjectById<T>(int id) where T : class, IEntity
{
    return context.CreateObjectSet<T>().SingleOrDefault(e => e.Id == id);
}

You can also use similar approach to one provided in the linked question. You just have to use another method to get your entity:

public virtual T GetByKey<T>(int id) where T : class, IEntity
{
     string containerName = context.DefaultContainerName;
     string setName = context.CreateObjectSet<T>().EntitySet.Name;
     // Build entity key
     var entityKey = new EntityKey(containerName + "." + setName, "Id", id);
     return (TEntity)Context.GetObjectByKey(entityKey);         
}

The difference is that first method always query the database even if you have already loaded the instance to the context whereas second approach first checks if the instance is already loaded. The method is not so efficient because it builds these names over and over. Here is more general approach which can work with any key type and name and here is approach working with complex keys.

Neither of this method directly works with inheritance - you must provide base type to make it work.

Polite answered 27/5, 2011 at 20:54 Comment(3)
Your 2nd method might work for me, but it assumes my ID column is uniform across my whole database (which it isn't). I'm actually incredibly close to a method that won't make that assumption.Mychael
You might get some benefit from restricting your generic types to those which extend the EntityObject class.Dunno
@James: Did you even check links I provided? There are solution for different type of keys, complex keys and different key names.Polite
D
6

It's hard to make a completely generic solution because Entities can have composite keys, but this will work for a simple single key case.

The key is to restrict T to be of type System.Data.Objects.DataClasses.EntityObject, which then has as EntityKey property (which represents the primary key).

static T GetById<T>(object id) where T : EntityObject
{
    using (var context = new MyEntities())
    {
        return context.CreateObjectSet<T>()
            .SingleOrDefault(t => t.EntityKey.EntityKeyValues[0].Value == id);
    }
}
Dunno answered 28/5, 2011 at 3:7 Comment(1)
So this is the best answer, right? Also, I just extended my context object and made this a method on it. So, now, it's a one-liner: return CreateObjectSet<T>().SingleOrDefault(t => t.EntityKey.EntityKeyValues[0].Value == id);Gripe
M
3

Finally solved the issue with this:
http://pastebin.com/kjXUKBNS

To call the code I use this:

// Get the id of the object we are saving
PropertyInfo prop = GetProperty<TEntity>(entity, entity.EntityKey.EntityKeyValues[0].Key);
string entityID = prop.GetValue(entity, null).ToString();

// Get the current version of this object
var originalEntity = GetEntity<TEntity>(PropertyEquals, entityID);

This makes the assumption that the Primary Key you are searching on is the first one in the list of Primary Keys.

Mychael answered 28/5, 2011 at 2:5 Comment(3)
That is pretty ugly solution because it uses reflection and the solution doesn't correspond with your question - why do you extract value if your initial example knew the Id? And again did you even check links provided by others? @Tallman linked better solution for this problem in one of his comments.Polite
Yucks solutions are all for things like DbSets which I am not using. I haven't tried the answers posted after mine yet and it's possible that one of them will work better. My solution was thrown together through trial and error while learning so I'm sure that it could be done better. My initial example was just a general overview. I needed something that was entirely generic otherwise I would be writing code for each of the hundreds of EntityObjects in our EntityFramework.Mychael
Am I missing something? It looks like you have an overload for GetProperty that takes two parameters that isn't included in the link.Gripe
S
3

An other way to get entity from id on entitiy framework

public T Get<T>(int id) where T : EntityObject;
{
var ObjectSet = _context.CreateObjectSet<T>();
var PropName = ObjectSet.EntitySet.ElementType.KeyMembers[0].ToString();
return  ObjectSet.Where("it." + PropName + "=" + id).FirstOrDefault();
}

Result

SELECT TOP (1) 
[Extent1].[CatId] AS [CatId], 
[Extent1].[CatName] AS [CatName], 
[Extent1].[CatType] AS [CatType],  
FROM [dbo].[Categories] AS [Extent1]
WHERE [Extent1].[CatId] = 1
Sandpit answered 16/12, 2012 at 22:7 Comment(0)
B
2

I think this could help

public static TEntity Find<TEntity>(this ObjectSet<TEntity> set, object id) where TEntity : EntityObject
    {
      using (var context = new MyObjectContext())
      {
        var entity = set.Context.CreateObjectSet<TEntity>();
        string keyName = entity.FirstOrDefault().EntityKey.EntityKeyValues.FirstOrDefault().Key;

        return entity.Where("it." + keyName + " = " + id).FirstOrDefault();
      }
    }
Bethezel answered 17/11, 2011 at 10:26 Comment(0)
S
1

One thing I've done that has worked is to define a "KeyComparator" interface:

public interface IHasKeyComparator<TObject> {
   Expression<Func<TObject, bool>> KeyComparator { get; } 
}

And then you can implement it on some object even with a multi-valued key:

public sealed class MultiKeyedObject : IHasKeyComparator<MultiKeyedObject> {
   public int ID1 { get; set; }
   public string ID2 { get; set; }       

   readonly Expression<Func<MultiKeyedObject, bool>> mKeyComparator;

   public Expression<Func<MultiKeyedObject, bool>> KeyComparator {
      get { return mKeyComparator; }
   }

   public MultiKeyedObject() {
      mKeyComparator = other => other.ID1 == ID1 && other.ID2 == ID2;
   }
}

And can use it generically if you provide the constraint:

public TObject Refresh<TObject>(TObject pObject)
where TObject : IHasKeyComparator<TObject> {
   return this.Set<TObject>().Single(pObject.KeyComparator);
}

Doing it this way requires you to have an instance of the object, though, although you can fill an empty one with key values and then use it to pull from the DB.

Sadomasochism answered 6/11, 2015 at 22:4 Comment(0)
K
-1

Here's the method I use to get an Entity object by ID.

public TEntity GetByKey<TEntity>(object keyValue) where TEntity : class
    {            
        EntityKey key = GetEntityKey<TEntity>(keyValue);

        object originalItem;
        if (ObjectContext.TryGetObjectByKey(key, out originalItem))
        {
            return (TEntity)originalItem;
        }
        return default(TEntity);
    }

You may find this a more robust solution than some of those provided above. I've used it as part of a generic repository in a lot of projects. Hope it helps you out.

Khmer answered 30/5, 2011 at 7:21 Comment(1)
What's the value of this souliton without the GetEntityKey method?Fauman

© 2022 - 2024 — McMap. All rights reserved.