在服务层中存储状态

时间:2011-10-07 14:33:23

标签: .net design-patterns 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

     }

}

我可以简单地使用一个私有方法将引用对象存储在类中吗? e.g。

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

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

3 个答案:

答案 0 :(得分:2)

请注意,只要服务对象本身,类中的值才会生效。每次向服务发出请求时,都会创建和销毁服务对象,因此Quote仅在单个请求的长度内生效。我可以看到的唯一目的是每个请求缓存(即,在单个请求期间,您引用Quote对象五次。您应该只需要从后备存储中查找一次)

  1. 客户端向服务器发出请求
  2. 服务器实例化FunkyService
  3. 客户来电GetQuote
  4. 服务器在类中填充Quote
  5. 客户完​​成通话。
  6. 服务器完成请求,从(2)处理FunkyService对象。
  7. Quote的值不再存储在类中,因为对象已消失。
  8. 编辑:您希望这样做的原因是,检索引用对象是在一个地方完成的,但不会一遍又一遍地调用(即,多次调用)只需要一个请求到数据库)。您可以实现可缓存属性设计模式,以便在不使用类变量的情况下拥有每个请求缓存。它看起来像这样:

    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仍然存储在缓存中,直到服务请求完成并处理完毕。

    Edit2:在您提供的示例中:

    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 :(得分:1)

不确定这是否适用于您的情况,但是当将无状态服务(或通常任何组件)切换为有状态时,您需要考虑一些问题:

  1. 线程安全 - 您必须使状态不可变,以确保线程安全访问该状态。还要记住,当从多个线程访问相同的实例时,大多数跟踪(状态跟踪)ORM都不能很好地完成。
  2. 考虑将来可能在多个实例(例如负载平衡)中部署服务。如果状态不是不可变的,则最终可能会导致同一实体的实例不同。
  3. 如果这样做的原因是缓存和性能改进,您可以设计一个可以避免上述问题的正交缓存策略 - 例如使用NHibernate缓存提供程序或在应用程序层缓存数据。
  4. 根据经验,尽量保持代码无状态。使用它更安全,允许您通过执行异步操作来提高性能,我也认为其他人更容易理解它。当您需要保持状态,特别是共享状态时,请确保对它的所有访问都是线程安全的并且记录得非常好。

答案 2 :(得分:0)

在您的服务中处于有状态的问题是需要考虑Service类实例的生命周期。

例如,如果您的服务是通过WCF公开的,那么“每次调用”的默认实例化模式将否定方法调用之间的任何状态,因为每次调用都意味着为来自客户端的每个调用创建一个新的服务实例。但是,您可以使用InstanceContextMode.PerSession在调用之间“保持”客户端和服务之间的连接,这样可以为您提供状态的一些好处,尽管它还有一个缺点,即可扩展性可能受限,因为您的客户端最终现在拥有控制您所在州使用的服务器资源。

@mellamokb的观点很棒,因为SOA中状态的一个常见用途是缓存。