/// <summary>
        /// deletes the session information from the data store where the data store item matches the supplied SessionID value,
        /// the current application, and the supplied lock identifier.
        /// </summary>
        /// <param name="context">The HttpContext instance for the current request</param>
        /// <param name="sessionId">The session identifier.</param>
        /// <param name="lockId">The exclusive-lock identifier.</param>
        /// <param name="item"></param>
        public override void RemoveItem(HttpContext context, string sessionId, object lockId, SessionStateStoreData item)
        {
            try
            {
                Logger.Debug("Beginning RemoveItem. SessionId={0}; Application={1}; lockId={2}.", sessionId,
                             ApplicationName, lockId);

                using (var documentSession = _documentStore.OpenSession())
                {
                    //don't tolerate stale data
                    documentSession.Advanced.AllowNonAuthoritativeInformation = false;

                    var sessionStateDocument = documentSession
                                               .Load <SessionStateDocument>(SessionStateDocument.GenerateDocumentId(sessionId, ApplicationName));


                    if (sessionStateDocument != null && sessionStateDocument.LockId == (int)lockId)
                    {
                        documentSession.Delete(sessionStateDocument);
                        documentSession.SaveChanges();
                    }
                }

                Logger.Debug("Completed RemoveItem. SessionId={0}; Application={1}; lockId={2}.", sessionId,
                             ApplicationName, lockId);
            }
            catch (Exception ex)
            {
                Logger.ErrorException(string.Format("Error during RemoveItem. SessionId={0}; Application={1}; lockId={2}", sessionId, ApplicationName, lockId), ex);
                throw;
            }
        }
        /// <summary>
        /// Adds an uninitialized item to the session data store.
        /// </summary>
        /// <param name="context">The HttpContext instance for the current request</param>
        /// <param name="sessionId">The session identifier.</param>
        /// <param name="timeout">The expiry timeout in minutes.</param>
        public override void CreateUninitializedItem(HttpContext context, string sessionId, int timeout)
        {
            try
            {
                Logger.Debug("Beginning CreateUninitializedItem. SessionId={0}; Application={1}; timeout={1}.", sessionId, ApplicationName, timeout);

                using (var documentSession = _documentStore.OpenSession())
                {
                    var expiry = DateTime.UtcNow.AddMinutes(timeout);

                    var sessionStateDocument = new SessionStateDocument(sessionId, ApplicationName)
                    {
                        Expiry = expiry
                    };

                    documentSession.Store(sessionStateDocument);
                    documentSession.Advanced.GetMetadataFor(sessionStateDocument)["Raven-Expiration-Date"] =
                        new RavenJValue(expiry);

                    documentSession.SaveChanges();
                }

                Logger.Debug("Completed CreateUninitializedItem. Sessionid={0}; Application={1}; timeout={1}.", sessionId, ApplicationName, timeout);
            }
            catch (Exception ex)
            {
                Logger.ErrorException("Error during CreateUninitializedItem.", ex);
                throw;
            }
        }
        /// <summary>
        /// Releases the lock on an item in the session data store.
        /// </summary>
        /// <param name="context">The HttpContext instance for the current request</param>
        /// <param name="sessionId">The session identifier.</param>
        /// <param name="lockId">The lock identifier for the current request.</param>
        public override void ReleaseItemExclusive(HttpContext context, string sessionId, object lockId)
        {
            try
            {
                Logger.Debug("Beginning ReleaseItemExclusive. SessionId={0}; Application={1}; LockId={2}.", sessionId, ApplicationName, lockId);

                using (var documentSession = _documentStore.OpenSession())
                {
                    //don't tolerate stale data
                    documentSession.Advanced.AllowNonAuthoritativeInformation = false;

                    var sessionState =
                        documentSession
                        .Load <SessionStateDocument>(SessionStateDocument.GenerateDocumentId(sessionId, ApplicationName));

                    //if the session-state is not present (it may have expired and been removed) or
                    //the locked id does not match, then we do nothing
                    if (sessionState == null || sessionState.LockId != (int)lockId)
                    {
                        Logger.Debug(
                            "Session state was not present or lock id did not match. Session id: {0}; Application: {1}; Lock id: {2}.",
                            sessionId, ApplicationName, lockId);
                        return;
                    }

                    sessionState.Locked = false;

                    //update the expiry
                    var expiry = DateTime.UtcNow.AddMinutes(SessionStateConfig.Timeout.TotalMinutes);
                    sessionState.Expiry = expiry;
                    documentSession.Advanced.GetMetadataFor(sessionState)["Raven-Expiration-Date"] = new RavenJValue(expiry);

                    documentSession.SaveChanges();
                }

                Logger.Debug("Completed ReleaseItemExclusive. SessionId={0}; Application={1}; LockId={2}.", sessionId, ApplicationName, lockId);
            }
            catch (Exception ex)
            {
                Logger.ErrorException(string.Format("Error during ReleaseItemExclusive. SessionId={0}; Application={1}; LockId={2}.", sessionId, ApplicationName, lockId), ex);
                throw;
            }
        }
        /// <summary>
        /// Resets the expiry timeout for a session item.
        /// </summary>
        /// <param name="context">The HttpContext instance for the current request</param>
        /// <param name="sessionId">The session identifier.</param>
        public override void ResetItemTimeout(HttpContext context, string sessionId)
        {
            try
            {
                Logger.Debug("Beginning ResetItemTimeout. SessionId={0}; Application={1}.", sessionId, ApplicationName);


                using (var documentSession = _documentStore.OpenSession())
                {
                    //we never want to over-write data with this method
                    documentSession.Advanced.UseOptimisticConcurrency = true;

                    var sessionStateDocument = documentSession
                                               .Load <SessionStateDocument>(SessionStateDocument.GenerateDocumentId(sessionId, ApplicationName));


                    if (sessionStateDocument != null)
                    {
                        var expiry = DateTime.UtcNow.AddMinutes(SessionStateConfig.Timeout.TotalMinutes);
                        sessionStateDocument.Expiry = expiry;
                        documentSession.Advanced.GetMetadataFor(sessionStateDocument)["Raven-Expiration-Date"] =
                            new RavenJValue(expiry);

                        documentSession.SaveChanges();
                    }
                }

                Logger.Debug("Completed ResetItemTimeout. SessionId={0}; Application={1}.", sessionId, ApplicationName);
            }
            catch (ConcurrencyException ex)
            {
                //swallow, we don't care
            }
            catch (Exception ex)
            {
                Logger.ErrorException("Error during ResetItemTimeout. SessionId=" + sessionId, ex);
                throw;
            }
        }
        //
        // GetSessionStoreItem is called by both the GetItem and
        // GetItemExclusive methods. GetSessionStoreItem retrieves the
        // session data from the data source. If the lockRecord parameter
        // is true (in the case of GetItemExclusive), then GetSessionStoreItem
        // locks the record and sets a new LockId and LockDate.
        //
        private SessionStateStoreData GetSessionStoreItem(bool lockRecord,
                                                          HttpContext context,
                                                          string sessionId,
                                                          out bool locked,
                                                          out TimeSpan lockAge,
                                                          out object lockId,
                                                          out SessionStateActions actionFlags)
        {
            // Initial values for return value and out parameters.
            lockAge     = TimeSpan.Zero;
            lockId      = null;
            locked      = false;
            actionFlags = 0;

            using (var documentSession = _documentStore.OpenSession())
            {
                //don't tolerate stale data
                documentSession.Advanced.AllowNonAuthoritativeInformation = false;

                Logger.Debug("Retrieving item from RavenDB. SessionId: {0}; Application: {1}.", sessionId, ApplicationName);

                var sessionState = documentSession.Load <SessionStateDocument>(SessionStateDocument.GenerateDocumentId(sessionId, ApplicationName));

                if (sessionState == null)
                {
                    Logger.Debug("Item not found in RavenDB with SessionId: {0}; Application: {1}.", sessionId, ApplicationName);
                    return(null);
                }

                //if the record is locked, we can't have it.
                if (sessionState.Locked)
                {
                    Logger.Debug("Item retrieved is locked. SessionId: {0}; Application: {1}.", sessionId, ApplicationName);

                    locked  = true;
                    lockAge = DateTime.UtcNow.Subtract((DateTime)sessionState.LockDate);
                    lockId  = sessionState.LockId;
                    return(null);
                }

                //generally we shouldn't get expired items, as the expiration bundle should clean them up,
                //but just in case the bundle isn't installed, or we made the window, we'll delete expired items here.
                if (DateTime.UtcNow > sessionState.Expiry)
                {
                    Logger.Debug("Item retrieved has expired. SessionId: {0}; Application: {1}; Expiry (UTC): {2}", sessionId, ApplicationName, sessionState.Expiry);

                    try
                    {
                        documentSession.Delete(sessionState);
                        documentSession.SaveChanges();
                    }
                    catch (Exception ex)
                    {
                        //we never want this clean-up op to throw
                        Logger.DebugException("Exception thrown while attempting to remove expired item.", ex);
                    }

                    return(null);
                }

                if (lockRecord)
                {
                    sessionState.Locked   = true;
                    sessionState.LockId  += 1;
                    sessionState.LockDate = DateTime.UtcNow;

                    documentSession.SaveChanges();
                }

                lockId = sessionState.LockId;
                return
                    (sessionState.Flags == SessionStateActions.InitializeItem
                        ? new SessionStateStoreData(new SessionStateItemCollection(),
                                                    GetSessionStaticObjects(context),
                                                    (int)SessionStateConfig.Timeout.TotalMinutes)
                        : Deserialize(context, sessionState.SessionItems, (int)SessionStateConfig.Timeout.TotalMinutes));
            }
        }
        /// <summary>
        /// If the newItem parameter is true, the SetAndReleaseItemExclusive method inserts a new item into the data store with the supplied values.
        /// Otherwise, the existing item in the data store is updated with the supplied values, and any lock on the data is released.
        /// </summary>
        /// <param name="context">The HttpContext instance for the current request</param>
        /// <param name="sessionId">The session identifier.</param>
        /// <param name="item">The current session values to be stored</param>
        /// <param name="lockId">The lock identifier for the current request.</param>
        /// <param name="newItem">If true, a new item is inserted into the store.  Otherwise, the existing item in
        /// the data store is updated with the supplied values, and any lock on the data is released. </param>
        public override void SetAndReleaseItemExclusive(HttpContext context, string sessionId, SessionStateStoreData item,
                                                        object lockId, bool newItem)
        {
            try
            {
                Logger.Debug(
                    " Beginning SetAndReleaseItemExclusive. SessionId={0}, Application: {1}, LockId={2}, newItem={3}.",
                    sessionId, ApplicationName, lockId, newItem);

                if (item == null)
                {
                    throw new ArgumentNullException("item");
                }

                var serializedItems = Serialize((SessionStateItemCollection)item.Items);

                using (var documentSession = _documentStore.OpenSession())
                {
                    //don't tolerate stale data
                    documentSession.Advanced.AllowNonAuthoritativeInformation = false;

                    SessionStateDocument sessionStateDocument;

                    if (newItem) //if we are creating a new document
                    {
                        sessionStateDocument = new SessionStateDocument(sessionId, ApplicationName);

                        documentSession.Store(sessionStateDocument);
                    }
                    else //we are not creating a new document, so load it
                    {
                        sessionStateDocument =
                            documentSession.Load <SessionStateDocument>(SessionStateDocument.GenerateDocumentId(sessionId, ApplicationName));

                        //if the lock identifier does not match, then we don't modifiy the data
                        if (sessionStateDocument.LockId != (int)lockId)
                        {
                            Logger.Debug(
                                "Lock Id does not match, so data will not be modified. Session Id: {0}; Application: {1}; Lock Id {2}.",
                                sessionId, ApplicationName, lockId);
                            return;
                        }
                    }

                    sessionStateDocument.SessionItems = serializedItems;
                    sessionStateDocument.Locked       = false;

                    //set the expiry
                    var expiry = DateTime.UtcNow.AddMinutes(SessionStateConfig.Timeout.TotalMinutes);
                    sessionStateDocument.Expiry = expiry;
                    documentSession.Advanced.GetMetadataFor(sessionStateDocument)["Raven-Expiration-Date"] = new RavenJValue(expiry);

                    documentSession.SaveChanges();
                }

                Logger.Debug("Completed SetAndReleaseItemExclusive. SessionId={0}; Application:{1}; LockId={2}; newItem={3}.", sessionId, ApplicationName, lockId, newItem);
            }
            catch (Exception ex)
            {
                Logger.ErrorException(string.Format("Error during SetAndReleaseItemExclusive. SessionId={0}; Application={1}; LockId={2}, newItem={3}.", sessionId, ApplicationName, lockId, newItem), ex);
                throw;
            }
        }
        /// <summary>
        /// Adds an uninitialized item to the session data store.
        /// </summary>
        /// <param name="context">The HttpContext instance for the current request</param>
        /// <param name="sessionId">The session identifier.</param>
        /// <param name="timeout">The expiry timeout in minutes.</param>
        public override void CreateUninitializedItem(HttpContext context, string sessionId, int timeout)
        {
            try
            {
                Logger.Debug("Beginning CreateUninitializedItem. SessionId={0}; Application={1}; timeout={1}.", sessionId, ApplicationName, timeout);

                using (var documentSession = _documentStore.OpenSession())
                {
                    var expiry = DateTime.UtcNow.AddMinutes(timeout);

                    var sessionStateDocument = new SessionStateDocument(sessionId, ApplicationName)
                        {
                            Expiry = expiry
                        };

                    documentSession.Store(sessionStateDocument);
                    documentSession.Advanced.GetMetadataFor(sessionStateDocument)["Raven-Expiration-Date"] =
                        new RavenJValue(expiry);

                    documentSession.SaveChanges();
                }

                Logger.Debug("Completed CreateUninitializedItem. Sessionid={0}; Application={1}; timeout={1}.", sessionId, ApplicationName, timeout);

            }
            catch (Exception ex)
            {
                Logger.ErrorException("Error during CreateUninitializedItem.", ex);
                throw;
            }
        }
        /// <summary>
        /// If the newItem parameter is true, the SetAndReleaseItemExclusive method inserts a new item into the data store with the supplied values. 
        /// Otherwise, the existing item in the data store is updated with the supplied values, and any lock on the data is released. 
        /// </summary>
        /// <param name="context">The HttpContext instance for the current request</param>
        /// <param name="sessionId">The session identifier.</param>
        /// <param name="item">The current session values to be stored</param>
        /// <param name="lockId">The lock identifier for the current request.</param>
        /// <param name="newItem">If true, a new item is inserted into the store.  Otherwise, the existing item in 
        /// the data store is updated with the supplied values, and any lock on the data is released. </param>
        public override void SetAndReleaseItemExclusive(HttpContext context, string sessionId, SessionStateStoreData item,
                                                        object lockId, bool newItem)
        {
            try
            {
                Logger.Debug(
                    " Beginning SetAndReleaseItemExclusive. SessionId={0}, Application: {1}, LockId={2}, newItem={3}.",
                    sessionId, ApplicationName, lockId, newItem);

                if ( item == null)
                    throw new ArgumentNullException("item");

                var serializedItems = Serialize((SessionStateItemCollection) item.Items);

                using (var documentSession = _documentStore.OpenSession())
                {
                    //don't tolerate stale data
                    documentSession.Advanced.AllowNonAuthoritativeInformation = false;

                    SessionStateDocument sessionStateDocument;

                    if (newItem) //if we are creating a new document
                    {
                        sessionStateDocument = new SessionStateDocument(sessionId, ApplicationName);

                        documentSession.Store(sessionStateDocument);

                    }
                    else //we are not creating a new document, so load it
                    {
                        sessionStateDocument =
                            documentSession .Load<SessionStateDocument>(SessionStateDocument.GenerateDocumentId( sessionId, ApplicationName));

                        //if the lock identifier does not match, then we don't modifiy the data
                        if (sessionStateDocument.LockId != (int) lockId)
                        {
                            Logger.Debug(
                                "Lock Id does not match, so data will not be modified. Session Id: {0}; Application: {1}; Lock Id {2}.",
                                sessionId, ApplicationName, lockId);
                            return;
                        }
                    }

                    sessionStateDocument.SessionItems = serializedItems;
                    sessionStateDocument.Locked = false;

                    //set the expiry
                    var expiry = DateTime.UtcNow.AddMinutes(SessionStateConfig.Timeout.TotalMinutes);
                    sessionStateDocument.Expiry = expiry;
                    documentSession.Advanced.GetMetadataFor(sessionStateDocument)["Raven-Expiration-Date"] = new RavenJValue(expiry);

                    documentSession.SaveChanges();
                }

                Logger.Debug("Completed SetAndReleaseItemExclusive. SessionId={0}; Application:{1}; LockId={2}; newItem={3}.", sessionId, ApplicationName, lockId, newItem);

            }
            catch(Exception ex)
            {
                Logger.ErrorException(string.Format("Error during SetAndReleaseItemExclusive. SessionId={0}; Application={1}; LockId={2}, newItem={3}.", sessionId, ApplicationName, lockId, newItem), ex);
                throw;
            }
        }