代码之家  ›  专栏  ›  技术社区  ›  Beep beep

具有使用相同接口(StructureMap或任何其他DI框架)的多个数据库的IOC

  •  2
  • Beep beep  · 技术社区  · 16 年前

    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 回复  |  直到 16 年前
        1
  •  2
  •   Richard Nienaber    16 年前

    不可能有一个 IClientContext IMasterContext ,可能继承自 IContext

        2
  •  1
  •   Konamiman    16 年前

    在里面 Unity 您可以进行命名注册,从而可以有效地为给定接口注册多个类。因此,您可以这样做(用心输入,如果感兴趣,请查看实际的Unity文档):

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

    然后,某个服务的构造函数将是:

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

    缺点是,通过这种方式,您的服务类不再独立于所使用的DI框架,而是取决于可能合适的项目。

        3
  •  1
  •   John Foster    16 年前

    如果依赖StructureMap自动解析依赖项,这可能会有点困难。第一个解决方案(我会犯错误)是使用Richard在回答中提到的标记接口,然后注册它们。然后,您可以显式地指定是希望客户机上下文还是主上下文出现在那里。

    第二种方法是使用命名注册,然后显式指定构造函数参数。

    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(或任何您真正想要的)