我有一个应用程序,它侦听队列中的消息并处理所述消息。
到目前为止,我已经为每个客户安装了一个单独的应用程序实例,每个客户在配置文件中都有自己的数据库连接字符串。在更新应用程序时,这变得很痛苦。
现在我试图重构这个,这样我就可以安装一个具有所有必要连接字符串的实例,并且会根据一些运行时参数在数据库之间切换 - 例如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的时间/方式,无论我尝试什么,我都会质疑线程的安全性。
任何帮助表示感谢。
答案 0 :(得分: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
}
}
我希望这会有所帮助。