Redis提供商配置ASP.NET会话状态

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Collections.Specialized;
using System.Web.SessionState;
using ServiceStack.Redis;
using System.Configuration;
using System.Configuration.Provider;
using System.Web.Configuration;
using System.IO;

namespace RedisProvider.SessionProvider
{

    #region Session Item Model
    public class SessionItem
    {

        #region Properties
        public DateTime CreatedAt { get; set; }
        public DateTime LockDate { get; set; }
        public int LockID { get; set; }
        public int Timeout { get; set; }
        public bool Locked { get; set; }
        public string SessionItems { get; set; }
        public int Flags { get; set; }
        #endregion Properties

    }
    #endregion Session Item Model

    public class CustomServiceProvider : System.Web.SessionState.SessionStateStoreProviderBase, IDisposable
    {

        #region Properties
        private string ApplicationName
        {
            get
            {
                if (ConfigurationManager.AppSettings.AllKeys.Contains("Application.Name"))
                {
                    return ConfigurationManager.AppSettings["Application.Name"];
                }

                return string.Empty;
            }
        }

        private RedisClient RedisSessionClient
        {
            get
            {

                if (!string.IsNullOrEmpty(this.redisPassword))
                {
                    return new RedisClient(this.redisServer, this.redisPort, this.redisPassword);
                }

                return new RedisClient(this.redisServer, this.redisPort);
            }
        }

        private string redisServer = "localhost";
        private int redisPort = 6379;
        private string redisPassword = string.Empty;
        private SessionStateSection sessionStateConfig = null;
        private bool writeExceptionsToLog = false;
        #endregion Properties

        #region Private Methods
        /// <summary>
        /// Prepends the application name to the redis key if one exists. Querying by application name is recommended for session
        /// </summary>
        /// <param name="id">The session id</param>
        /// <returns>Concatenated string applicationname:sessionkey</returns>
        private string RedisKey(string id)
        {
            return string.Format("{0}{1}", !string.IsNullOrEmpty(this.ApplicationName) ? this.ApplicationName + ":" : "", id);
        }
        #endregion Private Methods

        #region Constructor
        public CustomServiceProvider()
        {

        }
        #endregion Constructor

        #region Overrides
        public override void Dispose()
        {

        }

        public override void Initialize(string name, NameValueCollection config)
        {


            // Initialize values from web.config.
            if (config == null)
            {
                throw new ArgumentNullException("config");
            }

            if (name == null || name.Length == 0)
            {
                name = "RedisSessionStateStore";
            }

            if (String.IsNullOrEmpty(config["description"]))
            {
                config.Remove("description");
                config.Add("description", "Redis Session State Provider");
            }

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

            // Get <sessionState> configuration element.
            Configuration cfg = WebConfigurationManager.OpenWebConfiguration(ApplicationName);
            sessionStateConfig = (SessionStateSection)cfg.GetSection("system.web/sessionState");


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

            if (config["server"] != null)
            {
                this.redisServer = config["server"];
            }

            if (config["port"] != null)
            {
                int.TryParse(config["port"], out this.redisPort);
            }

            if (config["password"] != null)
            {
                this.redisPassword = config["password"];
            }
        }

        public override bool SetItemExpireCallback(SessionStateItemExpireCallback expireCallback)
        {
            return true;
        }

        public override void SetAndReleaseItemExclusive(HttpContext context, string id, SessionStateStoreData item, object lockId, bool newItem)
        {
            using (RedisClient client = this.RedisSessionClient)
            {
                // Serialize the SessionStateItemCollection as a string.
                string sessionItems = Serialize((SessionStateItemCollection)item.Items);

                try
                {
                    if (newItem)
                    {
                        SessionItem sessionItem = new SessionItem();
                        sessionItem.CreatedAt = DateTime.UtcNow;
                        sessionItem.LockDate = DateTime.UtcNow;
                        sessionItem.LockID = 0;
                        sessionItem.Timeout = item.Timeout;
                        sessionItem.Locked = false;
                        sessionItem.SessionItems = sessionItems;
                        sessionItem.Flags = 0;

                        client.Set<SessionItem>(this.RedisKey(id), sessionItem, DateTime.UtcNow.AddMinutes(item.Timeout));
                    }
                    else
                    {
                        SessionItem currentSessionItem = client.Get<SessionItem>(this.RedisKey(id));
                        if (currentSessionItem != null && currentSessionItem.LockID == (int?)lockId)
                        {
                            currentSessionItem.Locked = false;
                            currentSessionItem.SessionItems = sessionItems;
                            client.Set<SessionItem>(this.RedisKey(id), currentSessionItem, DateTime.UtcNow.AddMinutes(item.Timeout));
                        }
                    }
                }
                catch (Exception e)
                {
                    throw e;
                }
            }
        }

        public override SessionStateStoreData GetItemExclusive(HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actions)
        {
            return GetSessionStoreItem(true, context, id, out locked, out lockAge, out lockId, out actions);
        }

        public override SessionStateStoreData GetItem(HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actionFlags)
        {
            return GetSessionStoreItem(false, context, id, out locked, out lockAge, out lockId, out actionFlags);
        }

        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;

            // String to hold serialized SessionStateItemCollection.
            string serializedItems = "";

            // Timeout value from the data store.
            int timeout = 0;

            using (RedisClient client = this.RedisSessionClient)
            {
                try
                {
                    if (lockRecord)
                    {
                        locked = false;
                        SessionItem currentItem = client.Get<SessionItem>(this.RedisKey(id));

                        if (currentItem != null)
                        {
                            // If the item is locked then do not attempt to update it
                            if (!currentItem.Locked)
                            {
                                currentItem.Locked = true;
                                currentItem.LockDate = DateTime.UtcNow;
                                client.Set<SessionItem>(this.RedisKey(id), currentItem, DateTime.UtcNow.AddMinutes(sessionStateConfig.Timeout.TotalMinutes));
                            }
                            else
                            {
                                locked = true;
                            }
                        }
                    }

                    SessionItem currentSessionItem = client.Get<SessionItem>(this.RedisKey(id));

                    if (currentSessionItem != null)
                    {
                        serializedItems = currentSessionItem.SessionItems;
                        lockId = currentSessionItem.LockID;
                        lockAge = DateTime.UtcNow.Subtract(currentSessionItem.LockDate);
                        actionFlags = (SessionStateActions)currentSessionItem.Flags;
                        timeout = currentSessionItem.Timeout;
                    }
                    else
                    {
                        locked = false;
                    }

                    if (currentSessionItem != null && !locked)
                    {
                        // Delete the old item before inserting the new one
                        client.Remove(this.RedisKey(id));

                        lockId = (int?)lockId + 1;
                        currentSessionItem.LockID = lockId != null ? (int)lockId : 0;
                        currentSessionItem.Flags = 0;

                        client.Set<SessionItem>(this.RedisKey(id), currentSessionItem, DateTime.UtcNow.AddMinutes(sessionStateConfig.Timeout.TotalMinutes));

                        // If the actionFlags parameter is not InitializeItem,
                        // deserialize the stored SessionStateItemCollection.
                        if (actionFlags == SessionStateActions.InitializeItem)
                        {
                            item = CreateNewStoreData(context, 30);
                        }
                        else
                        {
                            item = Deserialize(context, serializedItems, timeout);
                        }
                    }
                }

                catch (Exception e)
                {
                    throw e;
                }
            }

            return item;
        }

        public override void ReleaseItemExclusive(HttpContext context, string id, object lockId)
        {

            using (RedisClient client = this.RedisSessionClient)
            {
                SessionItem currentSessionItem = client.Get<SessionItem>(this.RedisKey(id));

                if (currentSessionItem != null && (int?)lockId == currentSessionItem.LockID)
                {
                    currentSessionItem.Locked = false;
                    client.Set<SessionItem>(this.RedisKey(id), currentSessionItem, DateTime.UtcNow.AddMinutes(sessionStateConfig.Timeout.TotalMinutes));
                }
            }
        }

        public override void RemoveItem(HttpContext context, string id, object lockId, SessionStateStoreData item)
        {
            using (RedisClient client = this.RedisSessionClient)
            {
                // Delete the old item before inserting the new one
                client.Remove(this.RedisKey(id));
            }
        }

        public override void CreateUninitializedItem(HttpContext context, string id, int timeout)
        {
            using (RedisClient client = this.RedisSessionClient)
            {
                SessionItem sessionItem = new SessionItem();
                sessionItem.CreatedAt = DateTime.Now.ToUniversalTime();
                sessionItem.LockDate = DateTime.Now.ToUniversalTime();
                sessionItem.LockID = 0;
                sessionItem.Timeout = timeout;
                sessionItem.Locked = false;
                sessionItem.SessionItems = string.Empty;
                sessionItem.Flags = 0;

                client.Set<SessionItem>(this.RedisKey(id), sessionItem, DateTime.UtcNow.AddMinutes(timeout));
            }
        }

        public override SessionStateStoreData CreateNewStoreData(System.Web.HttpContext context, int timeout)
        {
            return new SessionStateStoreData(new SessionStateItemCollection(),
                SessionStateUtility.GetSessionStaticObjects(context),
                timeout);
        }

        public override void ResetItemTimeout(HttpContext context, string id)
        {

            using (RedisClient client = this.RedisSessionClient)
            {
                try
                {
                    // TODO :: GET THIS VALUE FROM THE CONFIG
                    client.ExpireEntryAt(id, DateTime.UtcNow.AddMinutes(sessionStateConfig.Timeout.TotalMinutes));
                }
                catch (Exception e)
                {
                    throw e;
                }
            }
        }

        public override void InitializeRequest(HttpContext context)
        {
            // Was going to open the redis connection here but sometimes I had 5 connections open at one time which was strange
        }

        public override void EndRequest(HttpContext context)
        {
            this.Dispose();
        }
        #endregion Overrides

        #region Serialization
        /// <summary>
        /// Serialize is called by the SetAndReleaseItemExclusive method to
        /// convert the SessionStateItemCollection into a Base64 string to
        /// be stored in MongoDB.
        /// </summary>
        private string Serialize(SessionStateItemCollection items)
        {
            using (MemoryStream ms = new MemoryStream())
            using (BinaryWriter writer = new BinaryWriter(ms))
            {
                if (items != null)
                    items.Serialize(writer);

                writer.Close();

                return Convert.ToBase64String(ms.ToArray());
            }
        }

        private SessionStateStoreData Deserialize(HttpContext context, string serializedItems, int timeout)
        {
            using (MemoryStream ms = new MemoryStream(Convert.FromBase64String(serializedItems)))
            {
                SessionStateItemCollection sessionItems = new SessionStateItemCollection();

                if (ms.Length > 0)
                {
                    using (BinaryReader reader = new BinaryReader(ms))
                    {
                        sessionItems = SessionStateItemCollection.Deserialize(reader);
                    }
                }

                return new SessionStateStoreData(sessionItems,
                  SessionStateUtility.GetSessionStaticObjects(context),
                  timeout);
            }
        }
        #endregion Serialization
    }
}
<system.web
    <sessionState timeout="1" mode="Custom" customProvider="RedisSessionStateProvider" cookieless="false">
      <providers>
        <add name="RedisSessionStateProvider" writeexceptionstoeventlog="false" type="RedisProvider.SessionProvider.CustomServiceProvider" server="192.168.1.118" port="6379" password="admin"></add>
      </providers>
    </sessionState>
  </system.web>