6

у меня есть службы WCF и на клиенте я имею:Dependency Injection с несколькими хранилищами

var service = new ServiceReference1.CACSServiceClient() 

Фактический код обслуживание:

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

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

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

+1

+1 для «новичка» – Jacob

ответ

0

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

Пользователь может затем назначить их, если необходимо, в противном случае.

Например:

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

ли ваши репозитории есть состояние объекта уровня? Наверное, нет, поэтому создавайте их как одиночные и располагайте контейнер 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(); 
+3

Не нужно прибегать к синглтонам и конструкторам по умолчанию. WCF отлично поддерживает ** Constructor Injection **. –

+0

Эта реализация звучит круто, я дам ей шанс, а также попробую, что сказал Марк Симан о ленивой загрузке репозитория. – user282807

+0

Друзья не позволяют друзьям писать одиночные игры. –

0

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

Если вы хотите настроить приложение, это значит, что вы можете изменять способ построения графического объекта. В действительно простых выражениях, если вы хотите изменить реализацию чего-либо (например, иногда вам нужен экземпляр UserRepository, в других случаях вам нужен экземпляр MemoryUserRepository), тогда тип использует. Реализация (CACService в этом случае) должна не взимать с него новизну. Каждое использование new связывает вас с конкретной реализацией. Misko has written some nice articles about this point.

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

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

Вы можете выбрать телеграфировать вещи по-разному, основываясь на таких вещах, как:

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

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

E.g. вот один из относительно простых способов сделать это с использованием заводского интерфейса - мы просто обновляем любую фабрику, подходящую для наших нужд, и вызываем ее метод Create. Мы используем контейнер Injection Dependency (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()); 
    }  
} 
16

красота чистого 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 будет только инициализирован, если это необходимо.

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

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

+2

согласен. Не ограничивайте себя по умолчанию ctor, потому что это то, что предлагает WCF. используйте IInstanceProvider для создания экземпляра службы. –

+0

Thankx Отметьте ответ. У меня может быть 10 или 20 репозиториев для моих классов домена, я должен выполнять ленивую загрузку для каждого? это будет много кода. – user282807

+0

Хорошо, один из моих основных моментов состоит в том, что вам, возможно, не придется это делать вообще. Внимательно изучите, может ли это быть преждевременная оптимизация или нет. В качестве одного примера, если вы используете LINQ to SQL или LINQ для Entities, вы вполне можете в конечном итоге реализовать все свои репозитории на основе одного и того же контекста, и в таком случае накладные расходы на создание нескольких экземпляров репозитория могут быть незначительными. –