亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > .NET > 正文

詳解Asp.net Core 使用Redis存儲Session

2024-07-10 13:31:40
字體:
來源:轉載
供稿:網友

前言

Asp.net Core 改變了之前的封閉,現在開源且開放,下面我們來用Redis存儲Session來做一個簡單的測試,或者叫做中間件(middleware)。

對于Session來說褒貶不一,很多人直接說不要用,也有很多人在用,這個也沒有絕對的這義,個人認為只要不影什么且又可以方便實現的東西是可以用的,現在不對可不可用做表態,我們只關心實現。

類庫引用

這個相對于之前的.net是方便了不少,需要在project.json中的dependencies節點中添加如下內容:

  "StackExchange.Redis": "1.1.604-alpha",  "Microsoft.AspNetCore.Session": "1.1.0-alpha1-21694"

Redis實現

這里并非我實現,而是借用不知道為什么之前還有這個類庫,而現在NUGET止沒有了,為了不影響日后升級我的命名空間也用 Microsoft.Extensions.Caching.Redis

可以看到微軟這里有四個類,其實我們只需要三個,第四個拿過來反而會出錯:

using System;using System.Threading.Tasks;using Microsoft.Extensions.Caching.Distributed;using Microsoft.Extensions.Options;using StackExchange.Redis;namespace Microsoft.Extensions.Caching.Redis{  public class RedisCache : IDistributedCache, IDisposable  {    // 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)    {      if (optionsAccessor == null)      {        throw new ArgumentNullException(nameof(optionsAccessor));      }      _options = optionsAccessor.Value;      // This allows partitioning a single backend cache for use with multiple apps/services.      _instance = _options.InstanceName ?? string.Empty;    }    public byte[] Get(string key)    {      if (key == null)      {        throw new ArgumentNullException(nameof(key));      }      return GetAndRefresh(key, getData: true);    }    public async Task<byte[]> GetAsync(string key)    {      if (key == null)      {        throw new ArgumentNullException(nameof(key));      }      return await GetAndRefreshAsync(key, getData: true);    }    public void Set(string key, byte[] value, DistributedCacheEntryOptions options)    {      if (key == null)      {        throw new ArgumentNullException(nameof(key));      }      if (value == null)      {        throw new ArgumentNullException(nameof(value));      }      if (options == null)      {        throw new ArgumentNullException(nameof(options));      }      Connect();      var creationTime = DateTimeOffset.UtcNow;      var absoluteExpiration = GetAbsoluteExpiration(creationTime, options);      var result = _cache.ScriptEvaluate(SetScript, new RedisKey[] { _instance + key },        new RedisValue[]        {            absoluteExpiration?.Ticks ?? NotPresent,            options.SlidingExpiration?.Ticks ?? NotPresent,            GetExpirationInSeconds(creationTime, absoluteExpiration, options) ?? NotPresent,            value        });    }    public async Task SetAsync(string key, byte[] value, DistributedCacheEntryOptions options)    {      if (key == null)      {        throw new ArgumentNullException(nameof(key));      }      if (value == null)      {        throw new ArgumentNullException(nameof(value));      }      if (options == null)      {        throw new ArgumentNullException(nameof(options));      }      await ConnectAsync();      var creationTime = DateTimeOffset.UtcNow;      var absoluteExpiration = GetAbsoluteExpiration(creationTime, options);      await _cache.ScriptEvaluateAsync(SetScript, new RedisKey[] { _instance + key },        new RedisValue[]        {            absoluteExpiration?.Ticks ?? NotPresent,            options.SlidingExpiration?.Ticks ?? NotPresent,            GetExpirationInSeconds(creationTime, absoluteExpiration, options) ?? NotPresent,            value        });    }    public void Refresh(string key)    {      if (key == null)      {        throw new ArgumentNullException(nameof(key));      }      GetAndRefresh(key, getData: false);    }    public async Task RefreshAsync(string key)    {      if (key == null)      {        throw new ArgumentNullException(nameof(key));      }      await GetAndRefreshAsync(key, getData: false);    }    private void Connect()    {      if (_connection == null)      {        _connection = ConnectionMultiplexer.Connect(_options.Configuration);        _cache = _connection.GetDatabase();      }    }    private async Task ConnectAsync()    {      if (_connection == null)      {        _connection = await ConnectionMultiplexer.ConnectAsync(_options.Configuration);        _cache = _connection.GetDatabase();      }    }    private byte[] GetAndRefresh(string key, bool getData)    {      if (key == null)      {        throw new ArgumentNullException(nameof(key));      }      Connect();      // This also resets the LRU status as desired.      // TODO: Can this be done in one operation on the server side? Probably, the trick would just be the DateTimeOffset math.      RedisValue[] results;      if (getData)      {        results = _cache.HashMemberGet(_instance + key, AbsoluteExpirationKey, SlidingExpirationKey, DataKey);      }      else      {        results = _cache.HashMemberGet(_instance + key, AbsoluteExpirationKey, SlidingExpirationKey);      }      // TODO: Error handling      if (results.Length >= 2)      {        // Note we always get back two results, even if they are all null.        // These operations will no-op in the null scenario.        DateTimeOffset? absExpr;        TimeSpan? sldExpr;        MapMetadata(results, out absExpr, out sldExpr);        Refresh(key, absExpr, sldExpr);      }      if (results.Length >= 3 && results[2].HasValue)      {        return results[2];      }      return null;    }    private async Task<byte[]> GetAndRefreshAsync(string key, bool getData)    {      if (key == null)      {        throw new ArgumentNullException(nameof(key));      }      await ConnectAsync();      // This also resets the LRU status as desired.      // TODO: Can this be done in one operation on the server side? Probably, the trick would just be the DateTimeOffset math.      RedisValue[] results;      if (getData)      {        results = await _cache.HashMemberGetAsync(_instance + key, AbsoluteExpirationKey, SlidingExpirationKey, DataKey);      }      else      {        results = await _cache.HashMemberGetAsync(_instance + key, AbsoluteExpirationKey, SlidingExpirationKey);      }      // TODO: Error handling      if (results.Length >= 2)      {        // Note we always get back two results, even if they are all null.        // These operations will no-op in the null scenario.        DateTimeOffset? absExpr;        TimeSpan? sldExpr;        MapMetadata(results, out absExpr, out sldExpr);        await RefreshAsync(key, absExpr, sldExpr);      }      if (results.Length >= 3 && results[2].HasValue)      {        return results[2];      }      return null;    }    public void Remove(string key)    {      if (key == null)      {        throw new ArgumentNullException(nameof(key));      }      Connect();      _cache.KeyDelete(_instance + key);      // TODO: Error handling    }    public async Task RemoveAsync(string key)    {      if (key == null)      {        throw new ArgumentNullException(nameof(key));      }      await ConnectAsync();      await _cache.KeyDeleteAsync(_instance + key);      // TODO: Error handling    }    private void MapMetadata(RedisValue[] results, out DateTimeOffset? absoluteExpiration, out TimeSpan? slidingExpiration)    {      absoluteExpiration = null;      slidingExpiration = null;      var absoluteExpirationTicks = (long?)results[0];      if (absoluteExpirationTicks.HasValue && absoluteExpirationTicks.Value != NotPresent)      {        absoluteExpiration = new DateTimeOffset(absoluteExpirationTicks.Value, TimeSpan.Zero);      }      var slidingExpirationTicks = (long?)results[1];      if (slidingExpirationTicks.HasValue && slidingExpirationTicks.Value != NotPresent)      {        slidingExpiration = new TimeSpan(slidingExpirationTicks.Value);      }    }    private void Refresh(string key, DateTimeOffset? absExpr, TimeSpan? sldExpr)    {      if (key == null)      {        throw new ArgumentNullException(nameof(key));      }      // Note Refresh has no effect if there is just an absolute expiration (or neither).      TimeSpan? expr = null;      if (sldExpr.HasValue)      {        if (absExpr.HasValue)        {          var relExpr = absExpr.Value - DateTimeOffset.Now;          expr = relExpr <= sldExpr.Value ? relExpr : sldExpr;        }        else        {          expr = sldExpr;        }        _cache.KeyExpire(_instance + key, expr);        // TODO: Error handling      }    }    private async Task RefreshAsync(string key, DateTimeOffset? absExpr, TimeSpan? sldExpr)    {      if (key == null)      {        throw new ArgumentNullException(nameof(key));      }      // Note Refresh has no effect if there is just an absolute expiration (or neither).      TimeSpan? expr = null;      if (sldExpr.HasValue)      {        if (absExpr.HasValue)        {          var relExpr = absExpr.Value - DateTimeOffset.Now;          expr = relExpr <= sldExpr.Value ? relExpr : sldExpr;        }        else        {          expr = sldExpr;        }        await _cache.KeyExpireAsync(_instance + key, expr);        // TODO: Error handling      }    }    private static long? GetExpirationInSeconds(DateTimeOffset creationTime, DateTimeOffset? absoluteExpiration, DistributedCacheEntryOptions options)    {      if (absoluteExpiration.HasValue && options.SlidingExpiration.HasValue)      {        return (long)Math.Min(          (absoluteExpiration.Value - creationTime).TotalSeconds,          options.SlidingExpiration.Value.TotalSeconds);      }      else if (absoluteExpiration.HasValue)      {        return (long)(absoluteExpiration.Value - creationTime).TotalSeconds;      }      else if (options.SlidingExpiration.HasValue)      {        return (long)options.SlidingExpiration.Value.TotalSeconds;      }      return null;    }    private static DateTimeOffset? GetAbsoluteExpiration(DateTimeOffset creationTime, DistributedCacheEntryOptions options)    {      if (options.AbsoluteExpiration.HasValue && options.AbsoluteExpiration <= creationTime)      {        throw new ArgumentOutOfRangeException(          nameof(DistributedCacheEntryOptions.AbsoluteExpiration),          options.AbsoluteExpiration.Value,          "The absolute expiration value must be in the future.");      }      var absoluteExpiration = options.AbsoluteExpiration;      if (options.AbsoluteExpirationRelativeToNow.HasValue)      {        absoluteExpiration = creationTime + options.AbsoluteExpirationRelativeToNow;      }      return absoluteExpiration;    }    public void Dispose()    {      if (_connection != null)      {        _connection.Close();      }    }  }}
using Microsoft.Extensions.Options;namespace Microsoft.Extensions.Caching.Redis{  /// <summary>  /// Configuration options for <see cref="RedisCache"/>.  /// </summary>  public class RedisCacheOptions : IOptions<RedisCacheOptions>  {    /// <summary>    /// The configuration used to connect to Redis.    /// </summary>    public string Configuration { get; set; }    /// <summary>    /// The Redis instance name.    /// </summary>    public string InstanceName { get; set; }    RedisCacheOptions IOptions<RedisCacheOptions>.Value    {      get { return this; }    }  }}
using System.Threading.Tasks;using StackExchange.Redis;namespace Microsoft.Extensions.Caching.Redis{  internal static class RedisExtensions  {    private const string HmGetScript = (@"return redis.call('HMGET', KEYS[1], unpack(ARGV))");    internal static RedisValue[] HashMemberGet(this IDatabase cache, string key, params string[] members)    {      var result = cache.ScriptEvaluate(        HmGetScript,        new RedisKey[] { key },        GetRedisMembers(members));      // TODO: Error checking?      return (RedisValue[])result;    }    internal static async Task<RedisValue[]> HashMemberGetAsync(      this IDatabase cache,      string key,      params string[] members)    {      var result = await cache.ScriptEvaluateAsync(        HmGetScript,        new RedisKey[] { key },        GetRedisMembers(members));      // TODO: Error checking?      return (RedisValue[])result;    }    private static RedisValue[] GetRedisMembers(params string[] members)    {      var redisMembers = new RedisValue[members.Length];      for (int i = 0; i < members.Length; i++)      {        redisMembers[i] = (RedisValue)members[i];      }      return redisMembers;    }  }}

配置啟用Session

我們在Startup中ConfigureServices增加

services.AddSingleton<IDistributedCache>(        serviceProvider =>          new RedisCache(new RedisCacheOptions          {            Configuration = "192.168.178.141:6379",            InstanceName = "Sample:"          }));      services.AddSession();

在Startup中Configure增加

app.UseSession(new SessionOptions() { IdleTimeout = TimeSpan.FromMinutes(30) });

到此我們的配置完畢,可以測試一下是否寫到了Redis中

驗證結果

在Mvc項目中,我們來實現如下代碼

if (string.IsNullOrEmpty(HttpContext.Session.GetString("D")))      {        var d = DateTime.Now.ToString();        HttpContext.Session.SetString("D", d);        HttpContext.Response.ContentType = "text/plain";        await HttpContext.Response.WriteAsync("Hello First timer///" + d);      }      else      {        HttpContext.Response.ContentType = "text/plain";        await HttpContext.Response.WriteAsync("Hello old timer///" + HttpContext.Session.GetString("D"));      }

運行我們發現第一次出現了Hello First timer字樣,刷新后出現了Hello old timer字樣,證明Session成功,再查看一下Redis看一下,有值了,這樣一個分布式的Session就成功實現了。

對于上面的實例我把源碼放在了:demo下載

Tianwei.Microsoft.Extensions.Caching.Redis ,只是ID加了Tianwei 空間名還是Microsoft.Extensions.Caching.Redis

從上面的實例我們發現微軟這次是真的開放了,這也意味著如果我們使用某些類不順手或不合適時可以自已寫自已擴展

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VeVb武林網。


注:相關教程知識閱讀請移步到ASP.NET教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
青青久久av北条麻妃海外网| 久久夜色精品国产亚洲aⅴ| 欧美激情精品久久久久| 欧美激情视频免费观看| 国产精品精品一区二区三区午夜版| 亚洲三级 欧美三级| 亚洲欧美一区二区三区久久| 精品欧美一区二区三区| 亚洲国产欧美精品| 亚洲新声在线观看| 欧美成年人视频| 亚洲天堂开心观看| 久久精品国产一区| 国产精品久久久久久久久久| 91久久久久久| 国产精品人人做人人爽| 中文字幕日韩专区| 久久91精品国产| 热久久这里只有| 最近2019年中文视频免费在线观看| 国产精品日韩在线播放| 亚洲一二三在线| 欧美激情久久久久| 亚洲国产日韩欧美在线动漫| 97在线视频精品| 国产成人精品一区二区| 国产视频久久网| 亚洲精品www久久久| 国产福利精品av综合导导航| 欧美精品国产精品日韩精品| 91在线中文字幕| www.日韩.com| 欧美激情a在线| 欧美精品电影免费在线观看| 国产欧美日韩高清| 久久久久久有精品国产| 国产精品精品一区二区三区午夜版| yw.139尤物在线精品视频| 精品国产一区二区三区久久| 日韩av在线播放资源| 国精产品一区一区三区有限在线| 欧美日韩日本国产| 国产视频综合在线| 中文字幕亚洲欧美在线| 九色精品美女在线| 两个人的视频www国产精品| 国产日产久久高清欧美一区| 欧美性xxxxxx| 欧美精品videos另类日本| 精品二区三区线观看| 免费成人高清视频| 久久久久这里只有精品| 亚洲男人天堂2024| 日韩av手机在线| 92国产精品视频| 亚洲综合在线做性| 国产中文字幕日韩| 日韩精品中文字幕在线播放| 国产精品大片wwwwww| 国产精品视频精品视频| 成人av资源在线播放| 日韩在线激情视频| 精品视频在线播放色网色视频| 亚洲free性xxxx护士白浆| 国产精品1234| 亚洲欧美日韩精品久久奇米色影视| 国产精品老牛影院在线观看| 久久久久99精品久久久久| 久久国产精品首页| 亚洲国产欧美自拍| 日韩精品免费在线视频| 国产精品偷伦一区二区| 亚洲成色777777女色窝| 欧美视频裸体精品| 亚洲香蕉av在线一区二区三区| 欧美日韩xxxxx| 国产精品日日做人人爱| 欧美一级淫片videoshd| 性亚洲最疯狂xxxx高清| 国产精品成人av在线| 大伊人狠狠躁夜夜躁av一区| 国产精品欧美激情在线播放| 91chinesevideo永久地址| 国产精品第三页| 在线观看国产精品日韩av| 亚洲第一福利在线观看| 中文字幕免费精品一区| 日韩精品免费电影| 免费97视频在线精品国自产拍| 国产成人jvid在线播放| 国产91免费看片| 国产一区玩具在线观看| 欧美成人久久久| 欧美激情免费观看| 亚洲精品视频免费| 高清欧美性猛交xxxx| 国产精品日本精品| 日韩欧美亚洲范冰冰与中字| 亚洲精品自拍第一页| 97国产真实伦对白精彩视频8| 丝袜亚洲另类欧美重口| 日韩综合中文字幕| 两个人的视频www国产精品| 久久久精品网站| 91精品视频免费看| 亚洲天堂开心观看| 九九视频直播综合网| 久久伊人91精品综合网站| 欧美日韩性视频在线| 国产欧美精品va在线观看| 亚洲精品福利在线观看| 激情成人中文字幕| 久久国产精品影片| 精品久久久视频| 欧美日韩一二三四五区| 色综合导航网站| 亚洲精品国产品国语在线| 亚洲最新在线视频| 中文字幕亚洲综合| 18久久久久久| 成人黄色短视频在线观看| 日韩中文字幕免费视频| 亚洲精品乱码久久久久久金桔影视| 国产69精品久久久久久| 色综合老司机第九色激情| 国产精品爽黄69天堂a| 精品国偷自产在线视频99| 国产亚洲欧美另类中文| 国产精品一区电影| 国产a∨精品一区二区三区不卡| 欧美激情啊啊啊| 亚洲综合自拍一区| 亚洲第一中文字幕| 中文字幕综合在线| 亚洲国产日韩欧美在线99| 91tv亚洲精品香蕉国产一区7ujn| 国产不卡精品视男人的天堂| 亚洲天堂色网站| 91久久精品国产91久久| 26uuu日韩精品一区二区| 欧美夜福利tv在线| 亚洲国产精品成人va在线观看| 欧美中文字幕第一页| 国产精品久久在线观看| 久久综合五月天| 成人精品久久久| 久久精品久久久久久| 亚洲欧美日韩精品久久奇米色影视| 亚洲精品一区二区三区不| 久久综合五月天| 国产精品吴梦梦| 91久久精品日日躁夜夜躁国产| 色诱女教师一区二区三区| 欧美一级片免费在线| 国产亚洲美女精品久久久| 日韩欧美在线免费观看| 欧美日韩免费看| 国产有码在线一区二区视频| 国产精品美女主播| 欧美一区二区视频97| 欧美中文字幕在线观看| 国模私拍一区二区三区| 热99精品只有里视频精品| 国产伊人精品在线|