1

У меня есть приложение, которое прослушивает сообщения в очереди и обрабатывает указанные сообщения.Переключение баз данных с использованием DI/StructureMap

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

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

Это все хорошо, если я телеграфировать все вручную ....

var data = new MyData(message.Client); 
var processor = new MessageProcessor(new Foo(data), new Bar(data)); 
processor.Process(message); 

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

using System; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.Linq; 
using StructureMap; 

namespace M.Test 
{ 
    public interface IData 
    { 
     IEnumerable<string> Names { get; } 
    } 

    public class MyData : IData 
    { 
     private readonly string _client; 

     public MyData(string client) 
     { 
      _client = client; 
     } 

     public IEnumerable<string> Names 
     { 
      get { return _client == "Client A" ? new[] {"One", "Two", "Three"} : new[] {"Uno", "Dos", "Tres"}; } 
     } 
    } 

    public interface IFoo 
    { 
     void FooDo(); 
    } 

    public class Foo : IFoo 
    { 
     private readonly IData _dataContext; 

     public Foo(IData dataContext) 
     { 
      _dataContext = dataContext; 
     } 

     public void FooDo() 
     { 
      Debug.WriteLine(_dataContext.Names.First()); 
     } 
    } 

    public interface IBar 
    { 
     void BarDo(); 
    } 

    public class Bar : IBar 
    { 
     private readonly IData _data; 

     public Bar(IData data) 
     { 
      _data = data; 
     } 

     public void BarDo() 
     { 
      Debug.WriteLine(_data.Names.Last()); 
     } 
    } 

    public interface IMessageProcessor 
    { 
     void Process(Message message); 
    } 

    public class MessageProcessor : IMessageProcessor 
    { 
     private readonly IFoo _foo; 
     private readonly IBar _bar; 

     public MessageProcessor(IFoo foo, IBar bar) 
     { 
      _foo = foo; 
      _bar = bar; 
     } 

     public void Process(Message message) 
     { 
      _foo.FooDo(); 
      _bar.BarDo(); 
     } 
    } 

    public class Message 
    { 
     public string Client { get; set; } 
    } 

    class Program 
    { 
     private static Container _container; 

     static void Main(string[] args) 
     { 
      _container = new Container(); 
      _container.Configure(x => 
      { 
       x.For<IData>().Use<MyData>(); 
       x.For<IMessageProcessor>().Use<MessageProcessor>(); 
       x.For<IBar>().Use<Bar>(); 
       x.For<IFoo>().Use<Foo>(); 
      }); 

      MessageReceived(new Message {Client = "Client A"}); 
      MessageReceived(new Message {Client = "Client B"}); 
      MessageReceived(new Message {Client = "Client A"}); 

      Console.ReadKey(); 
     } 

     private static void MessageReceived(Message message) 
     { 
      // Fine if I do this... 
      var data = new MyData(message.Client); 
      var processor = new MessageProcessor(new Foo(data), new Bar(data)); 
      processor.Process(message); 

      // But would like to do this... 
      var diProcessor = _container.TryGetInstance<IMessageProcessor>(); 
      diProcessor.Process(message); 
     } 
    } 
} 

Из того, что я прочитал, я вижу, что параметры конструктора могут использоваться с StructureMap. Например:

x.For<IData>().Use<MyData>().Ctor<string>("client").Is(someValueAtRunTime); 

... но не могу понять, когда/как someValueAtRunTime должен быть установлен, и все, что я пытаюсь я вопрос, как поточно это.

Любая помощь приветствуется.

ответ

1

Обычный шаблон для достижения такой конфигурации, как это, заключается в использовании abstract factory pattern. (См. Аналогичный вопрос here)

Отремонтированный завод по существу является обычным заводским объектом, который отвечает за возврат правильной реализации базы данных на основе вашей конфигурации времени выполнения, однако вместо возвращаемого конкретного типа ваш заводский метод возвращает абстракцию в виде интерфейса или абстрактного класса).

Вы зарегистрировать этот класс фабрики с StructureMap поэтому он может также его зависимость управляется через StructureMap так:

public class StorageRegistry : Registry 
{ 
    public StorageRegistry() 
    { 
     ... 
     this.For<IDataStoreInstance>().Use(ctx => ctx.GetInstance<DataStoreAbstractFactory>().ConfigureStorage()); 
     ... 
    } 
} 

Фабрика класс будет выглядеть примерно так (этот пример взят из ответа ссылки aboven но вы можете ясно видеть, как это было бы скомпоновать в вашем обстоятельстве.

public class DataStoreAbstractFactory 
{ 
    public DataStoreAbstractFactory() 
    { 
     // Dependencies to figure out data storage method can be injected here. 
    } 

    public IDataStoreInstance ConfigureStorage() 
    { 
     // This method can be used to return type of storage based on your configuration (ie: online or maintenance) 
    } 
} 

public interface IDataStoreInstance 
{ 
    void Save(); 
} 

public class DatabaseStorage : IDataStoreInstance 
{ 
    public void Save() 
    { 
     // Implementation details of persisting data in a database 
    } 
} 

public class FileStorage : IDataStoreInstance 
{ 
    public void Save() 
    { 
     // Implementation details of persisting data in a file system 
    } 
} 

Я надеюсь, что это помогает.