麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 編程 > .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教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 欧美成人黄色小视频 | 91色成人 | 成人精品一区二区三区中文字幕 | 嗯~啊~用力~高h | 国产精品久久久久久久久久久久久久久久 | 末成年女av片一区二区 | 成人在线观看地址 | 精品一区二区三区四区在线 | 久草在线手机视频 | 国产一精品一av一免费爽爽 | 成人毛片100免费观看 | 中文字幕免费在线观看视频 | 久久精品欧美一区二区三区不卡 | 国产精品成人久久久久a级 av电影在线免费 | 欧美一级免费高清 | 一区二区三区无码高清视频 | 国产91九色在线播放 | 色视频在线| 18视频在线观看娇喘 | 亚洲婷婷日日综合婷婷噜噜噜 | 久久国产精品二国产精品 | 国产精品久久久久久久hd | 中文字幕偷拍 | 亚洲精品a级 | 色婷婷av一区二区三区久久 | 亚洲第一综合色 | 黄视频网站免费 | 欧美一级黄色录相 | 黄色网址电影 | 久久久久久久久久亚洲精品 | 九草在线视频 | 欧美成年性h版影视中文字幕 | 亚洲精品午夜国产va久久成人 | 黄色美女网站免费看 | 成人精品久久久 | 日韩毛片免费观看 | 色综合久久久久综合99 | 中国的免费的视频 | 毛片小网站 | 日韩毛片一区二区三区 | 久久久资源网 |