Сохранение состояния на уровне обслуживания

#.net #design-patterns #c#-3.0

#.net #шаблоны проектирования #c #-3.0

Вопрос:

У меня есть базовый класс обслуживания, и у меня есть куча сервисов, хорошая или плохая практика хранить данные в классе обслуживания?

Например:

 public interface IFunkyService
{
  void AddComment(int quoteid, string comment);
  void SetProirirty(int quoteid, Importance proirity);
}

public class FunkyService : CustomServiceBase, IFunkyService
{

 private readonly IRepo _repo;
 private readonly IUserSession _user;
 public FunkyService(IRepo repo, IUserSession user)
 {
  _repo = repo;
  _user = user;
 }


     public void SetProirirty(int quoteid, Important priority)
     {

      //get quote object then persists

     } 

    public void AddComment(int quoteid, string comment)
     {

      //get quote object then persists

     }

}
  

Могу ли я просто иметь частный метод, который хранит объект quote в классе?
например

 private Quote _quote {get;set;} //store on the class???

private void GetQuote(int quoteid)
{
 _quote = _repo.single(quoteid); //or whatever
}
  

Ответ №1:

Обратите внимание, что значение в классе будет существовать только до тех пор, пока сам объект service. Объект service создается и уничтожается при каждом запросе к сервису, поэтому Quote он будет существовать только в течение одного запроса. Единственная цель, которую я вижу для чего-то подобного, — это кэш для каждого запроса (т. Е. Во время одного запроса вы ссылаетесь на Quote объект пять раз. Вам нужно будет только один раз просмотреть его из резервного хранилища).

  1. Клиент отправляет запрос на сервер
  2. Сервер создает экземпляр FunkyService класса
  3. Клиентские вызовы GetQuote
  4. Сервер заполняется Quote в классе.
  5. Клиент завершает вызов.
  6. Сервер завершает запрос, удаляет FunkyService объект из (2).
  7. Значение Quote больше не сохраняется в классе, поскольку объект исчез.

Редактировать: похоже, причина, по которой вы хотите это сделать, заключается в том, что извлечение объекта цитаты выполняется в одном месте, но он не вызывается снова и снова (т. Е. Выполнение нескольких запросов к базе данных, когда требуется только один). Вы можете реализовать шаблон проектирования кэшируемых свойств, чтобы иметь кэш для каждого запроса без использования переменной класса. Это будет выглядеть примерно так:

 private Dictionary<int, Quote> _quoteCache = 
    new Dictionary<int, Quote>(); // cache storage - NEVER ACCESS DIRECTLY

public Quote GetQuote(int quoteid)
{
    // cache is invalid, so populate
    if (!_quoteCache.ContainsKey(quoteid))
    {
        _quoteCache.Add(quoteid, _repo.single(quoteid));
    }

    // return back to caller
    return _quoteCache[quoteid];
}
  

В приведенном выше примере в кэше хранится каждый уникальный идентификатор quoteid, полученный из базы данных. Поэтому, если вы вызываете GetQuote(5) пять раз подряд, оно извлекается из базы данных только _repo один раз. Однако, если вы вызываете GetQuote(6) , он снова отправляется в базу данных, потому что эта цитата недоступна из кэша. После этого и 5, и 6 все еще хранятся в кэше, пока запрос на обслуживание не будет завершен и удален.

Редактирование 2: в приведенном вами примере:

 var t = GetTags(_quote);
// then do something with t, I may pass to another method:
if(IsClosed(_quote)){} 
  

Вместо ссылки на переменную класса, пусть ваш репозиторий вернет Quote объект и передаст эту ссылку, например:

 private Quote GetQuote(int quoteid)
{
    return _repo.single(quoteid); //or whatever
}

// other code
var q = GetQuote(quoteid); // get the quote once
var t = GetTags(q);  // q doesn't have to retrieve from repo again
if (IsClosed(q)) {}
// etc.
  

Комментарии:

1. Самое большое преимущество для меня в этом — сохранить «Get me объект цитаты в одном месте», если я вызываю один метод или передаю объект другому методу, я не хочу каждый раз переустанавливать объект. В одном вызове я хочу выполнить свою задачу, если компромисс заключается в том, что я не могу выполнить другой вызов с этим объектом (после завершения 7. выше), это нормально. Что вы думаете?

2. Я не уверен, что я вас понимаю. Вы должны иметь возможность передавать Quote объект от метода к методу, не получая его из базы данных снова и снова, передавая его в качестве параметра. Если вы сохраняете его в классе, чтобы сохранить его как ссылку между методами, это на самом деле плохая идея (например, глобальные переменные). Можете ли вы добавить еще немного кода, демонстрирующего пример цепочки методов, которые будут вызываться?

3. У вас определенно может быть «Получить мне объект цитаты в одном месте», в этом нет ничего плохого. Но вместо того, чтобы сохранять его в глобальной переменной класса, верните его из метода private Quote GetQuote(int quoteid) { return _repo.single(quoteid); } . Затем передайте ссылку от метода к методу. Имеет смысл?

4. плохая идея … хорошо, я добавлю немного кода, да, я буду передавать объект из метода в метод, но я хочу сохранить объект quote в самом классе service только для этого запроса.

5. Зачем вам нужно хранить его в классе? Вы можете реализовать кэшируемый шаблон проектирования. Я добавлю пример к своему ответу. Редактировать: я обновил пример. Я сохраняю его в классе, как и вы, но к нему никогда не следует обращаться напрямую. Он используется только как кэш, поэтому все вызывающие по-прежнему используют GetQuote для получения объекта quote, а не _quote самого объекта. Имеет смысл?

Ответ №2:

Не уверен, применимо ли это в вашем случае, но при переключении службы без состояния (или вообще любого компонента) на состояние состояния, вам необходимо рассмотреть несколько вопросов:

  1. Потокобезопасность — вы должны сделать состояние неизменяемым, чтобы обеспечить потокобезопасный доступ к этому состоянию. Также имейте в виду, что большинство отслеживающих (отслеживающих состояние) ORM работают не так хорошо, когда к одним и тем же экземплярам обращаются из нескольких потоков.
  2. Рассмотрите возможность развертывания службы в нескольких экземплярах (скажем, для балансировки нагрузки) в будущем. Если состояние не является неизменяемым, вы можете получить несинхронизированные экземпляры для одного и того же объекта.
  3. Если причиной этого является кэширование и повышение производительности, вы можете разработать стратегию ортогонального кэширования, которая позволит избежать вышеуказанных проблем, например, с использованием поставщика кэша NHibernate или кэширования данных на прикладном уровне.

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

Комментарии:

1. Итак, даже если мне придется обращаться к базе данных несколько раз для объекта, это намного безопаснее, чем хранение экземпляра объекта? Я использую Linq To Sql, я не уверен, что поддержка кэширования хороша.

2. не могли бы вы сказать, что пример, приведенный @mellamokb, достаточно хорош? Я имею в виду все мои вызовы asp.net метод действия mvc обычно находится в одном веб-запросе (на самом деле пока не настроен ninject с этим), поэтому я не чувствую, что объекты будут не синхронизированы, как вы думаете? Чтобы подчеркнуть, я собираюсь вызывать службу и связанный с ней код только в одном веб-запросе, в другом веб-запросе я не против снова вызвать базу данных (в моей голове), это означает, что я потерял способность кэширования, но для меня это пока не так уж плохо … мысли?

3. Я провел только итоговое тестирование, но оно использует linq2sql реализует карту идентификаторов на уровне контекста, поэтому, если вы запрашиваете один и тот же контекст для одного и того же идентификатора несколько раз, выполняется только первый раз, когда выполняется запрос, а для остальной части запроса возвращается один и тот же экземпляр объекта — так долго, каквы выполняете запросы в том же экземпляре контекста данных L2S, вам не следует беспокоиться о сохранении состояния, поскольку linq делает это за вас. Как (другое) эмпирическое правило, не оптимизируйте без достоверных фактов и измеренных чисел.

4. Я предполагаю, что в моих сценариях я пока даже не пытаюсь оптимизировать, да, я слежу за этим, но я только хотел убедиться, что логика получения некоторых данных остается неизменной для всего этого класса, спасибо за всю эту информацию, действительно оценил 1

Ответ №3:

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

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

Точка зрения @mellamokb велика, поскольку одним из распространенных применений состояния в SOA является кэширование.