Внедрение зависимости с несколькими репозиториями

У меня есть wcf сервис, и на клиенте я имею:

var service = new ServiceReference1.CACSServiceClient()

Код практической эксплуатации:

public CACSService() : this(new UserRepository(), new BusinessRepository()) { }

public CACSService(IUserRepository Repository, IBusinessRepository businessRepository)
{
     _IRepository = Repository;
     _IBusinessRepository = businessRepository;
}

Так, все это хорошо работает, но мне не нравится, как я - newing все репозитории одновременно, потому что клиентскому коду, возможно, не понадобилось бы к новому UserRepository и только заинтересованный newing BusinessRepository. Так, есть ли способ передать в чем-то этому коду:
var service = new ServiceReference1.CACSServiceClient()
сказать это который репозиторий к новому на основе кода, который называет сервис или любой другой совет, о котором я должен пойти при разработке репозиториев для моей платформы объекта. Thankx

6
задан Robert Koritnik 31 July 2012 в 13:18
поделиться

4 ответа

Прелесть чистого DI в том, что вам не нужно беспокоиться о времени жизни ваших зависимостей , потому что они управляются за вас кто бы их ни предоставил (контейнер DI или другой код, который вы написали сами).

(Кстати, вам следует избавиться от ваших текущих конструкторов Bastard Injection .Выбросьте конструктор без параметров и оставьте тот, который явно объявляет о его зависимостях.)

Сохраните свой конструктор в таком виде и используйте _IRepository и _IBusinessRepository по мере необходимости:

public CACSService(IUserRepository Repository, IBusinessRepository businessRepository) 
{ 
    _IRepository = Repository; 
    _IBusinessRepository = businessRepository; 
} 

Если вы беспокоитесь, что один из этих репозиториев не понадобится во время выполнения вы можете внедрить реализацию с отложенной загрузкой, скажем, IUserRepsository вместо реальной, которую вы изначально имели в виду.

Предположим, что IUserRepository выглядит так:

public interface IUserRepository
{
    IUser SelectUser(int userId);
}

Теперь вы можете реализовать реализацию отложенной загрузки следующим образом:

public class LazyUserRepository : IUserRepository
{
    private IUserRepository uRep;

    public IUser SelectUser(int userId)
    {
        if (this.uRep == null)
        {
            this.uRep = new UserRepository();
        }
        return this.uRep.SelectUser(userId);
    }
}

Когда вы создаете CACService, вы можете сделать это, внедрив в него LazyUserRepository, что гарантирует, что настоящий UserRepository будет инициализирован только в случае необходимости.

Прелесть этого подхода в том, что вам не нужно делать это, пока оно вам не понадобится . Часто в этом действительно нет необходимости, поэтому приятно иметь возможность отложить такие оптимизации до тех пор, пока они действительно не потребуются.

Я впервые описал технику Ленивых зависимостей здесь и здесь .

16
ответ дан 8 December 2019 в 14:42
поделиться

Вместо того, чтобы создавать экземпляры («создание новых») репозиториев при создании, вы можете лениво загружать их в их свойствах. Это позволит вам сохранить второй конструктор, но первый конструктор ничего не сделает.

В противном случае пользователь мог бы назначить их по мере необходимости.

Например:

public class CACSService
{
    public CACSService() {}

    public CACSService(IUserRepository Repository, IBusinessRepository businessRepository)
    {
        _IRepository = Repository;
        _IBusinessRepository = businessRepository;
    }

    private IUserRepository _IRepository;
    public IUserRepository Repository
    {
        get {
             if (this._IRepository == null)
                  this._IRepository = new UserRepository();
             return this._IRepository;
        }
    }

   // Add same for IBusinessRepository
}
0
ответ дан 8 December 2019 в 14:42
поделиться

Есть ли у ваших репозиториев состояние на уровне объектов? Скорее всего, нет, поэтому создайте их как синглтоны и попросите DI-контейнер предоставить их CACService.

Иначе, действительно ли их создание требует больших затрат? Если нет, то создание нового хранилища на каждый запрос имеет незначительную стоимость по сравнению с операциями RPC и базы данных.

Используя контейнер инъекции зависимостей Ninject, ваш CACService может выглядеть следующим образом. Другие DI-контейнеры имеют столь же лаконичные механизмы для этого.

public class CACSService
{
    public CACService
    {
        // need to do this since WCF creates us
        KernelContainer.Inject( this );
    }

    [Inject]
    public IUserRepository Repository
    { set; get; }

    [Inject]
    public IBusinessRepository BusinessRepository
    { set; get; }
}

И во время запуска приложения вы сообщите Ninject об этих типах.

Bind<IUserRepository>().To<UserRepository>().InSingletonScope();
Bind<IBusinessRepository>().To<BusinessRepository>().InSingletonScope();
0
ответ дан 8 December 2019 в 14:42
поделиться

Предисловие : Это общее руководство по инверсии зависимостей. Если вам нужен конструктор по умолчанию для выполнения работы (например, если он был создан путем отражения или чего-то еще), тогда будет сложнее сделать это чисто.

Если вы хотите сделать свое приложение настраиваемым, это означает возможность изменять способ построения графа вашего объекта. Проще говоря, если вы хотите изменить реализацию чего-либо (например, иногда вам нужен экземпляр UserRepository , в других случаях вам нужен экземпляр MemoryUserRepository ), тогда тип, использует , реализация (в данном случае CACService ) не требует обновления. Каждое использование new связывает вас с определенной реализацией. Misko написал несколько хороших статей по этому поводу .

Принцип инверсии зависимостей часто называют «параметризацией сверху», поскольку каждый конкретный тип получает свои (уже созданные) зависимости от вызывающей стороны.

Чтобы применить это на практике, переместите код создания объекта из конструктора без параметров CACService и вместо этого поместите его в фабрику.

Затем вы можете выбрать другое подключение на основе таких вещей, как:

  • чтение файла конфигурации
  • передача аргументов фабрике
  • создание фабрики другого типа

Разделение типов на два категории (типы, которые создают вещи, и типы, которые делают вещи) - это мощная техника.

Например. Вот один относительно простой способ сделать это с использованием интерфейса фабрики - мы просто создаем новую фабрику, которая подходит для наших нужд, и вызываем ее метод Create . Мы используем контейнер внедрения зависимостей ( Autofac ), чтобы делать это на работе, но это может быть излишним для ваших нужд.

public interface ICACServiceFactory
{
    CACService Create();
}

// A factory responsible for creating a 'real' version
public class RemoteCACServiceFactory : ICACServiceFactory
{
    public CACService Create()
    {
         return new CACService(new UserRepository(), new BusinessRepository());
    }        
}

// Returns a service configuration for local runs & unit testing
public class LocalCACServiceFactory : ICACServiceFactory
{
    public CACService Create()
    {
         return new CACService(
               new MemoryUserRepository(), 
               new MemoryBusinessRepository());
    }     
}
0
ответ дан 8 December 2019 в 14:42
поделиться
Другие вопросы по тегам:

Похожие вопросы: