【问题标题】:using RavenDB with ServiceStack将 RavenDB 与 ServiceStack 一起使用
【发布时间】:2013-01-04 23:22:47
【问题描述】:

我阅读了 Phillip Haydon 的 this 帖子,了解如何将 NHibernate/RavenDB 与 ServiceStack 结合使用。
每次我需要像这样从数据库中获取一些东西时,我都看不到获取 IDocumentStore 并打开新会话的意义:

public class FooService : ServiceBase<Foo>
{
    public IDocumentStore RavenStore{ get; set; }

    protected override object Run(ProductFind request)
    {
        using (var session = RavenStore.OpenSession())
        {
            // Do Something...

            return new FooResponse{/*Object init*/};
        }
    }
}

为什么我不能每个请求只使用一个会话,当请求结束时,根据响应状态提交更改或回滚?

如果我的方法很好,那么我该如何实施呢? 这是我的尝试:

我创建了这个类:

    public class RavenSession : IRavenSession
    {
        #region Data Members

        private readonly IDocumentStore _store;
        private IDocumentSession _innerSession;

        #endregion

        #region Properties

        public IDocumentSession InnerSession
        {
            get { return _innerSession ?? (_innerSession = _store.OpenSession()); }
        }

        #endregion

        #region Ctor

        public RavenSession(IDocumentStore store)
        {
            _store = store;
        }

        #endregion

        #region Public Methods

        public void Commit()
        {
            if (_innerSession != null)
            {
                try
                {
                    InnerSession.SaveChanges();
                }
                finally
                {
                    InnerSession.Dispose();
                }
            }
        }

        public void Rollback()
        {
            if (_innerSession != null)
            {
                InnerSession.Dispose();
            }
        }

        #endregion

        #region IDocumentSession Delegation

        public ISyncAdvancedSessionOperation Advanced
        {
            get { return InnerSession.Advanced; }
        }

        public void Delete<T>(T entity)
        {
            InnerSession.Delete(entity);
        }

        public ILoaderWithInclude<object> Include(string path)
        {
            return InnerSession.Include(path);
        }

        public ILoaderWithInclude<T> Include<T, TInclude>(Expression<Func<T, object>> path)
        {
            return InnerSession.Include<T, TInclude>(path);
        }

        public ILoaderWithInclude<T> Include<T>(Expression<Func<T, object>> path)
        {
            return InnerSession.Include(path);
        }

        public T Load<T>(string id)
        {
            return InnerSession.Load<T>(id);
        }

        public T[] Load<T>(params string[] ids)
        {
            return InnerSession.Load<T>(ids);
        }

        public T Load<T>(ValueType id)
        {
            return InnerSession.Load<T>(id);
        }

        public T[] Load<T>(IEnumerable<string> ids)
        {
            return InnerSession.Load<T>(ids);
        }

        public IRavenQueryable<T> Query<T, TIndexCreator>() where TIndexCreator : AbstractIndexCreationTask, new()
        {
            return InnerSession.Query<T, TIndexCreator>();
        }

        public IRavenQueryable<T> Query<T>()
        {
            return InnerSession.Query<T>();
        }

        public IRavenQueryable<T> Query<T>(string indexName)
        {
            return InnerSession.Query<T>(indexName);
        }

        public void Store(dynamic entity, string id)
        {
            InnerSession.Store(entity, id);
        }

        public void Store(object entity, Guid etag, string id)
        {
            InnerSession.Store(entity, etag, id);
        }

        public void Store(object entity, Guid etag)
        {
            InnerSession.Store(entity, etag);
        }

        public void Store(dynamic entity)
        {
            InnerSession.Store(entity);
        }

        #endregion

    }

现在我的服务看起来像这样:

public class FooService : ServiceBase<Foo>
{
    public IRavenSession RavenSession { get; set; }

    protected override object Run(ProductFind request)
    {
        // Do Something with RavenSession...

        return new FooResponse {/*Object init*/};
    }
}

但我仍然需要找到一种方法来知道何时结束提交/回滚更改的请求。
我发现最好的方法是使用 ResponseFilters:

public class AppHost : AppHostBase
{
    public AppHost()
        : base("", typeof (Foo).Assembly, typeof (FooService).Assembly)
    {
    }

    public override void Configure(Container container)
    {
        // Some Configuration...

        this.ResponseFilters.Add((httpReq, httpResp, respnseDto) =>
                                     {
                                         var currentSession = (RavenSession) this.Container.Resolve<IRavenSession>();

                                         if (!httpResp.IsErrorResponse())
                                         {
                                             currentSession.Commit();
                                         }
                                         else
                                         {
                                             currentSession.Rollback();
                                         }
                                     });

        // Some Configuration...
    }
}

我确信有更好的方法可以做到这一点,但是如何?

【问题讨论】:

    标签: ravendb servicestack funq


    【解决方案1】:

    我刚刚将它包含在 AppHost 的 Configure 方法中

    var store = new DocumentStore()
    {
        Url = "http://127.0.0.1:8080",
        DefaultDatabase = "Test"
    }.Initialize();
    
    container.Register(store);
    
    container.Register(c => c.Resolve<IDocumentStore>().OpenSession()).ReusedWithin(ReuseScope.Request);
    

    你可以把它放在模块上并初始化它。

    然后在您的服务中添加一个接受 IDocumentSession 的构造函数

    public HelloService : Service {
        private readonly IDocumentSession session;
        public HelloService(IDocumentSession session) {
            this.session = session;
        }
    }
    

    你很高兴。

    【讨论】:

    • 似乎是个好主意。但这会在每次请求后关闭会话吗?我想这与使用模式相同吗?但我其实不明白怎么会一样
    • 是的,当我设置 ReusedWithin 时,它确保为每个请求处理会话。
    【解决方案2】:

    过滤ServiceStack中的响应

    自省ServiceStack 中的响应的方法是:

    其他一些可能有用的注释:

    ServiceStack 的内置 IOC (Funq) now supports RequestScope

    您可以将IDisposable 添加到在服务完成执行后立即调用的基类,例如如果您要使用 RDBMS:

    public class FooServiceBase : IService, IDisposable
    {
        public IDbConnectionFactory DbFactory { get; set; }
    
        private IDbConnection db;
        public IDbConnection Db
        {
            get { return db ?? (db = DbFactory.OpenDbConnection()); }
        }
    
        public object Any(ProductFind request)
        {
            return new FooResponse {
                Result = Db.Id<Product>(request.Id)
            };
        }
    
        public void Dispose()
        {
            if (db != null) db.Dispose();
        }
    }
    

    【讨论】:

      【解决方案3】:

      我尝试了 Felipe Leusin 提供的 answer,但它对我不起作用。我想要实现的主要目标是每个请求都有一个 DocumentSession.SaveChanges 调用。在查看了 RacoonBlog DocumentSession 生命周期管理和 ServiceStack 请求生命周期事件之后,我整理了一个适合我的配置:

          public override void Configure(Funq.Container container)
          {
              RequestFilters.Add((httpReq, httpRes, requestDto) =>
                  {
      
                      IDocumentSession documentSession = Container.Resolve<IDocumentStore>().OpenSession();
                      Container.Register<IDocumentSession>(documentSession);
                  });
      
              ResponseFilters.Add((httpReq, httpRes, requestDto) =>
                  {
                      using (var documentSession = Container.Resolve<IDocumentSession>())
                      {
                          if (documentSession == null)
                              return;
      
                          if (httpRes.StatusCode >= 400 && httpRes.StatusCode < 600)
                              return;
      
                          documentSession.SaveChanges();
                      }
                  });
              var documentStore = new DocumentStore
                  {
                      ConnectionStringName = "RavenDBServer",
                      DefaultDatabase = "MyDatabase",
                  }.Initialize();
      
              container.Register(documentStore);
      

      【讨论】:

      • 需要将IDocumentSession注册到Request范围的容器中
      • 我也尝试过这样做。在这一点上,我更喜欢让我的实现尽可能接近 RacoonBlog - 如果它适用于 Ayende,它也适用于我。
      • 与其手动注册文档会话,不如使用 Felipe Leusin 答案中的容器注册,然后包含此答案中的响应过滤器,以实现两全其美。
      【解决方案4】:

      我正在为我的 RavenSession 使用带有 RequestScope 的 funq,现在我将其更新为:

      public class RavenSession : IRavenSession, IDisposable
      {
          #region Data Members
      
          private readonly IDocumentStore _store;
          private readonly IRequestContext _context;
          private IDocumentSession _innerSession;
      
          #endregion
      
          #region Properties
      
          public IDocumentSession InnerSession
          {
              get { return _innerSession ?? (_innerSession = _store.OpenSession()); }
          }
      
          #endregion
      
          #region Ctor
      
          public RavenSession(IDocumentStore store, IRequestContext context)
          {
              _store = store;
              _context = context;
          }
      
          #endregion
      
          #region IDocumentSession Delegation
      
          public ISyncAdvancedSessionOperation Advanced
          {
              get { return InnerSession.Advanced; }
          }
      
          public void Delete<T>(T entity)
          {
              InnerSession.Delete(entity);
          }
      
          public ILoaderWithInclude<object> Include(string path)
          {
              return InnerSession.Include(path);
          }
      
          public ILoaderWithInclude<T> Include<T, TInclude>(Expression<Func<T, object>> path)
          {
              return InnerSession.Include<T, TInclude>(path);
          }
      
          public ILoaderWithInclude<T> Include<T>(Expression<Func<T, object>> path)
          {
              return InnerSession.Include(path);
          }
      
          public T Load<T>(string id)
          {
              return InnerSession.Load<T>(id);
          }
      
          public T[] Load<T>(params string[] ids)
          {
              return InnerSession.Load<T>(ids);
          }
      
          public T Load<T>(ValueType id)
          {
              return InnerSession.Load<T>(id);
          }
      
          public T[] Load<T>(IEnumerable<string> ids)
          {
              return InnerSession.Load<T>(ids);
          }
      
          public IRavenQueryable<T> Query<T, TIndexCreator>() where TIndexCreator : AbstractIndexCreationTask, new()
          {
              return InnerSession.Query<T, TIndexCreator>();
          }
      
          public IRavenQueryable<T> Query<T>()
          {
              return InnerSession.Query<T>();
          }
      
          public IRavenQueryable<T> Query<T>(string indexName)
          {
              return InnerSession.Query<T>(indexName);
          }
      
          public void Store(dynamic entity, string id)
          {
              InnerSession.Store(entity, id);
          }
      
          public void Store(object entity, Guid etag, string id)
          {
              InnerSession.Store(entity, etag, id);
          }
      
          public void Store(object entity, Guid etag)
          {
              InnerSession.Store(entity, etag);
          }
      
          public void Store(dynamic entity)
          {
              InnerSession.Store(entity);
          }
      
          #endregion
      
          #region Implementation of IDisposable
      
          public void Dispose()
          {
              if (_innerSession != null)
              {
                  var httpResponse = _context.Get<IHttpResponse>();
      
                  try
                  {
                      if (!httpResponse.IsErrorResponse())
                      {
                          _innerSession.SaveChanges();
                      }
                  }
                  finally
                  {
                      _innerSession.Dispose();
                  }
              }
          }
      
          #endregion
      }
      

      但这行不通,因为:
      1) 虽然我使用的是 RequestScope,但没有人注册请求的 IRequestContext,所以 funq 无法解析我的 RavenSession。
      2) funq 在请求完成后没有运行 Dispose 方法,这很奇怪。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-02-10
        相关资源
        最近更新 更多