使用DI缓存应用程序生命周期的查询

时间:2011-02-17 16:33:35

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

使用DI容器(在这种情况下,Ninject)是可能的 - 或者更确切地说,明智在整个应用程序生命周期内缓存一个经常使用的对象(或者至少直到它是刷新的)?

举个例子,说我有一个Template。有许多Template个对象,但每个用户至少继承最低级别的对象。这是不可变的,如果不更新连接到它的所有东西,它将永远不会改变(因此它只会根据管理需求而改变,永远不会基于用户输入)。对于我知道不会改变的信息,一遍又一遍地查询数据库似乎是愚蠢的。

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

我已将ISessionFactorynHibernate)存储为单身人士。但这有点不同,因为它不包括对数据库的查询,只包括打开和关闭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();
 }
}

这是一种糟糕的做法吗?如果是这样,任何人都可以推荐一个更聪明的人吗?

1 个答案:

答案 0 :(得分:2)

我通常会避免在代码中使用static ness和null-checking - 默认情况下创建没有单例连接的普通类,并通过容器将该方面放在顶层。同上,不再依赖财产注入 - 除非你别无选择,否则注射总是更好

即:

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范围)。

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

这样,您可以:

  1. 不依赖于花哨的ninject提供者和/或将自己绑在容器上
  2. 没有单身琐事或静态方法
  3. TemplateManager
  4. 中有简单的缓存逻辑
  5. 在不更改所有客户端代码的情况下改变经理的范围
  6. 明确表示获取模板可能是也可能不是简单的获取操作
  7. 即,我会像这样管理它:

    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有一个存储库和一个Manager,它们都没有做好自己定义的任何事情,这不是一个好兆头 - 存储库不应该只是一个表数据网关 - 它应该能够成为聚合根(如模板)被缓存的地方并整理在一起。但是在没有上下文的情况下抛出类似的东西之前,我必须更多地了解你的代码库!