2013-08-17 3 views
3

J'ai créé une classe de référentiel générique dont héritent toutes les autres classes de référentiel. C'est super, car cela signifie que presque toute la plomberie est faite une seule fois pour tous les dépôts. Je mis une explication complète de ce que je parle here, mais voici le code pour mon GenericRepository (un code est supprimé par souci de concision):Méthode CreateOrUpdate très générique avec Entity Framework

public abstract class GenericRepository<T> : IGenericRepository<T> where T : class, new() 
{ 
    private IMyDbContext _myDbContext; 

    public GenericRepository(IMyDbContext myDbContext) 
    { 
     _myDbContext = myDbContext; 
    } 

    protected IMyDbContext Context 
    { 
     get 
     { 
      return _myDbContext; 
     } 
    } 

    public IQueryable<T> AsQueryable() 
    { 
     IQueryable<T> query = Context.Set<T>(); 
     return query; 
    } 

    public virtual void Create(T entity) 
    { 
     Context.Set<T>().Add(entity); 
    } 

    public virtual void Update(T entity) 
    { 
     Context.Entry(entity).State = System.Data.EntityState.Modified; 
    } 
} 

Comme vous le voyez, j'ai une méthode Créer et une mise à jour méthode. Il serait très pratique d'avoir une méthode "CreateOrUpdate", donc je n'ai pas à vérifier manuellement les objets existants chaque fois que je dois sauvegarder quelque chose dans la base de données.

Chacun de mes objets dans Entity Framework possède un "Id", mais le défi ici est que GenericRepository fonctionne avec "T".

Maintenant, avec cette introduction plutôt longue, à ma question spécifique. Comment créer une méthode CreateOrUpdate générique pour mon GenericRepository?

MISE À JOUR

Après réponse Marcins, je mis en œuvre les méthodes génériques suivantes dans mon GenericRepository. Il faudra un certain temps avant que je puisse tester que cela fonctionne comme prévu, mais cela semble très prometteur.

public virtual bool Exists(Guid id) 
{ 
    return Context.Set<T>().Any(t => t.Id == id); 
} 

public virtual void CreateOrUpdate(T entity) 
{ 
    if (Exists(entity.Id)) 
    { 
     var oldEntity = GetSingle(entity.Id); 
     Context.Entry(oldEntity).CurrentValues.SetValues(entity); 
     Update(oldEntity); 
    } 
    else 
    { 
     Create(entity); 
    } 
} 

Le code ci-dessus ne compte pas moins de 3 allers-retours avec la base de données lors de la mise à jour. Je suis sûr que cela peut être optimisé, mais ce n'était pas vraiment l'exercice pour cette question.

Cette question gère ce sujet mieux: An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key

+1

Vous vous rendez dans la base de données pour décider si l'entité doit être insérée ou mise à jour. Vous avez seulement besoin de vérifier si l'Id de l'entité est la valeur par défaut pour un Guid. ('entity.ID == default (Guid)'). Vous pouvez également vous assurer que les proxies sont toujours créés si vous avez l'intention d'utiliser le chargement paresseux. Voir cet exemple: http://stackoverflow.com/a/16811976/150342 – Colin

+0

Une certaine logique métier peut vouloir définir un Guid comme clé primaire au début de la création, donc je devrais faire le petit aller-retour à la base de données dans mon cas . Sinon, un très bon point, qui est pertinent dans la plupart des autres cas. Merci. –

+0

En fait, mon implémentation échoue avec l'erreur suivante à chaque fois que j'essaie de mettre à jour 'Un objet avec la même clé existe déjà dans ObjectStateManager. ObjectStateManager ne peut pas suivre plusieurs objets avec la même clé. Cela se produit lors de l'envoi d'un nouvel objet avec le même Guid qu'un objet existant. Ce n'est pas lié à cette question, mais juste pensé que je vous ferais savoir au cas où quelqu'un d'autre voulait utiliser ce code. –

Répondre

4

Créer une interface avec Id propriété, mettre en œuvre sur chacun de vos entités et ajouter une autre contrainte générique à votre classe:

public interface IEntity 
{ 
    int Id { get; set;} 
} 

Et

public abstract class GenericRepository<T> : IGenericRepository<T> where T : class, IEntity, new() 

Avec cela, vous serez en mesure d'utiliser Id propriété dans votre classe de référentiel générique.

Bien sûr - Id ne doit pas être un int, il peut être aussi Guid.

+0

Bonne idée, je vais essayer. En passant, 'class' doit précéder' IEntity'. –

+0

@NielsBrinch Nice spot! MSDN ne dit pas un mot à ce sujet, mais vous avez raison: la contrainte 'struct' ou' class' doit être la première sur la liste. J'ai édité ma question. – MarcinJuraszek

+0

Cela fonctionne à merveille. J'aime ça. –

Questions connexes