Помощь с шаблонами Windsor и Repository и Unit of Work - PullRequest
5 голосов
/ 04 августа 2011

У меня есть следующие интерфейсы:

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();
}

Я реализую IUnitOfWork следующим образом:

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();
    }
}

В реализациях IBookRepository и IPersonRepository используется конструктор, который принимаетDbContext в качестве параметра, и этот DbContext создается в SqlUnitOfWork (код выше), и я передаю этот параметр, используя перегрузку метода Resolve.

Мой вопрос заключается в том, является ли это правильным способом сделать это?Это хорошая практика?

Спасибо!

1 Ответ

8 голосов
/ 04 августа 2011

Использование DI-контейнера в качестве Service Locator вряд ли можно назвать хорошей практикой . В дополнение к этому, передача DbContext в контейнер при разрешении интерфейса является Leaky Abstraction , потому что это подразумевает, что вы знаете что-то о конкретной реализации, чего не должны делать.

Вместо этого я бы порекомендовал Конструктор Инъекций , который будет выглядеть примерно так:

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();
    }
}

Несмотря на то, что нет явного совместного использования DbContext, это можно настроить через контейнер. Поскольку контекст этого вопроса указывает на то, что Castle Windsor является используемым контейнером, время жизни по умолчанию уже Singleton, поэтому у вас нет , чтобы явно настроить это. С Castle Windsor DbContext будет автоматически разделен между классом SqlUnitOfWork и обоими репозиториями.

Однако вы также можете явно настроить контекст для совместного использования, например:

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

Если бы вы использовали другой DI-контейнер, API был бы другим, но концепция та же.

Информация о бонусе: я не знаю, каков общий контекст, но , если , это должно использоваться в веб-приложении, а DbContext - это Entity Framework или контекст LINQ to SQL, правильный конфигурация времени жизни вместо этого будет PerWebRequest, поскольку ни один из этих классов контекста не является поточно-ориентированным:

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