2013-07-17 16 views
13

Ich verwende derzeit Entity Framework mit einem generischen Repository und Unit Of Work Pattern. Mein Modell ist ähnlich dem in this articleSollte ich ein generisches Repository mit Entity Framework 5 verwenden?

beschriebenen Ich habe in der Vergangenheit generische Repositories verwendet und wirklich die globale Funktionalität genossen, die es zur Verfügung stellen kann. Es scheint jedoch, dass ich jeden Tag mehr Probleme bekomme, wenn es darum geht, es mit Entity Framework zu verwenden. Diese Probleme scheinen noch mehr aufzutreten, wenn es um die Behandlung von Eltern/Kinder/Knoten-Beziehungen geht.

Die Verwendung eines generischen Repository mit EF beginnt, einen schlechten Geschmack in meinem Mund zu hinterlassen, und ich fange an zu denken, dass die Verwendung eines Generic Repository mit EF der falsche Ansatz ist.

Könnte jemand bitte helfen, mich in die richtige Richtung zu lenken?

+2

Es hängt davon ab, was Ihr Ziel ist es, eine generische Repository/UOW zu haben. Wenn Sie "DbSet " bereits als generisches Repository betrachten und 'DbContext' (' SaveChanges') Ihre UoW ist, dann könnten Sie diese Konzepte duplizieren. Es könnte das Testen erleichtern, auf Kosten einer anderen Abstraktionsebene. Ich benutze das für meine Repo/UoW, und zum Testen verwende ich http://effort.codeplex.com – Matthew

+0

Mein einziger echter Zweck, ein generisches Repository zu verwenden, ist globale, generische CRUD Methoden. – Lando

+1

'DbSet <>' führt diese bereits durch. Das Testen auf einem generischen Repository ist einfacher und entkoppelt die Abhängigkeit von Entity Framework, wenn dies von Bedeutung ist. – Matthew

Antwort

5

Der Ansatz dieses Artikels ist wirklich etwas, das zu einem Schmerz werden kann, weil Sie bereits ein generisches Repository und eine generische IUnitOfWork in EF haben und das spezifische Repository für jeden Typ nur entfernt den Vorteil der generischen!

Ich poste hier ein Beispiel, wie ich ein generisches Repository und mein IUnitOfWork habe, mit dem Sie ein sehr schönes Repository haben können!

public interface IUnitOfWork : IDisposable 
{ 
    void Save(); 
    void Save(SaveOptions saveOptions); 
} 

public interface IRepository<TEntity> : IDisposable where TEntity : class 
{ 
    IUnitOfWork Session { get; } 
    IList<TEntity> GetAll(); 
    IList<TEntity> GetAll(Expression<Func<TEntity, bool>> predicate); 
    bool Add(TEntity entity); 
    bool Delete(TEntity entity); 
    bool Update(TEntity entity); 
    bool IsValid(TEntity entity); 
} 

Und die Umsetzung so etwas wie:

public class Repository : Component, IRepository 
{ 

    protected DbContext session; 

    public virtual IUnitOfWork Session 
    { 
     get 
     { 
      if (session == null) 
       throw new InvalidOperationException("A session IUnitOfWork do repositório não está instanciada."); 
      return (session as IUnitOfWork); 
     } 
    } 

    public virtual DbContext Context 
    { 
     get 
     { 
      return session; 
     } 
    } 

    public Repository(IUnitOfWork instance) 
    { 
     SetSession(instance); 
    } 

    public IList<TEntity> GetAll<TEntity>() where TEntity : class 
    { 
     return session.Set<TEntity>().ToList(); 
    } 

    public IList<TEntity> GetAll<TEntity>(Expression<Func<TEntity, bool>> predicate) where TEntity : class 
    { 
     return session.Set<TEntity>().Where(predicate).ToList(); 
    } 

    public bool Add<TEntity>(TEntity entity) where TEntity : class 
    { 
     if (!IsValid(entity)) 
      return false; 
     try 
     { 
      session.Set(typeof(TEntity)).Add(entity); 
      return session.Entry(entity).GetValidationResult().IsValid; 
     } 
     catch (Exception ex) 
     { 
      if (ex.InnerException != null) 
       throw new Exception(ex.InnerException.Message, ex); 
      throw new Exception(ex.Message, ex); 
     } 
    } 

    public bool Delete<TEntity>(TEntity entity) where TEntity : class 
    { 
     if (!IsValid(entity)) 
      return false; 
     try 
     { 
      session.Set(typeof(TEntity)).Remove(entity); 
      return session.Entry(entity).GetValidationResult().IsValid; 
     } 
     catch (Exception ex) 
     { 
      if (ex.InnerException != null) 
       throw new Exception(ex.InnerException.Message, ex); 
      throw new Exception(ex.Message, ex); 
     } 
    } 

    public bool Update<TEntity>(TEntity entity) where TEntity : class 
    { 
     if (!IsValid(entity)) 
      return false; 
     try 
     { 
      session.Set(typeof(TEntity)).Attach(entity); 
      session.Entry(entity).State = EntityState.Modified; 
      return session.Entry(entity).GetValidationResult().IsValid; 
     } 
     catch (Exception ex) 
     { 
      if (ex.InnerException != null) 
       throw new Exception(ex.InnerException.Message, ex); 
      throw new Exception(ex.Message, ex); 
     } 
    } 

    public virtual bool IsValid<TEntity>(TEntity value) where TEntity : class 
    { 
     if (value == null) 
      throw new ArgumentNullException("A entidade não pode ser nula."); 
     return true; 
    } 

    public void SetSession(IUnitOfWork session) 
    { 
     SetUnitOfWork(session); 
    } 

    protected internal void SetUnitOfWork(IUnitOfWork session) 
    { 
     if (!(session is DbContext)) 
      throw new ArgumentException("A instância IUnitOfWork deve um DbContext."); 
     SetDbContext(session as DbContext); 
    } 

    protected internal void SetDbContext(DbContext session) 
    { 
     if (session == null) 
      throw new ArgumentNullException("DbContext: instance"); 
     if (!(session is IUnitOfWork)) 
      throw new ArgumentException("A instância DbContext deve implementar a interface IUnitOfWork."); 
     this.session = session; 
    } 

} 
+0

In dem hinzufügen, was ist der Sinn des Versuches zu fangen? Auch warum nicht session.Set () .Add (entity)? – jcmcbeth

+0

Bei der Frage ging es um den allgemeinen Gebrauch, also denken Sie daran, dass dies nur ein Beispiel ist, um zu erklären. Das ist nicht der Punkt der Frage. Der Punkt des try catch ist es, die innere Ausnahme, die ausgelöst wird, anstelle eines allgemeineren Fehlers anzuzeigen. Auch mit Set () .Add (entity) würde in diesem Fall funktionieren, aber es gibt einen Punkt, wo wir die generische Version nicht verwenden können (zB wenn Sie Vererbung haben und verwenden .TypeOf <>) –

+0

Wenn Sie beabsichtigt Wiederholen Sie die Ausnahme, dann können Sie einfach "throw;" sagen, da das Werfen einer neuen Ausnahme den aktuellen Aufrufstapel verwirft. Siehe http://stackoverflow.com/questions/881473/why-catch-and-rethrow-exception-in-c. Ich stimme auch nicht damit überein, die innere Ausnahme erneut auszugeben, da jeder, der die Methode aufruft, genauso gut die innere Ausnahme überprüfen könnte, aber mit diesem Code gibt man ihnen nicht die Option. – jcmcbeth

Verwandte Themen