EntityFramework Получить объект по идентификатору? - PullRequest
23 голосов
/ 27 мая 2011

Возможно ли с помощью Generics получить объект из моей EntityFramework, не зная типа?

Я думаю о чем-то вроде:

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

Это выполнимо? Могу ли я использовать Reflection, чтобы как-то взять T.GetType().Name и использовать это для таблицы?

EDIT
Еще одно замечание: не все доступные мне таблицы используют «ID» в качестве уникального имени столбца.

Ответы [ 8 ]

30 голосов
/ 28 мая 2011

Вы можете определить интерфейс, реализованный всеми вашими сущностями:

public interface IEntity
{
    int Id { get; }
}

и способ получения вашей сущности:

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

Вы также можете использовать подход, аналогичный предложенному в связанном вопросе . Вам просто нужно использовать другой метод, чтобы получить вашу сущность:

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

Разница в том, что первый метод всегда запрашивает базу данных, даже если вы уже загрузили экземпляр в контекст, тогда как второй подход сначала проверяет, загружен ли экземпляр. Метод не так эффективен, потому что он строит эти имена снова и снова. Здесь - более общий подход, который может работать с любым типом и именем ключа, а здесь - подход, работающий со сложными ключами.

Ни один из этих методов напрямую не работает с наследованием - вы должны предоставить базовый тип, чтобы он работал.

25 голосов
/ 27 мая 2011

Я думаю, что метод Find() может сделать то, что вы ищете ( Метод DbSet.Find ).

var someEntity = dbSet.Find(keyValue);
5 голосов
/ 28 мая 2011

Трудно сделать полностью общее решение, потому что у сущностей могут быть составные ключи, но это будет работать для простого случая с одним ключом.

Ключ должен ограничить T типом System.Data.Objects.DataClasses.EntityObjectкоторый затем имеет свойство EntityKey (которое представляет первичный ключ).

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);
    }
}
3 голосов
/ 17 декабря 2012

Другой способ получить сущность из id в рамках полномочий

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

Результат

SELECT TOP (1) 
[Extent1].[CatId] AS [CatId], 
[Extent1].[CatName] AS [CatName], 
[Extent1].[CatType] AS [CatType],  
FROM [dbo].[Categories] AS [Extent1]
WHERE [Extent1].[CatId] = 1
3 голосов
/ 28 мая 2011

Наконец-то решена проблема с этим:
http://pastebin.com/kjXUKBNS

Для вызова кода я использую это:

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

Это предполагает, что первичный ключ вы ищетеon является первым в списке первичных ключей.

2 голосов
/ 17 ноября 2011

Я думаю, что это может помочь

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();
      }
    }
0 голосов
/ 07 ноября 2015

Одна вещь, которую я сделал, сработала, это определил интерфейс "KeyComparator":

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

И затем вы можете реализовать его на каком-либо объекте даже с помощью многозначного ключа:

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

И может использовать его в общем случае, если вы предоставите ограничение:

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

Для этого необходимо, чтобы у вас был экземпляр объекта, хотя вы можете заполнить пустой ключ значениями, а затем использовать его для извлечения из БД.

0 голосов
/ 30 мая 2011

Вот метод, который я использую для получения объекта Entity по идентификатору.

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

Возможно, вы найдете это более надежное решение, чем некоторые из предложенных выше. Я использовал его как часть общего хранилища во многих проектах. Надеюсь, это поможет вам.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...