首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >分区IOC容器

分区IOC容器
EN

Stack Overflow用户
提问于 2015-03-26 22:41:22
回答 1查看 92关注 0票数 1

我目前正在移植一个大型的WIN应用程序,这样我就可以将它扩展到MVC。其中一个原则是它支持多个DAL,这些DAL由fluent config动态绑定。例如

代码语言:javascript
复制
interface IDataStore
{
}

class SqlRepository :IDataStore
{
//do sql stuff
} 

class VistaDBRepository : IDataStore
{
//do vista db stuff
}

最后

代码语言:javascript
复制
class WCFClientRepository :IDataStore
{
//do WFC stuff
}

要在业务逻辑中使用此实现,应如下所示

代码语言:javascript
复制
public IDataStore GetCurrentStore()
{
    get
    {
        switch(EnterpriseConfiguration.Instance.ActiveProvider)
        {
            case "MSSQL":
               return new SqlRepository();
            case "VistaDB":
               return new VistaDBRespository();
            case "WFC":
               return new WCFReposity();
         }
    }
}

void GetDate()
{
   using(IDataStore db = GetCurrentStore())
   {
      //do something with db
   }
}

由此,存在将在主db提供商(SQL)和移动提供商(WCF)之间监视的连接监听器。如果主提供商的连接断开或不可用,则它将选择次提供商(WFC)作为主提供商。这是为了支持移动用户,效果很好,在局域网内的用户直接转到SQL,当他们离开时会自动切换到WFC。

最后,VistaDB被用作持久性存储(插件信息、表单元数据、位置、国家等)

我想用IOC容器替换这个模式,我的想法是为每个定义的IDataStore构建一个容器,该容器可以绑定到一个名称,进而获得消费

代码语言:javascript
复制
SQLReposity db = Container.Resolve<IDataStore>().ContainerName("MSSQL");
VistaDBReposity db = Container.Resolve<IDataStore>().ContainerName("VistaDB");
WFCReposity db = Container.Resolve<IDataStore>().ContainerName("WCF");

对使用Unity或StructureMap构建容器有什么想法吗?

EN

回答 1

Stack Overflow用户

发布于 2015-03-26 23:22:03

尽管您可以为应用程序创建多个容器,但通常不鼓励这样做。拥有多个容器可能会使您的DI配置复杂化,因为在容器之间共享实例会更加困难,并且容器将更难帮助您验证DI配置。

一般来说,我只会在应用程序由多个独立的模块组成的情况下使用多个容器,这些模块几乎没有任何共享。在所有其他场景中,使用单个全局容器实例。

在查看您的设计时,我的第一个想法是:为什么业务层必须知道有多个IDatabase实现?您的设计似乎通过公开GetCurrentStore()方法来传达这一点。

相反,我会选择让它对业务层透明和隐藏。这样做会使消费代码变得更简单,因为它不必知道有多个商店。这使得API更小,因此更简单。更容易阅读,更容易测试。

使其透明意味着您必须创建一个能够分派和回退到正确实现的IDatabase实现。这意味着这个“dispatcher”可以在内部调用GetCurrentStore(),但可以对应用程序的其余部分隐藏这一点。这也可能是实现重试逻辑的地方,也可能是实现回退行为的地方。

我想象这个实现看起来像这样:

代码语言:javascript
复制
public class DatabaseDispatcher : IDatabase
{
    private readonly SQLReposity primaryStore;
    private readonly WFCReposity fallbackStore;
    private readonly VistaDBReposity persistentStore;

    public DatabaseDispatcher(
        SQLReposity primaryStore,
        WFCReposity fallbackStore,
        VistaDBReposity persistentStore) {
        this.primaryStore = primaryStore;
        this.fallbackStore = fallbackStore;
        this.persistentStore = persistentStore;
    }

    // IDatabase methods here. Example:
    public TResult Execute<TResult>(IQuery<TResult> query)
        try {
            return GetCurrentStore().Execute<TResult>(query);
        } catch (Exception ex) {
           // Decide what to do here. Is primary store offline? Then fallback
        }
    }

    private IDatabase GetCurrentStore() {
       // complex fallback logic here.
    }
}

这个DatabaseDispatcher实现是可以放在Composition Root中的一块基础设施。您可能有多个具有不同调度策略的应用程序。如您所见,此实现依赖于具体的存储库类型。这使得注册/解析此类型非常容易,如下所示:

代码语言:javascript
复制
// Ninject
kernel.Bind<IDatabase>().To<DatabaseDispatcher>();

// Unity
container.RegisterType<IDatabase, DatabaseDispatcher>();

// Simple Injector
container.Register<IDatabase, DatabaseDispatcher>();

DatabaseDispatcher仅依赖于抽象也是可能的,您可以很容易地做到这一点,而无需使用命名注册:

代码语言:javascript
复制
// Ninject
kernel.Bind<IDatabase>().ToMethod(c => new DatabaseDispatcher(
    primaryStore: kernel.Get<SQLReposity>(),
    fallbackStore: kernel.Get<WFCReposity>(),
    persistentStore: kernel.Get<VistaDBReposity>()));

// Unity
container.Register<IDatabase>(new InjectionFactory(c => new DatabaseDispatcher(
    primaryStore: c.Resolve<SQLReposity>(),
    fallbackStore: c.Resolve<WFCReposity>(),
    persistentStore: c.Resolve<VistaDBReposity>())));

// Simple Injector
container.Register<IDatabase>(() => new DatabaseDispatcher(
    primaryStore: container.GetInstance<SQLReposity>(),
    fallbackStore: container.GetInstance<WFCReposity>(),
    persistentStore: container.GetInstance<VistaDBReposity>()));

这允许您拥有一个DI容器实例,这可以极大地降低您的Composition的复杂性。

我注意到的另一件事是,您的IDatabase抽象似乎实现了IDisposable。这使得IDatabase成为leaky abstraction - Dependency Inversion Principle冲突的一种特定形式,因为:

抽象不应该依赖于细节。细节应该取决于抽象。

有任何东西要处理的事实是一个实现细节,消费者不应该知道这一点。所以你应该只在实现上实现IDisposable,如果所有的实现都需要处理的话。让容器管理数据库实例的生命周期,而不是让消费者自行处理。尤其是因为您可能希望在请求期间缓存这些数据库实例,所以您不希望向使用者公开Dispose方法,因为这将允许他们在请求尚未完成时处理该实例,并且该实例仍需要由其他代码使用。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/29281364

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档