Использование DI для кэширования запроса на время жизни приложения - PullRequest
1 голос
/ 17 февраля 2011

Использование 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();
 }
}

Это плохой подход? И если да, может кто-нибудь порекомендовать более умный?

1 Ответ

2 голосов
/ 17 февраля 2011

Я бы вообще не использовал static ness и проверку на нуль из вашего кода - создавайте нормальные классы без одноэлементной разводки по умолчанию и размещайте этот аспект поверх контейнера. То же самое, снимите зависимость от инъекции свойства - инъекция 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 стать провайдером или стать участником.

Что касается самого вопроса ... Большой вопрос в том, как и когда вы хотите контролировать очистку кэша для принудительной перезагрузки, если вы решили, что кэширование должно быть на уровне сеанса, а не на уровне приложения из-за влияния авторизации на шаблон дерева? В общем, я бы сказал, что это должно быть заботой о реальном классе, а не быть привязанным к проводке вашего DI или жестко привязанным к тому, является ли класс статическим или одноэлементным (как в шаблоне проектирования, а не в целевой области).

Я бы хотел иметь класс TemplateManager без статических методов и сделать его одноэлементным классом в контейнере. Однако, чтобы получить корневой шаблон, потребители должны вставить TemplateManager (через ctor), а затем сказать _templateManager.GetRootTemplate (), чтобы получить шаблон.

Таким образом, вы можете:

  1. не полагайтесь на провайдеров фантазии и / или привязывайте себя к своему контейнеру
  2. не имеет синглтон-флюта или статических методов
  3. имеет простую логику кэширования в TemplateManager
  4. изменить область видимости менеджера без изменения всего клиентского кода
  5. ясно, что получение шаблона может быть, а может и не быть простой операцией get

То есть я бы справился так:

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 и единицей работы. То есть наличие репозитория и менеджера, ни один из которых не выполняет ничего четко определенного самостоятельно, не является хорошим знаком - репозиторий должен быть не просто шлюзом табличных данных - он должен быть тем местом, где кэшируется агрегированный корень, такой как шаблоны и сопоставлены вместе. Но я должен был бы узнать намного больше о вашей кодовой базе, прежде чем выделять подобные вещи без контекста!

...