Using RavenDB with ServiceStack

I read this post from Phillip Haydon on how to use NHibernate / RavenDB with ServiceStack.
I don't see the point of getting an IDocumentStore and opening a new session every time I need something from db, like this:

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*/};
        }
    }
}

Why can't I use one session for each request and when the request ends, commit the changes or roll them back according to the status of the response?

If my approach is fine, how can I implement it? here is my attempt:

I created this class:

    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

    }

Now my service looks like this:

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*/};
    }
}

but I still need to find a way to find out when the request is completed to commit / rollback changes.
best way to find this with 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...
    }
}

I am sure there is a better way to do this, but how?

+5
4

Configure AppHost

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;
    }
}

.

+10

ServiceStack

ServiceStack :

, :

ServiceStack IOC (Funq) RequestScope

IDisposable , , . :

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();
    }
}
+2

, , . , , - 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);
+1

funq RequestScope RavenSession, :

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
}

but this will not work because:
1) although I use RequestScope, no one registers the IRequestContext of the request, so the funq canant resolves my RavenSession.
2) funq does not run the Dispose method after executing the request, which is odd.

0
source