Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Help with Windsor and Repository and Unit of Work patterns

I have these interfaces:

public interface IUnitOfWork
{
    IPersonRepository People { get; }
    IBookRepository Books { get; }
    int Commit();
}

public interface IBookRepository
{
    Book GetBookById(int id);
    IQueryable<Book> GetAllBooks();
}

public interface IPersonRepository
{
    Person GetPersonById(int id);
    IQueryable<Person> GetAllPeople();
}

I implement IUnitOfWork as follow:

public class SqlUnitOfWork : IUnitOfWork
{
    private readonly DbContext dbContext;

    public SqlUnitOfWork()
    {
        dbContext = new DbContext("name=SQLContainer");
    }

    public IPersonRepository People
    {
        get { return IoC.Container.Resolve<IPersonRepository>(new { DbContext = dbContext }); }
    }

    public IBookRepository Books
    {
        get { return IoC.Container.Resolve<IBookRepository>(new { DbContext = dbContext }); }
    }

    public int Commit()
    {
        return dbContext.SaveChanges();
    }
}

The implementations of IBookRepository and IPersonRepository uses a constructor that takes a DbContext as a parameter, and this DbContext is created in the SqlUnitOfWork (code above) and I pass this parameter using an overload of the Resolve method.

My question is, is this the right way to do it? Is this a good practice?

Thanks!

like image 963
Gustav Avatar asked Aug 04 '11 01:08

Gustav


People also ask

What is repository pattern and unit of work?

The repository and unit of work patterns are intended to create an abstraction layer between the data access layer and the business logic layer of an application.

What is the repository pattern?

The Repository pattern. Repositories are classes or components that encapsulate the logic required to access data sources. They centralize common data access functionality, providing better maintainability and decoupling the infrastructure or technology used to access databases from the domain model layer.

What are the benefits of repository pattern?

Benefits of Repository Pattern It centralizes data logic or business logic and service logic. It gives a substitution point for the unit tests. Provides a flexible architecture. If you want to modify the data access logic or business access logic, you don't need to change the repository logic.

Should I use unit of work pattern?

Ideally to completely decouple the business logic layer from persistence layer, unit of work pattern can be used as it can easily coordinate the writing out of changes made to objects using repository pattern. So, saving changes is the responsibility of unit of work.


1 Answers

Using a DI Container as a Service Locator can hardly be said to be good practice. In addition to that, passing the DbContext to the container while resolving an interface is a Leaky Abstraction because it implies that you know something about the concrete implementation that you should not.

Instead I would recommend Constructor Injection, which would go something like this:

public class SqlUnitOfWork : IUnitOfWork
{
    private readonly DbContext dbContext;
    private readonly IPersonRepository personRepository;
    private readonly IBookRepository bookRepository;

    public SqlUnitOfWork(DbContext dbContext,
         IPersonRepository personRepository, IBookRepository bookRepository)
    {
        if (dbContext == null)
            throw new ArgumentNullException("dbContext");
        if (personRepository == null)
            throw new ArgumentNullException("personRepository");
        if (bookRepository = null)
            throw new ArgumentNullException("bookRepository");

        this.dbContext = dbContext;
        this.personRepository = personRepository;
        this.bookRepository = bookRepository;
    }

    public IPersonRepository People
    {
        get { return this.personRepository; }
    }

    public IBookRepository Books
    {
        get { return this.bookRepository; }
    }

    public int Commit()
    {
        return this.dbContext.SaveChanges();
    }
}

Even though there's no explicit sharing of DbContext, this can be configured through the container. Since the context of this question indicates that Castle Windsor is the container being used, the default lifetime is already Singleton, so you don't have to explicitly set this up. With Castle Windsor, the DbContext will automatically be shared between the SqlUnitOfWork class and both repositories.

However, you can also explicitly configure the context to be shared, like this:

container.Register(Component.For<DbContext>().LifeStyle.Singleton);

If you were to use another DI Container, the API would be different, but the concept is the same.

Bonus info: I don't know what the overall context is, but if this is to be used in a web application and DbContext is an Entity Framework or LINQ to SQL context, the correct lifetime configuration would instead be PerWebRequest as none of those context classes are thread-safe:

container.Register(Component.For<DbContext>().LifeStyle.PerWebRequest);
like image 97
Mark Seemann Avatar answered Oct 06 '22 08:10

Mark Seemann