public override void RemoveItem(HttpContext context, string id, object lockId, SessionStateStoreData item)
        {
            MongoClient conn = GetConnection();
            IMongoCollection <BsonDocument> sessionCollection = GetSessionCollection(conn);

            var filter = Builders <BsonDocument> .Filter.And(
                Builders <BsonDocument> .Filter.Eq("_id", MongoSessionStateStoreHelpers.GetDocumentSessionId(id, ApplicationName)),
                Builders <BsonDocument> .Filter.Eq("LockId", (Int32)lockId));

            this.DeleteSessionDocument(sessionCollection, filter);
        }
        public override void ResetItemTimeout(HttpContext context, string id)
        {
            MongoClient conn = GetConnection();
            IMongoCollection <BsonDocument> sessionCollection = GetSessionCollection(conn);

            var filter = Builders <BsonDocument> .Filter.Eq("_id", MongoSessionStateStoreHelpers.GetDocumentSessionId(id, ApplicationName));

            var update = Builders <BsonDocument> .Update.Set("Expires", DateTime.Now.AddMinutes(_config.Timeout.TotalMinutes).ToUniversalTime());

            this.UpdateSessionCollection(sessionCollection, filter, update);
        }
        public override void ReleaseItemExclusive(HttpContext context, string id, object lockId)
        {
            MongoClient conn = GetConnection();
            IMongoCollection <BsonDocument> sessionCollection = GetSessionCollection(conn);

            var filter = Builders <BsonDocument> .Filter.And(
                Builders <BsonDocument> .Filter.Eq("_id", MongoSessionStateStoreHelpers.GetDocumentSessionId(id, ApplicationName)),
                Builders <BsonDocument> .Filter.Eq("LockId", (Int32)lockId));

            var update = Builders <BsonDocument> .Update.Set("Locked", false)
                         .Set("Expires", DateTime.Now.AddMinutes(_config.Timeout.TotalMinutes).ToUniversalTime());

            this.UpdateSessionCollection(sessionCollection, filter, update);
        }
        public override void CreateUninitializedItem(HttpContext context, string id, int timeout)
        {
            MongoClient conn = GetConnection();
            IMongoCollection <BsonDocument> sessionCollection = GetSessionCollection(conn);
            var doc = MongoSessionStateStoreHelpers.GetNewBsonSessionDocument(
                id: id,
                applicationName: ApplicationName,
                created: DateTime.Now.ToUniversalTime(),
                lockDate: DateTime.Now.ToUniversalTime(),
                lockId: 0,
                timeout: timeout,
                locked: false,
                jsonSessionItemsArray: new BsonArray(),
                flags: 1);

            this.UpsertEntireSessionDocument(sessionCollection, doc);
        }
        /// <summary>
        /// SessionStateProviderBase.SetAndReleaseItemExclusive
        /// </summary>
        public override void SetAndReleaseItemExclusive(
            HttpContext context,
            string id,
            SessionStateStoreData item,
            object lockId,
            bool newItem)
        {
            BsonArray arraySession = MongoSessionStateStoreHelpers.Serialize(item);

            MongoClient conn = GetConnection();
            var         sessionCollection = GetSessionCollection(conn);

            if (newItem)
            {
                var insertDoc = MongoSessionStateStoreHelpers.GetNewBsonSessionDocument(
                    id: id,
                    applicationName: ApplicationName,
                    created: DateTime.Now.ToUniversalTime(),
                    lockDate: DateTime.Now.ToUniversalTime(),
                    lockId: 0,
                    timeout: item.Timeout,
                    locked: false,
                    jsonSessionItemsArray: arraySession,
                    flags: 0);

                this.UpsertEntireSessionDocument(sessionCollection, insertDoc);
            }
            else
            {
                var filter = Builders <BsonDocument> .Filter.And(
                    Builders <BsonDocument> .Filter.Eq("_id", MongoSessionStateStoreHelpers.GetDocumentSessionId(id, ApplicationName)),
                    Builders <BsonDocument> .Filter.Eq("LockId", (Int32)lockId)
                    );

                var update = Builders <BsonDocument> .Update
                             .Set("Expires", DateTime.Now.AddMinutes(item.Timeout).ToUniversalTime())
                             .Set("SessionItemJSON", arraySession)
                             .Set("Locked", false);

                this.UpdateSessionCollection(sessionCollection, filter, update);
            }
        }
        /// <summary>
        /// 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.
        /// </summary>
        private SessionStateStoreData GetSessionStoreItem(
            bool lockRecord,
            HttpContext context,
            string id,
            out bool locked,
            out TimeSpan lockAge,
            out object lockId,
            out SessionStateActions actionFlags)
        {
            // Initial values for return value and out parameters.
            SessionStateStoreData item = null;

            lockAge     = TimeSpan.Zero;
            lockId      = null;
            locked      = false;
            actionFlags = 0;

            MongoClient conn = GetConnection();
            var         sessionCollection = GetSessionCollection(conn);

            // DateTime to check if current session item is expired.
            // String to hold serialized SessionStateItemCollection.
            BsonArray serializedItems = new BsonArray();
            // True if a record is found in the database.
            bool foundRecord = false;
            // True if the returned session item is expired and needs to be deleted.
            bool deleteData = false;
            // Timeout value from the data store.
            int timeout = 0;


            // lockRecord is true when called from GetItemExclusive and
            // false when called from GetItem.
            // Obtain a lock if possible. Ignore the record if it is expired.
            FilterDefinition <BsonDocument> query;

            if (lockRecord)
            {
                query = Builders <BsonDocument> .Filter.And(
                    Builders <BsonDocument> .Filter.Eq("_id", MongoSessionStateStoreHelpers.GetDocumentSessionId(id, ApplicationName)),
                    Builders <BsonDocument> .Filter.Eq("Locked", false),
                    Builders <BsonDocument> .Filter.Gt("Expires", DateTime.Now.ToUniversalTime()));

                var update = Builders <BsonDocument> .Update.Set("Locked", true)
                             .Set("LockDate", DateTime.Now.ToUniversalTime());

                var result = this.UpdateSessionCollection(sessionCollection, query, update);

                if (result.IsAcknowledged)
                {
                    locked = result.ModifiedCount == 0; // DocumentsAffected == 0 == No record was updated because the record was locked or not found.
                }
            }

            // Retrieve the current session item information.
            query = Builders <BsonDocument> .Filter.Eq("_id", MongoSessionStateStoreHelpers.GetDocumentSessionId(id, ApplicationName));

            var results = this.FindOneSessionItem(sessionCollection, query);

            if (results != null)
            {
                DateTime expires = results["Expires"].ToUniversalTime();

                if (expires < DateTime.Now.ToUniversalTime())
                {
                    // The record was expired. Mark it as not locked.
                    locked = false;
                    // The session was expired. Mark the data for deletion.
                    deleteData = true;
                }
                else
                {
                    foundRecord = true;
                }

                serializedItems = results["SessionItemJSON"].AsBsonArray;
                lockId          = results["LockId"].AsInt32;
                lockAge         = DateTime.Now.ToUniversalTime().Subtract(results["LockDate"].ToUniversalTime());
                actionFlags     = (SessionStateActions)results["Flags"].AsInt32;
                timeout         = results["Timeout"].AsInt32;
            }

            // If the returned session item is expired,
            // delete the record from the data source.
            if (deleteData)
            {
                query = Builders <BsonDocument> .Filter.Eq("_id", MongoSessionStateStoreHelpers.GetDocumentSessionId(id, ApplicationName));

                this.DeleteSessionDocument(sessionCollection, query);
            }

            // The record was not found. Ensure that locked is false.
            if (!foundRecord)
            {
                locked = false;
            }

            // If the record was found and you obtained a lock, then set
            // the lockId, clear the actionFlags,
            // and create the SessionStateStoreItem to return.
            if (foundRecord && !locked)
            {
                lockId = (int)lockId + 1;

                query = Builders <BsonDocument> .Filter.Eq("_id", MongoSessionStateStoreHelpers.GetDocumentSessionId(id, ApplicationName));

                var update = Builders <BsonDocument> .Update.Set("LockId", (int)lockId).Set("Flags", 0);

                this.UpdateSessionCollection(sessionCollection, query, update);

                // If the actionFlags parameter is not InitializeItem,
                // deserialize the stored SessionStateItemCollection.
                item = actionFlags == SessionStateActions.InitializeItem
                    ? CreateNewStoreData(context, (int)_config.Timeout.TotalMinutes)
                    : MongoSessionStateStoreHelpers.Deserialize(context, serializedItems, timeout);
            }

            return(item);
        }
        /// <summary>
        /// Initialise the session state store.
        /// </summary>
        /// <param name="name">session state store name. Defaults to "MongoSessionStateStore" if not supplied</param>
        /// <param name="config">configuration settings</param>
        public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
        {
            // Initialize values from web.config.
            if (config == null)
            {
                throw new ArgumentNullException("config");
            }

            if (name.Length == 0)
            {
                name = "MongoSessionStateStore";
            }

            if (String.IsNullOrEmpty(config["description"]))
            {
                config.Remove("description");
                config.Add("description", "MongoDB Session State Store provider");
            }

            // Initialize the abstract base class.
            base.Initialize(name, config);

            // Initialize the ApplicationName property.
            _applicationName = System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath;

            // Get <sessionState> configuration element.
            Configuration cfg = WebConfigurationManager.OpenWebConfiguration(ApplicationName);

            _config = (SessionStateSection)cfg.GetSection("system.web/sessionState");

            // Initialize connection string.
            _connectionStringSettings = ConfigurationManager.ConnectionStrings[config["connectionStringName"]];

            if (_connectionStringSettings == null || _connectionStringSettings.ConnectionString.Trim() == "")
            {
                throw new ProviderException("Connection string cannot be blank.");
            }

            _connectionString = _connectionStringSettings.ConnectionString;

            // Initialize WriteExceptionsToEventLog
            _writeExceptionsToEventLog = false;

            if (config["writeExceptionsToEventLog"] != null)
            {
                if (config["writeExceptionsToEventLog"].ToUpper() == "TRUE")
                {
                    _writeExceptionsToEventLog = true;
                }
            }


            // Write concern options j (journal) and w (write ack #)

            bool journal = false;

            //if (config["Journal"] != null)
            //{
            //    if (!bool.TryParse(config["Journal"], out journal))
            //        throw new Exception("Journal must be a valid value (true or false)");

            //    if (journal)
            //        _writeConcern = WriteConcern.WMajority;
            //}

            // If journal (j) is true, write ack # param (w) not applies.
            // Only the primary node will confirm the journal writing

            if (!journal)
            {
                _writeConcern = WriteConcern.W1;
                if (config["WriteConcern"] != null)
                {
                    string WCStr = config["WriteConcern"];
                    WCStr = WCStr.ToUpper();
                    switch (WCStr)
                    {
                    case "W1":
                        _writeConcern = WriteConcern.W1;
                        break;

                    case "W2":
                        _writeConcern = WriteConcern.W2;
                        break;

                    case "W3":
                        _writeConcern = WriteConcern.W3;
                        break;

                    //case "W4":
                    //    _writeConcern = WriteConcern.W4;
                    //    break;
                    case "WMAJORITY":
                        _writeConcern = WriteConcern.WMajority;
                        break;

                    default:
                        throw new Exception("WriteConcern must be a valid value W1, W2, W3 or WMAJORITY");
                    }
                }
            }

            // Initialize maxUpsertAttempts
            _maxUpsertAttempts = 220;
            if (config["maxUpsertAttempts"] != null)
            {
                if (!int.TryParse(config["maxUpsertAttempts"], out _maxUpsertAttempts))
                {
                    throw new Exception("maxUpsertAttempts must be a valid integer");
                }
            }

            //initialize msWaitingForAttempt
            _msWaitingForAttempt = 500;
            if (config["msWaitingForAttempt"] != null)
            {
                if (!int.TryParse(config["msWaitingForAttempt"], out _msWaitingForAttempt))
                {
                    throw new Exception("msWaitingForAttempt must be a valid integer");
                }
            }

            //Initialize AutoCreateTTLIndex
            _autoCreateTTLIndex = true;
            if (config["AutoCreateTTLIndex"] != null)
            {
                if (!bool.TryParse(config["AutoCreateTTLIndex"], out _autoCreateTTLIndex))
                {
                    throw new Exception("AutoCreateTTLIndex must be true or false");
                }
            }

            //Create TTL index if AutoCreateTTLIndex config parameter is true.
            if (_autoCreateTTLIndex)
            {
                var conn = GetConnection();
                var sessionCollection = GetSessionCollection(conn);
                MongoSessionStateStoreHelpers.CreateTTLIndex(sessionCollection);
            }
        }