IOC с несколькими базами данных, использующими один и тот же интерфейс (StructureMap или любой другой DI Framework) - PullRequest
2 голосов
/ 15 ноября 2009

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

public class SomeController : Controller
{
    private ISomeService _service;
    private IClientRepository _repository;
    protected IContext _masterContext;
    protected IContext _clientContext;

    public SomeController(ISomeService service, ISomeRepository repository
        , IContext masterCon, IContext clientCon)
    {
        _service = service;
        _repository = repository;
        _masterContext = masterCon;
        _clientContext = clientCon;
    }
}

public class SomeService : ISomeService
{
    private IContext _masterContext;
    private IContext _clientContext;

    public SomeService(IContext masterContext, IContext clientContext)
    {
        masterContext = _masterContext;
        clientContext = _clientContext;
    }
}

public class ClientRepository : IClientRepository
{
    private IContext _clientContext;

    public ClientRepository(IContext clientContext)
    {
        _clientContext = clientContext;
    }
}

public class MasterContext : IContext
{
    public MasterContext(String connString)
    //<snip, snip> implement 3rd party data context
}

public class ClientContext : IContext
{
    public ClientContext(String connString)
    //<snip, snip> implement 3rd party data context
}

StructureMap работал ОТЛИЧНО, когда у нас был один контекст (база данных), но как мне сказать, как разрешить второй? Примечание: в большинстве случаев у нас не было бы службы, обрабатывающей 2 базы данных (но мог бы быть контроллер, обрабатывающий 2 соединения, то есть 2 хранилища, обращающихся к 2 различным базам данных), но, похоже, это не облегчает ситуацию.

Я наполовину готов отказаться от использования инфраструктуры IoC и вернуться к DI бедняка.

Ответы [ 3 ]

2 голосов
/ 15 ноября 2009

Разве невозможно иметь IClientContext и IMasterContext, возможно, наследуя от IContext. Мне кажется, что код будет выполнять одну из двух совершенно разных вещей, в зависимости от того, говорите ли вы с базой данных «Мастер» или «Клиент».

1 голос
/ 15 ноября 2009

Это может быть немного сложно, если вы полагаетесь на StructureMap для автоматического разрешения зависимостей. Первое решение (и к чему я бы ошибался) - это использовать интерфейсы маркеров, которые Ричард упоминает в своем ответе, а затем просто зарегистрировать их. Затем вы можете явно указать, хотите ли вы, чтобы ваш клиент или основной контекст были там.

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

ForRequestedType<IContext>().AddInstances(
    i => {
        i.OfConcreteType<ClientContext>().WithName("Client");
        i.OfConcreteType<MasterContext>().WithName("Master");
    });
ForRequestedType<SomeController>().TheDefault.Is.ConstructedBy(
    i => new SomeController(i.GetInstance<ISomeService>(), 
        i.GetInstance<IClientRepository>(), 
        i.GetInstance<IContext>("Master"), 
        i.GetInstance<IContext>("Client")));

Не особенно хорошо, но он делает свою работу и, в конечном счете, если он находится только в одном или двух местах, он может быть в порядке.


Если вы хотите по-разному разрешить пространство имен / сборку, вы можете попробовать что-то вроде этого: -

ForRequestedType<IContext>().AddInstances(
    i => {
         i.OfConcreteType<ClientContext>().WithName("Client");
         i.OfConcreteType<MasterContext>().WithName("Master");
     }).TheDefault.Is.Conditional(c => {
         c.If(con => con.ParentType.Namespace.EndsWith("Client"))
          .ThenIt.Is.TheInstanceNamed("Client");
         c.If(con => con.ParentType.Namespace.EndsWith("Master"))
          .ThenIt.Is.TheInstanceNamed("Master");
         c.TheDefault.Is.OfConcreteType<ClientContext>();
     });

Где предикат для ParentType может ссылаться на Assembly (или как вам угодно)

1 голос
/ 15 ноября 2009

В Unity вы можете иметь именованные регистрации, что позволяет эффективно регистрировать больше, чем класс для данного интерфейса. Так что вы могли бы сделать (набрав наизусть, проверьте актуальную документацию Unity, если интересно):

container.RegisterType<IContext, MasterContext>("Master");
container.RegisterType<IContext, ClientContext>("Client");

и тогда конструктор для SomeService будет:

public SomeService(
    [Dependency("Master")]IContext masterContext, 
    [Dependency("Client")]IContext clientContext)
{
    //...
}

Недостатком является то, что таким образом ваш класс обслуживания больше не зависит от используемой инфраструктуры DI, а зависит от проекта, который может быть в порядке.

...