6

Ich versuche, meine Arbeitseinheit von meinen Diensten oder Repository zu entkoppeln, so dass ich den UoW-Code nicht berühren muss, wann immer ich einen neuen Dienst hinzufügen möchte. Wie mache ich das?Decouple Einheit der Arbeit von Diensten oder Repo

_categoryService = _unitOfWork.Get<ICategoryService>(); 

so statt

_unitOfWork.CategoryService.Add(category) 

kann ich nur sagen;

_categoryService.Add(category); 
+0

Können Sie mehr über Ihr Szenario erfahren? Es scheint mir, dass Sie über die Injektion von Abhängigkeit sprechen (Ihr Code hängt von ICategoryService ab und Sie möchten ihn automatisch injizieren), aber es ist nicht klar aus Ihrer Frage. –

+0

Hallo! Ich habe ein MVC4-Testprojekt, um das UoW-Muster zu untersuchen. Ich habe einen Controller, eine Serviceklasse, die auf das Repository verweist. Aber wie Sie sehen können, habe ich diesen CategoryService in meiner UoW ​​als Eigentum. Dann wurde mir klar, dass ich, wenn ich einen neuen Service für etwas in der Zukunft erstelle, diesen Dienst zu meiner UoW-Klasse hinzufügen müsste.Ich versuche, einen Weg zu finden, nur eine Get Funktion zu haben, die einen Diensttyp zurückgibt, der auf der Schnittstelle basiert, die ich ihm gab. Ich bin mir nicht einmal sicher, ob dies der richtige Weg ist. Vielen Dank! –

+0

Was verwenden Sie für UoW-Container? Sie sollten einige der IoCs verwenden, überprüfen Sie diese Seite für schöne Liste: http://www.hanselman.com/blog/ListOfNETDependencyInjectionContainersIOC.aspx Die meisten von ihnen haben .Resolve () -Methode oder ähnliches, und Sie können die meisten von registrieren sie als Standard-Abhängigkeit Resolver in asp.net mvc, so fügen Sie einfach IService als Parameter zu Ihrem Controller. Fragen Sie das? –

Antwort

15

Ich versuche, meine Einheit der Arbeit von meinen Dienstleistungen oder Repository zu entkoppeln, so dass ich nicht der UOW-Code berühren müssen, wenn ich wünschen einen neuen Service

hinzufügen

Nun, das ist ein guter Anfang! ;-)

Die Lösung, die ich vorstelle, ist nicht die einzig mögliche Lösung, es gibt mehrere gute Möglichkeiten, UoW zu implementieren (Google wird Ihnen helfen). Aber das sollte dir das große Bild geben.

Erstellen Sie zunächst 2-Schnittstellen: IUnitOfWork und IRepository

public interface IUnitOfWork : System.IDisposable 
{ 
    IRepository<TEntity> GetRepository<TEntity>() where TEntity : class; 
    void Save(); 
} 

public interface IRepository<T> : IDisposable where T : class 
{ 
    void Add(T entity); 
    void Delete(T entity); 
    void Update(T entity); 
    T GetById(long Id); 
    IEnumerable<T> All(); 
    IEnumerable<T> AllReadOnly(); 
    IEnumerable<T> Find(Expression<Func<T, bool>> predicate); 
} 

Die Implementierungen recht einfach sind (ich alle meine Kommentare zur besseren Lesbarkeit Zweck entfernt, aber vergessen Sie nicht verkaufen ;-) hinzufügen)

public class UnitOfWork<TContext> : IUnitOfWork where TContext : IDbContext, new() 
{ 
    private readonly IDbContext _ctx; 
    private Dictionary<Type, object> _repositories; 
    private bool _disposed; 

    public UnitOfWork() 
    { 
    _ctx   = new TContext(); 
    _repositories = new Dictionary<Type, object>(); 
    _disposed  = false; 
    } 

    public IRepository<TEntity> GetRepository<TEntity>() where TEntity : class 
    { 
    if (_repositories.Keys.Contains(typeof(TEntity))) 
     return _repositories[typeof(TEntity)] as IRepository<TEntity>; 

    var repository = new Repository<TEntity>(_ctx); 
    _repositories.Add(typeof(TEntity), repository); 
    return repository; 
    } 

    public void Save() 
    { 
    try 
    { 
     _ctx.SaveChanges(); 
    } 
    catch (DbUpdateConcurrencyException ex) 
    { 
     ex.Entries.First().Reload(); 
    } 
    } 

    … 
} 

public class Repository<T> : IRepository<T> where T : class 
{ 
    private readonly IDbContext _context; 
    private readonly IDbSet<T> _dbset; 

    public Repository(IDbContext context) 
    { 
    _context = context; 
    _dbset = context.Set<T>(); 
    } 

    public virtual void Add(T entity) 
    { 
    _dbset.Add(entity); 
    } 

    public virtual void Delete(T entity) 
    { 
    var entry = _context.Entry(entity); 
    entry.State = EntityState.Deleted; 
    } 

    public virtual void Update(T entity) 
    { 
    var entry = _context.Entry(entity); 
    _dbset.Attach(entity); 
    entry.State = EntityState.Modified; 
    } 

    public virtual T GetById(long id) 
    { 
    return _dbset.Find(id); 
    } 

    public virtual IEnumerable<T> All() 
    { 
    return _dbset.ToList(); 
    } 

    public virtual IEnumerable<T> AllReadOnly() 
    { 
    return _dbset.AsNoTracking().ToList(); 
    } 

    public IEnumerable<T> Find(Expression<Func<T, bool>> predicate) 
    { 
    return _dbset.Where(predicate); 
    } 

} 

Wie Sie sehen können, verwenden beide Implementierungen die IDbContext-Schnittstelle. Diese Schnittstelle ist nur für einfache Testzwecke:

public interface IDbContext 
{ 
    DbSet<T> Set<T>() where T : class; 
    DbEntityEntry<T> Entry<T>(T entity) where T : class; 
    int SaveChanges(); 
    void Dispose(); 
} 

(Wie Sie sehen, ich bin mit EntityFramework-Code zuerst)

Nun, da die gesamte Sanitärbereich eingerichtet ist, werfen wir einen Blick darauf, wie diese könnte in einem Dienst verwendet werden. Ich habe einen Basisdienst, der wie folgt aussieht:

internal class Service<T> where T : class 
{ 
    internal Service(Infrastructure.IUnitOfWork uow) 
    { 
    _repository = uow.GetRepository<T>(); 
    } 

    protected Infrastructure.IRepository<T> Repository 
    { 
    get { return _repository; } 
    } 

    private readonly Infrastructure.IRepository<T> _repository; 
} 

Und alle meine Dienste von dieser Basis-Service erben.

internal class CustomerService : Service<Model.Customer> 
{ 
    internal CustomerService(Infrastructure.IUnitOfWork uow) : base(uow) 
    { 
    } 

    internal void Add(Model.Customer customer) 
    { 
    Repository.Add(customer); 
    } 

    internal Model.Customer GetByID(int id) 
    { 
    return Repository.Find(c => c.CustomerId == id); 
    } 

} 

Und das ist es!

Nun, wenn du so teilen möchten gleich aussehen UOW auf mehrere Dienste in einer Fassade Methode oder irgendwo anders, es könnte nur:

using (var uow = new UnitOfWork<CompanyContext>()) 
{ 
    var catService = new Services.CategoryService(uow); 
    var custService = new Services.CustomerService(uow); 

    var cat = new Model.Category { Name = catName }; 
    catService.Add(dep); 

    custService.Add(new Model.Customer { Name = custName, Category = cat }); 

    uow.Save(); 
} 

hoffe, das hilft!

+0

Sorry, ich komme gerade von einem sehr langen Urlaub zurück. Ich werde es versuchen. Danke vielmals! –

+0

Max, lass mich dir eine Frage stellen. Sie haben Repository-Methoden als virtuell definiert, damit Sie sie überschreiben können. Wo würden Sie sie überschreiben, wenn Sie keine Klasse haben, die vom Repository erbt (vielleicht möchte ich ein Repository in einer bestimmten Weise löschen). Ich mag die Idee von Erweiterungsmethoden nicht, aber es ist ein Ausweg. Wäre es eine Möglichkeit, mit Klassen zu arbeiten, die vom Repository erben (z. B. CustomerRepository), einige Methoden zu überschreiben und neue hinzuzufügen, ohne dass Erweiterungsmethoden erforderlich sind? – snekkke

+0

@snekkke Vielen Dank für Ihr Feedback. Eigentlich hast du recht. Virtuell ist hier ziemlich nutzlos. Eine Möglichkeit, Dinge anzupassen, wäre das Hinzufügen bestimmter Methoden zu Ihren 'Diensten'. Das Hinzufügen dieser Art von Methoden könnte den Job erledigen: 'internes Model.Customer GetByExternalIdAndChannelsAsReadOnly (String externalCode, KanalId-Kanal) { var cust = Repository.Find (c => c.ExternalCode == externalCode && c.ChannelId == (Byte) channel) .SingleOrDefault(); zurück cust; } ' – MaxSC