【问题标题】:Using DI to cache a query for application lifetime使用 DI 缓存应用程序生命周期的查询
【发布时间】:2011-06-29 05:44:11
【问题描述】:

使用 DI 容器(在本例中为 Ninject)是否有可能 - - 或者更确切地说,明智 在整个应用程序生命周期(或至少直到它被刷新)?

举个例子,假设我有一个Template。有许多Template 对象,但每个用户至少继承最低级别的对象。这是不可变的,并且在不更新与其连接的所有内容的情况下永远不会改变(因此它只会根据管理需求而改变,永远不会基于用户输入)。一遍又一遍地查询数据库以获取我知道没有改变的信息似乎很愚蠢。

缓存最好在我的 IoC 容器中完成,还是应该外包给其他东西?

我已经将ISessionFactory (nHibernate) 存储为单例。但这有点不同,因为它不包括对数据库的查询,只是打开和关闭 ISession 对象的后端。

所以基本上我会做这样的事情..

static class Immutable
{
 [Inject]
 public IRepository<Template> TemplateRepository { get; set; }

 public static ITemplate Template { get; set; }

 public void Initialize()
 {
  if(Immutable.Template == null)
  {
   Immutable.Template = TemplateRepository.Retrieve(1); // obviously better logic here.
  }
}

class TemplateModule : Module
{
 public void Load()
 {
   Bind<ITemplate>().ToMethod(() => Immutable.Initialize())InSingletonScope();
 }
}

这是一个糟糕的方法吗?如果是这样,谁能推荐一个更智能的?

【问题讨论】:

    标签: asp.net-mvc-3 singleton ioc-container ninject


    【解决方案1】:

    我通常会避免在您的代码中使用staticness 和空值检查 - 默认情况下创建没有单例接线的普通类,并通过容器将该方面分层。同上,消除对属性注入的依赖 - ctor 注入总是更好,除非你别无选择

    即:

    class TemplateManager
    {
     readonly IRepository<Template> _templateRepository;
    
     public TemplateManager(IRepository<Template> templateRepository)
     {
         _templateRepository = templateRepository;
     }
    
     public ITemplate LoadRoot()
     {
      return _templateRepository.Retrieve(1); // obviously better logic here.
     }
    }
    
    class TemplateModule : Module
    {
     public void Load()
     {
       Bind<ITemplate>().ToMethod(() => kernel.Get<TemplateManager>().LoadRoot()).InSingletonScope();
     }
    }
    

    然后我会质疑 TemplateManager 是否应该成为 ninject 提供程序或内联。

    至于实际问题...最大的问题是,如果您决定缓存应该在会话级别而不是应用级别,由于授权影响,您如何以及何时控制清除缓存以强制重新加载模板树?一般来说,我会说这应该是实际类的关注点,而不是绑定到您的 DI 接线或硬连线到一个类是静态类还是单例类(如在设计模式中,而不是 ninject Scope)。

    我的倾向是拥有一个没有静态方法的 TemplateManager 类,并将其作为容器中的单例类。但是,要获取根模板,消费者应该注入 TemplateManager(通过 ctor 注入),然后说 _templateManager.GetRootTemplate() 来获取模板。

    这样,您可以:

    1. 不依赖花哨的 ninject 提供程序和/或将自己绑定到您的容器
    2. 没有单例或静态方法
    3. TemplateManager中有简单的缓存逻辑
    4. 在不更改所有客户端代码的情况下更改管理器的范围
    5. 明确获取模板可能是也可能不是简单的获取操作

    也就是说,我会这样管理它:

    class TemplateManager
    {
     readonly IRepository<Template> _templateRepository;
    
     public TemplateManager(IRepository<Template> templateRepository)
     {
         _templateRepository = templateRepository;
     }
    
     ITemplate _cachedRootTemplate;    
     ITemplate FetchRootTemplate()
     { 
         if(_cachedRootTemplate==null)
               _cachedRootTemplate = LoadRootTemplate();
         return _cachedRootTemplate;
     }
    
     ITemplate LoadRoot()
     {
      return _templateRepository.Retrieve(1); // obviously better logic here.
     }
    }
    

    像这样注册它:

    class TemplateModule : Module
    {
     public void Load()
     {
       Bind<TemplateManager>().ToSelf().InSingletonScope();
     }
    }
    

    然后像这样消费它:

    class TemplateConsumer
    {
     readonly TemplateManager _templateManager;
    
     public TemplateConsumer(TemplateManager templateManager)
     {
         _templateManager = templateManager;
     }
    
     void DoStuff()
     {
          var rootTempalte = _templateManager.FetchRootTemplate();
    

    猜测:我还考虑在容器中没有可解析的单独 IRepository(并且 大概与工作单元有各种联系)。相反,我会让 TemplateRepository 成为一个寿命更长的东西,而不是与 ORM 层和工作单元耦合。 IOW 拥有一个存储库和一个管理器,它们都没有自己做任何明确定义的事情,这不是一个好兆头 - 存储库不应该只是一个表数据网关 - 它应该能够成为诸如模板之类的聚合根被缓存的地方并整理在一起。但是在没有上下文的情况下抛出类似的东西之前,我必须更多地了解你的代码库!

    【讨论】:

    • 所以你说要创建一个容器来获取管理器,但是管理器是独立于容器的,这样当ISession完成时可以处理管理器,但是容器( 'Consumer') 可以保留检索到的单例吗?
    • @Stacey:我想我是。总之,我实质上是在重申 SRP - 尝试将许多职责分开,例如生命周期(单例)、缓存、管理分层关系的遍历和数据访问到单独的概念 - 每件事应该只有一个主要的改变理由。所有这一切都必须保持在合理范围内 - 这并不意味着每件事都需要每次都成为婴儿班 - 你只需要寻找机会通过这样做来简化事情。
    • 一个容器(我只使用这个术语作为 DI 工具的通用术语)很容易成为一把金锤,可以同时满足所有这些需求的各个方面,但让它做所以意味着 a) 你最终得到了令人毛骨悚然的 DI 配置,由于所有这些重叠的目标/责任,这很难理解 b) 你没有积极地将代码中的重要概念转化为抽象,例如函数和类。
    • 简而言之,是的,我的意思是,如果您有一个执行缓存并且与生命周期无关的类,您可以在顶部分层生命周期/范围并轻松更改它 如果事实证明你想做一些事情,比如有办法在缓存数据变脏时转储缓存数据,则无需重构世界。关于缓存的最后一点是,一般来说,在测量/分析器告诉您需要它之前,您不应该这样做(即使在这种情况下它看起来像是一个打开和关闭的案例)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-19
    • 1970-01-01
    • 1970-01-01
    • 2012-08-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多