Zwiebelarchitektur, Arbeitseinheit und ein generisches Repository-Muster

Dies ist das erste Mal, dass ich einen stärker domänengetriebenen Designansatz implementiere. Ich habe beschlossen, das zu versuchenZwiebel-Architektur da es sich eher auf die Domain als auf die Infrastruktur / Plattformen / etc. konzentriert.

Um von Entity Framework abzuweichen, habe ich einegenerisches Repository mit einerArbeitseinheit Implementierung.

DasIRepository<T> undIUnitOfWork Schnittstellen:

public interface IRepository<T>
{
    void Add(T item);

    void Remove(T item);

    IQueryable<T> Query();
}

public interface IUnitOfWork : IDisposable
{
    void SaveChanges();
}

Entity Framework-Implementierungen vonIRepository<T> undIUnitOfWork:

public class EntityFrameworkRepository<T> : IRepository<T> where T : class
{
    private readonly DbSet<T> dbSet;

    public EntityFrameworkRepository(IUnitOfWork unitOfWork)
    {
        var entityFrameworkUnitOfWork = unitOfWork as EntityFrameworkUnitOfWork;

        if (entityFrameworkUnitOfWork == null)
        {
            throw new ArgumentOutOfRangeException("Must be of type EntityFrameworkUnitOfWork");
        }

        dbSet = entityFrameworkUnitOfWork.GetDbSet<T>();
    }

    public void Add(T item)
    {
        dbSet.Add(item);
    }

    public void Remove(T item)
    {
        dbSet.Remove(item);
    }

    public IQueryable<T> Query()
    {
        return dbSet;
    }
}

public class EntityFrameworkUnitOfWork : IUnitOfWork
{
    private readonly DbContext context;

    public EntityFrameworkUnitOfWork()
    {
        this.context = new CustomerContext();;
    }

    internal DbSet<T> GetDbSet<T>()
        where T : class
    {
        return context.Set<T>();
    }

    public void SaveChanges()
    {
        context.SaveChanges();
    }

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

DasKunde Repository:

public interface ICustomerRepository : IRepository<Customer>
{

}

public class CustomerRepository : EntityFrameworkRepository<Customer>, ICustomerRepository 
{
    public CustomerRepository(IUnitOfWork unitOfWork): base(unitOfWork)
    {
    }
}

ASP.NET MVC-Controller, der das Repository verwendet:

public class CustomerController : Controller
{
    UnityContainer container = new UnityContainer();

    public ActionResult List()
    {
        var unitOfWork = container.Resolve<IUnitOfWork>();
        var customerRepository = container.Resolve<ICustomerRepository>();

        return View(customerRepository.Query());
    }

    [HttpPost]
    public ActionResult Create(Customer customer)
    {
        var unitOfWork = container.Resolve<IUnitOfWork>();
        var customerRepository = container.Resolve<ICustomerRepository>();; 

        customerRepository.Add(customer);

        unitOfWork.SaveChanges();

        return RedirectToAction("List");
    }
}

Abhängigkeitsinjektion mit Einheit:

container.RegisterType<IUnitOfWork, EntityFrameworkUnitOfWork>();
container.RegisterType<ICustomerRepository, CustomerRepository>();

Lösung:

PROBLEME?

Die Repository-Implementierung (EF-Code) ist sehr allgemein. Es sitzt alles in der Seite derEntityFrameworkRepository<T> Klasse. Konkrete Modellrepositorys enthalten keine dieser Logik. Das erspart mir das Schreiben von tonnenweise redundantem Code, beeinträchtigt aber möglicherweise die Flexibilität?

DasICustomerRepository undCustomerRepository Klassen sind grundsätzlich leer. Sie dienen lediglich der Abstraktion. Soweit ich weiß, passt dies zu der Vision der Onion-Architektur, in der sich infrastruktur- und plattformabhängiger Code außerhalb Ihres Systems befindet, leere Klassen und leere Schnittstellen jedoch als falsch empfunden werden?

So verwenden Sie eine andere Persistenzimplementierung (z. B. Azure Table Storage) und dann eine neueCustomerRepository Klasse müsste erstellt werden und würde ein erbenAzureTableStorageRepository<T>. Aber dies könnte zu redundantem Code führen (mehrere CustomerRepositories)? Wie würde sich das spöttisch auswirken?

Eine andere Implementierung (z. B. Azure Table Storage) hat Einschränkungen hinsichtlich der transnationalen Unterstützung, sodass eine AzureTableStorageUnitOfWork-Klasse in diesem Kontext nicht funktioniert.

Gibt es andere Probleme mit der Art und Weise, wie ich das gemacht habe?

Ich habe den größten Teil meiner Inspiration vondieser Beitrag)

Antworten auf die Frage(3)

Ihre Antwort auf die Frage