2016-09-13 1 views
0

Ich habe ein generisches Repository-Muster mit UnitOfWork und ich verwende IoC. Anders als die vom Repository verwendeten Basismethoden habe ich einige benutzerdefinierte Methoden. Anstatt die gesamten IRepository-Methoden erneut zu implementieren, habe ich von der GenericRepository-Klasse geerbt.Generisches Repository mit UnitOfWork mit IoC Benutzerdefinierte Methoden

Hier ist meine UnitofWork Umsetzung:

public interface IUnitOfWork<T> : IDisposable where T : DbContext 
{ 
    int Save(); 
    T Context { get; } 
} 

public class UnitOfWork<T> : IUnitOfWork<T> where T : MyContext, new() 
{ 
    private readonly T _context; 

    public UnitOfWork() 
    { 
     _context = new T(); 
    } 

    public UnitOfWork(T Context) 
    { 
     _context = Context; 
    } 

    public int Save() 
    { 
     return _context.SaveChanges(); 
    } 


    public T Context 
    { 
     get 
     { 
      return _context; 
     } 
    } 

    public void Dispose() 
    { 
     _context.Dispose(); 
    } 
} 

Das ist mein Repository Implementierung:

public interface IGenericRepository 
{ 
    IQueryable<T> All<T>() where T : class; 
    void Remove<T>(int id)where T : class; 
    void Remove<T>(T entity) where T : class; 
    void RemoveRange<T>(IList<T> entities) where T : class; 
    T Find<T>(int id) where T : class; 
    void Add<T>(T entity) where T : class; 
    void AddRange<T>(IList<T> entities) where T : class; 
    void Update<T>(T entity) where T : class; 
    int SaveChanges(); 
} 

    public class GenericRepository<C> : IGenericRepository where C : MyContext 
{ 
    protected readonly C _context; 

    public GenericRepository(IUnitOfWork<C> unitOfWork) 
    { 
     _context = unitOfWork.Context; 
    } 

    public int SaveChanges() 
    { 
     return _context.SaveChanges(); 
    } 

    public IQueryable<T> All<T>() where T : class 
    { 
     return _context.Set<T>(); 
    } 

    public void Remove<T>(int id) where T : class 
    { 
     T entity = _context.Set<T>().Find(id); 
     if (entity != null) 
     { 
      _context.Set<T>().Remove(entity); 
     } 
    } 

    public void Remove<T>(T entity) where T : class 
    { 
     if (entity != null) 
     { 
      _context.Set<T>().Remove(entity); 
     } 
    } 

    public void RemoveRange<T>(IList<T> entities) where T : class 
    { 
     if (entities.Count > 0) 
     { 
      _context.Set<T>().RemoveRange(entities); 
     } 
    } 

    public T Find<T>(int id) where T : class 
    { 
     return _context.Set<T>().Find(id); 
    } 

    public void Add<T>(T entity) where T : class 
    { 
     _context.Set<T>().Add(entity); 
    } 

    public void AddRange<T>(IList<T> entities) where T : class 
    { 
     _context.Set<T>().AddRange(entities); 
    } 

    public void Update<T>(T entity) where T : class 
    { 
     _context.Set<T>().Attach(entity); 
     _context.Entry(entity).State = System.Data.Entity.EntityState.Modified; 
    } 
} 

Hier ist ein Beispiel Custom-Repository:

public interface IUserAccountRepository : IGenericRepository 
{ 
    UserAccount Find(string email, string password); 
    bool CheckDuplicate(string email); 
} 

public class UserAccountRepository<C> : GenericRepository<C> where C : CSharpAssigmentContext, IUserAccountRepository 
{ 
    protected readonly C _context; 

    public UserAccountRepository(IUnitOfWork<C> unitOfWork) 
    { 
     _context = unitOfWork.Context; 
    } 

    public int SaveChanges() 
    { 
     return _context.SaveChanges(); 
    } 

    /// <summary> 
    /// Find user by email and password 
    /// </summary> 
    public UserAccount Find(string email, string password) 
    { 
     return _context.Set<UserAccount>().Where(ua => ua.Email == email && ua.Password == password).FirstOrDefault(null); 
    } 

    /// <summary> 
    /// Check wether user exists or not 
    /// </summary> 
    public bool CheckDuplicate(string email) 
    { 
     return _context.Set<UserAccount>().Any(ua => ua.Email == email); 
    } 

    public IQueryable<T> All<T>() where T : class 
    { 
     return _context.Set<T>(); 
    } 

    public void Remove<T>(int id) where T : class 
    { 
     T entity = _context.Set<T>().Find(id); 
     if (entity != null) 
     { 
      _context.Set<T>().Remove(entity); 
     } 
    } 

    public void Remove<T>(T entity) where T : class 
    { 
     if (entity != null) 
     { 
      _context.Set<T>().Remove(entity); 
     } 
    } 

    public void RemoveRange<T>(IList<T> entities) where T : class 
    { 
     if (entities.Count > 0) 
     { 
      _context.Set<T>().RemoveRange(entities); 
     } 
    } 

    public T Find<T>(int id) where T : class 
    { 
     return _context.Set<T>().Find(id); 
    } 

    public void Add<T>(T entity) where T : class 
    { 
     _context.Set<T>().Add(entity); 
    } 

    public void AddRange<T>(IList<T> entities) where T : class 
    { 
     _context.Set<T>().AddRange(entities); 
    } 

    public void Update<T>(T entity) where T : class 
    { 
     _context.Set<T>().Attach(entity); 
     _context.Entry(entity).State = System.Data.Entity.EntityState.Modified; 
    } 

Hier ist meine Unity IoC Code:

public static class UnityConfig 
{ 
    public static void RegisterComponents() 
    { 
     var container = new UnityContainer(); 

     //UnitOfWork and GenericRepository 
     container.RegisterType(typeof(IUnitOfWork<CSharpAssigmentContext>),typeof(UnitOfWork<CSharpAssigmentContext>), new HierarchicalLifetimeManager()); 
     container.RegisterType(typeof(IGenericRepository), typeof(GenericRepository<CSharpAssigmentContext>), new HierarchicalLifetimeManager()); 

     //I keep receiving compile ERROR here 
     container.RegisterType(typeof(IUserAccountRepository), typeof(UserAccountRepository<CSharpAssigmentContext>), new HierarchicalLifetimeManager()); 

     //Services 
     container.RegisterType(typeof(IUsersAccountsService), typeof(UsersAccountsService), new TransientLifetimeManager()); 

     DependencyResolver.SetResolver(new UnityDependencyResolver(container)); 
    } 
} 

Wie im Code erwähnt, halte ich einen Fehler zur Kompilierzeit für den folgenden Code erhalten:

container.RegisterType(typeof(IUserAccountRepository), typeof(UserAccountRepository<CSharpAssigmentContext>), new HierarchicalLifetimeManager()); 

Der Fehler ist:

Der Typ mycontext nicht als Typ verwendet werden kann, Parameter C im generischen Typ oder in der Methode. Es gibt keine implizite Referenzkonvertierung von MyContext nach IMyClassRepository.

Wie löst man diesen Fehler? Ist meine Implementierung für benutzerdefinierte Repositories korrekt?

+0

Können Sie Ihre UserAccountRepository-Klassensignatur anzeigen? –

Antwort

1

Die Stiftung ist falsch. Ein generisches Repository sollte eine generische Entität Parameter haben.Ihr "generisches" Repository hat Methoden mit einem generischen Parameter T, aber es gibt keine Garantie, dass immer dieselbe Entität verwendet wird. Dies ist, was die Schnittstelle aussehen soll:

public interface IGenericRepository<TEntity> where TEntity : class 
{ 
    IQueryable<TEntity> All(); 
    void Remove(int id); 
    void Remove(TEntity entity); 
    void RemoveRange(IList<TEntity> entities); 
    TEntity Find(int id); 
    void Add(TEntity entity); 
    void AddRange(IList<TEntity> entities); 
    void Update(TEntity entity); 
    int SaveChanges(); 
} 

In der Tat, wenn Sie sich entscheiden, dass Sie eine UOW/Repository-Schicht auf den DbContext/DbSet wollen, ich keinen Grund, es anders zu machen als this standard example. Dort sehen Sie ein ähnliches generisches Repository neben einer UOW, die mehrere Repositories enthält.

Nachdem das getan, was man "UserAccountRepository" nennen, sollte ein Dienst sein, der eine UOW enthält, die von Ihrem IoC Behälter injiziert werden kann:

public interface IUserAccountService // No repository! 
{ 
    UserAccount Find(string email, string password); 
    bool CheckDuplicate(string email); 
} 

Beispielimplementierung:

public class UserAccountService : IUserAccountService 
{ 
    private readonly IUnitOfWork<CSharpAssigmentContext> _unitOfWork; 

    public UserAccountService(IUnitOfWork<CSharpAssigmentContext> unitOfWork) 
    { 
     this._unitOfWork = unitOfWork; 
    } 

Sie sehen, dass in dieser UoW ​​/ Repository-Implementierung der Kontext nicht offengelegt wird. Das ist einer der Ziele dieser Abstraktionsschicht.

+0

Wenn Sie auf meinen UnityCode schauen Sie können sehen, dass ich tatsächlich Dienste implementiere. Der Grund, warum ich ein IUserAccountRepository erstellt habe, war nur für benutzerdefinierte Methoden. Wenn ich die Dienste implementiere, würde ich statt des IGenericRepository das benutzerdefinierte Repository verwenden. In Ihrem letzten Codeabschnitt haben Sie die UoW mit dem Dienst verwendet. Was ich hier mache, verwende eine andere Ebene der Abstraktion. UoW -> DAL -> Repository -> Service -> Präsentation – ykh

+0

Meine einzige Frage hier ist, warum sollte ich nicht ein benutzerdefiniertes Repository und stattdessen meine benutzerdefinierten Methoden in einem Dienst anwenden. Ich verstehe, dass der Zweck der Service-Schicht darin besteht, Geschäftslogik offenzulegen, aber die Implementierung aller Repository-bezogenen Methoden im Repository scheint logischer zu sein. – ykh

+0

Weil das den Zweck von * generischen * Repositories vereitelt. Es führt auch eine immer wiederkehrende Entscheidung ein: Was stelle ich wo? Einige Methoden mögen für ein Repository geeignet sein, manche für einen Service, aber es wird immer eine Grauzone geben. –

1

Soweit ich sehen kann, könnte ein Fehler in Ihrer UserAccountRepository Klassendefinition sein:

Typ UserAccountRepository ein generischer Typ ist mit generic:

public class UserAccountRepository<C> : GenericRepository<C> 
    where C : CSharpAssigmentContext, IUserAccountRepository 

Das Klassendefinition kann so gelesen werden Argument vom Typ C; UserAccountRepository erbt von der generischen Klasse GenericRepository mit einem generischen Argument vom Typ C; Typ C muss von der Klasse CSharpAssignmentContext erben und Typ C muss die Schnittstelle IUserAccountRepository implementieren.

Entfernen IUserAccountRepository von Typeinschränkungen für allgemeine Parameter C und das Hinzufügen es nach GenericRepository nach einem Komma sollte die Arbeit machen:

public class UserAccountRepository<C> : GenericRepository<C>, IUserAccountRepository 
    where C : CSharpAssigmentContext 

jetzt Klassendefinition kann so gelesen werden: Typ UserAccountRepository ist ein generischer Typ mit generisches Argument vom Typ C; UserAccountRepository erbt von der generischen Klasse GenericRepository mit einem generischen Argument vom Typ C; type UserAccountRepository muss die Schnittstelle IUserAccountRepository implementieren. Generischer Argumenttyp (Typ C) muss von der Klasse CSharpAssignmentContext erben.

Wenn Sie eine Klasse von generischen Klasse/Schnittstelle zusammen mit anderen Schnittstellen erben, müssen Sie zuerst festlegen, welche Arten Sie erben von oder zu implementieren, und erst danach generischen Typ Einschränkungen angeben:

public class SomeImplementation<T1, T2> : ISomeInterface<T1>, IAnotherInterface<T2>, IDisposable, ICloneable 
    where T1 : IAbstraction 
    where T2 : class 
+0

Es scheint, dass ich meine Frage mit einer Problemumgehung aktualisiert habe, die funktionierte. Ich habe die Frage jetzt aktualisiert. Was ich in meinem benutzerdefinierten Repository mache, ist, dass ich von dem GenericRepository und dem IUserAccountRepository erben. Wenn ich vom generischen Repository allein erben, funktioniert es gut, aber sobald ich vom IUserAccountRepository erben, erhalte ich den Fehler, der in der Frage – ykh

+0

@ykh erwähnt wird, aktualisierte ich meine Antwort, schaut bitte –

Verwandte Themen