Я использую Unity как движок впрыска зависимостей. Он содержит интерфейсы / классы для классов типа «репозиторий» и «менеджер». Эти репозитории отвечают за получение / сохранение / обновление данных из / в БД, а менеджеры «знают» об отношениях объектов с другими компонентами / классами.
Реализована фабрика для получения экземпляра контейнера зависимостей (1 экземпляр на запрос). Когда создается какой-либо класс репозитория / менеджера, он получает контейнер зависимостей в качестве параметра конструктора и может при необходимости создавать все другие менеджеры / репозитории.
Есть 2 способа создания бизнес-сущностей:
- Когда данные извлекаются из репозитория БД, создаются экземпляры объектов и инициализируются соответствующими данными БД, среди прочего инициализируется поле «Контейнер».
- Когда объект создается в коде, который будет помещен в БД, среди прочего, он принимает параметр «IUnityContainer».
В результате в обоих случаях любой бизнес-объект имеет внутри контейнер зависимостей, и если ему нужно получить какие-либо данные, он может получить экземпляр соответствующего менеджера и запросить требуемые данные.
Постановка проблемы: на прошлой неделе я прочитал несколько вопросов / ответов по SO, в которых говорится примерно следующее:
- экземпляры объектов не должны иметь доступа к контейнеру зависимостей;
- вместо этого они должны получить все необходимые интерфейсы в конструкторе.
Думаю, то же самое применимо и к классам менеджера / репозитория: я не должен передавать экземпляр контейнера их конструктору, вместо этого я должен помещать интерфейсы к другим необходимым компонентам.
Мне кажется разумным, но:
Мне потребуется, чтобы любой объект предоставлял ВСЕ интерфейсы, которые ему нужны (обычно каждому менеджеру / репозиторию / сущности требуется 3-5 из них);
Очень много случаев, когда требуется всего 1-2 интерфейса (поэтому я не хочу создавать много других);
Вопрос 1: действительно ли это хороший способ «скрыть» контейнер? Почему? (на самом деле, мне кажется, я знаю почему, но если у вас есть хороший ответ, сообщите, пожалуйста).
Вопрос 2. Как лучше всего решить эту проблему?
Вопрос 3: в моей конкретной реализации, когда мои объекты извлекаются из БД (я использую Linq2Sql и думаю о переключении на EF), я не могу создавать объекты с помощью DependencyContainer, я должен сделать это сам (потому что объекты создаются с использованием выражения, которое выполняется на сайте SQL, будет вызываться конструктор без параметров со следующими данными, назначенными общедоступным свойствам; и контейнер зависимостей недоступен на стороне SQL). Есть ли обходной путь для этого?
Технические детали моей реализации:
Пример конструктора базового класса Manager:
public abstract class ManagerBase : IManager
{
protected ManagerBase(IUnityContainer container)
{
DependancyContainer = container;
}
protected readonly IUnityContainer DependancyContainer;
...
}
Пример базового класса репозитория:
public abstract class RepositoryBase<T, TDb> : IRepository<T>
where T : IEntity
where TDb : class, IDbEntity, new()
{
protected abstract ITable<TDb> GetTable();
public IQueryable<T> GetAll()
{
return GetTable().Select(GetConverter());
}
Пример извлечения данных из БД: репозиторий создает экземпляры объектов и инициализирует их соответствующими данными БД, среди прочего инициализируется поле «Контейнер»:
public class CountryRepository
: RepositoryBase<ICountry, DbData.Country>, ICountryRepository
{
protected override Expression<Func<DbData.Country, ICountry>> GetConverter()
{
return dbEntity => new Country
{
DependancyContainer = DependancyContainer,
Code = dbEntity.CountryCode,
Name = dbEntity.CountryName,
};
}
Когда требуется получить данные из БД, вызывается следующее:
public class CountryManager : ManagerBase
{
ICountry GetCountryById(int countryId)
{
ICountryRepository repository = DependancyContainer.Resolve<ICountryRepository>();
return repository.GetAll()
.Where(country=>country.Id==countryId)
.SingleOrDefault()
;
}
}
CountryManagerдолжен расширятьManagerBase? - person James Webster   schedule 18.03.2011