Est-il possible avec Generics d’obtenir un objet de mon EntityFramework sans connaître le type?
Je pense à quelque chose du genre:
public T GetObjectByID<T>(int id)
{
return (from i in myDatabase.T where i.ID == id select i);
}
Est-ce faisable? Puis-je utiliser Reflection pour prendre en quelque sorte T.GetType().Name
et l'utiliser pour la table?
MODIFIER
Un autre problème est que toutes les tables disponibles n’utilisent pas "ID" comme nom de colonne unique.
Enfin résolu le problème avec ceci:
http://Pastebin.com/kjXUKBNS
Pour appeler le code, j'utilise ceci:
// 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);
Cela suppose que la clé primaire sur laquelle vous effectuez une recherche est la première de la liste des clés primaires.
Vous pouvez définir une interface implémentée par toutes vos entités:
public interface IEntity
{
int Id { get; }
}
et méthode pour récupérer votre entité:
public T GetObjectById<T>(int id) where T : class, IEntity
{
return context.CreateObjectSet<T>().SingleOrDefault(e => e.Id == id);
}
Vous pouvez également utiliser une approche similaire à celle proposée dans la question liée . Il vous suffit d'utiliser une autre méthode pour obtenir votre entité:
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);
}
La différence est que la première méthode interroge toujours la base de données même si vous avez déjà chargé l'instance dans le contexte, tandis que la seconde approche vérifie d'abord si l'instance est déjà chargée. La méthode n'est pas aussi efficace car elle construit ces noms à plusieurs reprises. Ici est une approche plus générale pouvant fonctionner avec n’importe quel type et nom de clé et ici est une approche utilisant des clés complexes.
Aucune de ces méthodes ne fonctionne directement avec l'héritage - vous devez fournir un type de base pour le faire fonctionner.
Je pense que la méthode Find()
pourra peut-être faire ce que vous cherchez ( Méthode DbSet.Find ).
var someEntity = dbSet.Find(keyValue);
Il est difficile de créer une solution complètement générique car les entités peuvent avoir des clés composites, mais cela fonctionnera pour un cas simple et unique.
La clé consiste à limiter T
au type System.Data.Objects.DataClasses.EntityObject
, qui a alors la propriété EntityKey
(qui représente la clé primaire).
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);
}
}
Un autre moyen d'obtenir une entité d'id sur un cadre d'entité
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();
}
Résultat
SELECT TOP (1)
[Extent1].[CatId] AS [CatId],
[Extent1].[CatName] AS [CatName],
[Extent1].[CatType] AS [CatType],
FROM [dbo].[Categories] AS [Extent1]
WHERE [Extent1].[CatId] = 1
Je pense que cela pourrait aider
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();
}
}
Une chose que j'ai faite qui a fonctionné est de définir une interface "KeyComparator":
public interface IHasKeyComparator<TObject> {
Expression<Func<TObject, bool>> KeyComparator { get; }
}
Et vous pouvez ensuite l'implémenter sur un objet même avec une clé à valeurs multiples:
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;
}
}
Et pouvez l’utiliser de manière générique si vous fournissez la contrainte:
public TObject Refresh<TObject>(TObject pObject)
where TObject : IHasKeyComparator<TObject> {
return this.Set<TObject>().Single(pObject.KeyComparator);
}
Pour ce faire, vous devez avoir une instance de l'objet, bien que vous puissiez en remplir une vide avec des valeurs de clé, puis l'utiliser pour extraire de la base de données.