在之前的版本中,Session存在于System.Web中,新版ASP.NET 5中由于不在依賴于System.Web.dll庫(kù)了,所以相應(yīng)的,Session也就成了ASP.NET 5中一個(gè)可配置的模塊(middleware)了。
ASP.NET 5中的Session模塊存在于Microsoft.AspNet.Session類庫(kù)中,要啟用Session,首先需要在PRoject.json中的dependencies節(jié)點(diǎn)中添加如下內(nèi)容:
"Microsoft.AspNet.Session": "1.0.0-beta3"然后在ConfigureServices中添加Session的引用(并進(jìn)行配置):
services.AddCaching(); // 這兩個(gè)必須同時(shí)添加,因?yàn)镾ession依賴于Cachingservices.AddSession();//services.ConfigureSession(null); 可以在這里配置,也可以再后面進(jìn)行配置最后在Configure方法中,開(kāi)啟使用Session的模式,如果在上面已經(jīng)配置過(guò)了,則可以不再傳入配置信息,否則還是要像上面的配置信息一樣,傳入Session的配置信息,代碼如下:
app.UseInMemorySession(configure:s => { s.IdleTimeout = TimeSpan.FromMinutes(30); });//app.UseSession(o => { o.IdleTimeout = TimeSpan.FromSeconds(30); });//app.UseInMemorySession(null, null); //開(kāi)啟內(nèi)存Session//app.UseDistributedSession(null, null);//開(kāi)啟分布式Session,也即持久化Session//app.UseDistributedSession(new RedisCache(new RedisCacheOptions() { Configuration = "localhost" }));對(duì)于UseInMemorySession方法,接收2個(gè)可選參數(shù),分別是:IMemoryCache可用于修改Session數(shù)據(jù)的默認(rèn)保存地址;Action<SessionOptions>委托則可以讓你修改默認(rèn)選項(xiàng),比如Session cookie的路徑、默認(rèn)的過(guò)期時(shí)間等。本例中,我們修改默認(rèn)過(guò)期時(shí)間為30分鐘。
注意:該方法必須在app.UseMvc之前調(diào)用,否則在Mvc里獲取不到Session,而且會(huì)出錯(cuò)。
獲取和設(shè)置Session對(duì)象,一般是在Controller的action里通過(guò)this.Context.Session來(lái)獲取的,其獲取的是一個(gè)基于接口ISessionCollection的實(shí)例。該接口可以通過(guò)索引、Set、TryGetValue等方法進(jìn)行Session值的獲取和設(shè)置,但我們發(fā)現(xiàn)在獲取和設(shè)置Session的時(shí)候,我們只能使用byte[]類型,而不能像之前版本的Session一樣可以設(shè)置任意類型的數(shù)據(jù)。原因是因?yàn)椋掳姹镜腟ession要支持在遠(yuǎn)程服務(wù)器上存儲(chǔ),就需要支持序列化,所以才強(qiáng)制要求保存為byte[]類型。所以我們?cè)诒4鍿ession的時(shí)候,需要將其轉(zhuǎn)換為byte[]才能進(jìn)行保存,并且獲取以后要再次將byte[]轉(zhuǎn)換為自己的原有的類型才行。這種形式太麻煩了,好在微軟在Microsoft.AspNet.Http命名空間(所屬Microsoft.AspNet.Http.Extensions.dll中)下,為我們添加了幾個(gè)擴(kuò)展方法,分別用于設(shè)置和保存byte[]類型、int類型、以及string類型,代碼如下:
public static byte[] Get(this ISessionCollection session, string key);public static int? GetInt(this ISessionCollection session, string key);public static string GetString(this ISessionCollection session, string key);public static void Set(this ISessionCollection session, string key, byte[] value);public static void SetInt(this ISessionCollection session, string key, int value);public static void SetString(this ISessionCollection session, string key, string value);所以,在Controller里引用Microsoft.AspNet.Http命名空間以后,我們就可以通過(guò)如下代碼進(jìn)行Session的設(shè)置和獲取了:
Context.Session.SetString("Name", "Mike");Context.Session.SetInt("Age", 21);ViewBag.Name = Context.Session.GetString("Name");ViewBag.Age = Context.Session.GetInt("Age");前面我們說(shuō)了,要保存自定義類型的Session,需要將其類型轉(zhuǎn)換成byte[]數(shù)組才行,在本例中,我們對(duì)bool類型的Session數(shù)據(jù)進(jìn)行設(shè)置和獲取的代碼,示例如下:
public static class SessionExtensions{ public static bool? GetBoolean(this ISessionCollection session, string key) { var data = session.Get(key); if (data == null) { return null; } return BitConverter.ToBoolean(data, 0); } public static void SetBoolean(this ISessionCollection session, string key, bool value) { session.Set(key, BitConverter.GetBytes(value)); }}定義bool類型的擴(kuò)展方法以后,我們就可以像SetInt/GetInt那樣進(jìn)行使用了,示例如下:
Context.Session.SetBoolean("Liar", true);ViewBag.Liar = Context.Session.GetBoolean("Liar");另外,ISessionCollection接口上還提供了Remove(string key)和Clear()兩個(gè)方法分別用于刪除某個(gè)Session值和清空所有的Session值的功能。但同時(shí)也需要注意,該接口并沒(méi)提供之前版本中的Abandon方法功能。
使用分布式Session,其主要工作就是將Session保存的地方從原來(lái)的內(nèi)存換到分布式存儲(chǔ)上,本節(jié),我們以Redis存儲(chǔ)為例來(lái)講解分布式Session的處理。
先查看使用分布式Session的擴(kuò)展方法,示例如下,我們可以看到,其Session容器需要是一個(gè)支持IDistributedCache的接口示例。
public static IapplicationBuilder UseDistributedSession([NotNullAttribute]this IApplicationBuilder app, IDistributedCache cache, Action<SessionOptions> configure = null);該接口是緩存Caching的通用接口,也就是說(shuō),只要我們實(shí)現(xiàn)了緩存接口,就可以將其用于Session的管理。進(jìn)一步查看該接口發(fā)現(xiàn),該接口中定義的Set方法還需要實(shí)現(xiàn)一個(gè)ICacheContext類型的緩存上下文(以便在調(diào)用的時(shí)候讓其它程序進(jìn)行委托調(diào)用),接口定義分別如下:
public interface IDistributedCache{ void Connect(); void Refresh(string key); void Remove(string key); Stream Set(string key, object state, Action<ICacheContext> create); bool TryGetValue(string key, out Stream value);}public interface ICacheContext{ Stream Data { get; } string Key { get; } object State { get; } void SetAbsoluteExpiration(TimeSpan relative); void SetAbsoluteExpiration(DateTimeOffset absolute); void SetSlidingExpiration(TimeSpan offset);}接下來(lái),我們基于Redis來(lái)實(shí)現(xiàn)上述功能,創(chuàng)建RedisCache類,并繼承IDistributedCache,引用StackExchange.Redis程序集,然后實(shí)現(xiàn)IDistributedCache接口的所有方法和屬性,代碼如下:
using Microsoft.Framework.Cache.Distributed;using Microsoft.Framework.OptionsModel;using StackExchange.Redis;using System;using System.IO;namespace Microsoft.Framework.Caching.Redis{ public class RedisCache : IDistributedCache { // KEYS[1] = = key // ARGV[1] = absolute-expiration - ticks as long (-1 for none) // ARGV[2] = sliding-expiration - ticks as long (-1 for none) // ARGV[3] = relative-expiration (long, in seconds, -1 for none) - Min(absolute-expiration - Now, sliding-expiration) // ARGV[4] = data - byte[] // this order should not change LUA script depends on it private const string SetScript = (@" redis.call('HMSET', KEYS[1], 'absexp', ARGV[1], 'sldexp', ARGV[2], 'data', ARGV[4]) if ARGV[3] ~= '-1' then redis.call('EXPIRE', KEYS[1], ARGV[3]) end return 1"); private const string AbsoluteExpirationKey = "absexp"; private const string SlidingExpirationKey = "sldexp"; private const string DataKey = "data"; private const long NotPresent = -1; private ConnectionMultiplexer _connection; private IDatabase _cache; private readonly RedisCacheOptions _options; private readonly string _instance; public RedisCache(IOptions<RedisCacheOptions> optionsaccessor) { _options = optionsAccessor.Options; // This allows partitioning a single backend cache for use with multiple apps/services. _instance = _options.InstanceName ?? string.Empty; } public void Connect() { if (_connection == null) { _connection = ConnectionMultiplexer.Connect(_options.Configuration); _cache = _connection.GetDatabase(); } } public Stream Set(string key, object state, Action<ICacheContext> create) { Connect(); var context = new CacheContext(key) { State = state }; create(context); var value = context.GetBytes(); var result = _cache.ScriptEvaluate(SetScript, new RedisKey[] { _instance + key }, new RedisValue[] { context.AbsoluteExpiration?.Ticks ?? NotPresent, context.SlidingExpiration?.Ticks ?? NotPresent, context.GetExpirationInSeconds() ?? NotPresent, value }); // TODO: Error handling return new MemoryStream(value, writable: false); } public bool TryGetValue(string key, out Stream value) { value = GetAndRefresh(key, getData: true); return value != null; } public void Refresh(string key) {
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注