我们一直在尝试使用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。
答案 0 :(得分:2)
是否无法拥有IClientContext
和IMasterContext
,可能会继承IContext
。我的感觉是,根据您是在与“主人”还是“客户”数据库交谈,代码会做两种截然不同的事情之一。
答案 1 :(得分:1)
在Unity中,您可以进行命名注册,从而可以为给定的接口有效地注册多个类。所以你可以这样做(如果感兴趣,请仔细输入,查看实际的Unity文档):
container.RegisterType<IContext, MasterContext>("Master");
container.RegisterType<IContext, ClientContext>("Client");
然后SomeService的构造函数将是:
public SomeService(
[Dependency("Master")]IContext masterContext,
[Dependency("Client")]IContext clientContext)
{
//...
}
缺点是,通过这种方式,您的服务类不再独立于所使用的DI框架,而是取决于可能正常的项目。
答案 2 :(得分:1)
如果依靠StructureMap自动解决依赖关系,这可能会有点困难。第一个解决方案(以及我的错误)是在他的回答中使用像Richard提到的标记接口,然后注册它们。然后,您可以明确指定是否需要客户端或主上下文。
第二种方法是使用命名注册,然后明确指定构造函数params。
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(或任何你想要的)