ASP.NET Repository Pattern / Кэширование сервисного уровня - PullRequest
11 голосов
/ 25 апреля 2011

Я начинаю работать над инфраструктурой кэширования для моего сайта ASP.NET MVC. Проблема в том, что я не могу найти подходящее место для кэширования данных (кроме «везде»)

Сейчас моя архитектура выглядит так:

Контроллер -> Сервисный уровень -> Репозиторий. Хранилище использует Linq to SQL для доступа к данным.

Хранилище предоставляет универсальные методы, такие как Insert, GetById и GetQueryable, которые возвращают IQueryable, который сервисный уровень может дополнительно усовершенствовать.

Мне нравится идея поместить кэширование на уровень хранилища, так как на уровне службы не должно быть никакого значения, откуда берутся данные. Проблема, однако, заключается в недействительности кэша. Уровень обслуживания имеет больше информации о том, когда данные устаревают, чем хранилище. Например:

Предположим, у нас есть таблица Users и таблица Orders (канонический пример). Сервисный уровень предлагает такие методы, как GetOrder (int id), который вызовет уровень хранилища:

public Order GetOrder(int id)
{
    using(var repo = _repoFactory.Create<Order>())
    {
        return repo.GetById(id)
    }
}

или

repo.GetQueryable(order => order.Id == id && order.HasShipped == false).Single();

Если мы кешируем на уровне хранилища, кажется, что было бы очень ограниченно знать, когда изменились эти данные заказа. Предположим, что пользователь был удален, в результате чего все его заказы были удалены с помощью CASCADE. Служебный уровень может сделать недействительным кэш-память заказов, поскольку он знал, что пользователь был просто удален. Хранилище хотя (так как это единица работы), не будет знать. (Не обращайте внимания на тот факт, что мы не должны запрашивать заказы для удаленного пользователя, поскольку это всего лишь пример).

В других ситуациях я думаю, что это проявляется. Предположим, мы хотим получить все заказы пользователей:

repo.GetQueryable(order => order.UserId == userId).ToList()

Хранилище может кэшировать результаты этого запроса, но, если добавлен другой заказ, этот запрос больше не действителен. Только уровень сервиса знает об этом.

Также возможно, что мое понимание уровня хранилища неверно. Я как бы рассматриваю это как фасад вокруг источника данных (т. Е. Переходя с L2SQL на EF и т. Д., Сервисный уровень не знает о базовом источнике).

Ответы [ 2 ]

8 голосов
/ 25 апреля 2011

Реально вам понадобится еще один слой; слой кэширования данных. Он будет использоваться вашим сервисным уровнем при запросе данных. По такому запросу он решает, есть ли у него данные в кеше или ему нужно запросить соответствующий репозиторий. Аналогичным образом, ваш уровень обслуживания может сообщить этому новому уровню кэширования данных о признании недействительным (удаление определенного пользователя и т. Д.).

Что это может означать для вашей архитектуры, так это то, что ваш уровень кэширования данных будет реализовывать тот же интерфейс (интерфейсы), что и ваши репозитории. Довольно простая реализация кэширует данные по типу сущности и ключу. Однако, если вы используете более сложную ORM за кулисами (NHibernate, EF 4 и т. Д.), В качестве опции вам следует использовать кэширование.

2 голосов
/ 25 апреля 2011

Вы можете поместить событие в объекты, возвращаемые вашими репозиториями, и заставить репозиторий подписать аннулирование кэша на обработчик.

Например,

  public class SomethingRepository{
        public Something GetById(int id){
            var something = _table.Single(x=>x.id==id);
            something.DataChanged += this.InvalidateCache;
            return something;
        }

        public void InvalidateCache(object sender, EventArgs e){ 
            // invalidate your cache 
        }
  }

И ваш * 1006У объекта * должно быть событие DataChanged и некоторый публичный метод, чтобы ваш сервисный уровень мог вызвать его.Например,

  public class Something{
       private int _id;
       public int Id{
         get { return _id; }
         set {
            if( _id != value ) 
            {
              _id = value;
              OnDataChanged();
            }
         }
       }
       public event EventHandler DataChanged;
       public void OnDataChanged(){
            if(DataChanged!=null)
                 DataChanged(this, EventArgs.Empty);
       }
  } 

Итак, все, что нужно знать вашему сервисному уровню, - это то, что данные изменяются, а хранилище обрабатывает аннулирование кэша.

Я также предлагаю вам воспользоваться советом Вентавра ипоместите логику аннулирования кэша в отдельный сервис.Вам не нужно заходить так далеко, чтобы создать отдельный «слой кэширования данных», но логика будет чище, если хранить в другом классе.

...