激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務(wù)器之家 - 編程語言 - ASP.NET教程 - 解讀ASP.NET 5 & MVC6系列教程(8):Session與Caching

解讀ASP.NET 5 & MVC6系列教程(8):Session與Caching

2020-01-16 15:27湯姆大叔 ASP.NET教程

這篇文章主要介紹了ASP.NET 5 中的Session與Caching配置和使用,需要的朋友可以參考下

在之前的版本中,Session存在于System.Web中,新版ASP.NET 5中由于不在依賴于System.Web.dll庫了,所以相應(yīng)的,Session也就成了ASP.NET 5中一個(gè)可配置的模塊(middleware)了。

配置啟用Session

ASP.NET 5中的Session模塊存在于Microsoft.AspNet.Session類庫中,要啟用Session,首先需要在project.json中的dependencies節(jié)點(diǎn)中添加如下內(nèi)容:

?
1
"Microsoft.AspNet.Session": "1.0.0-beta3"

然后在ConfigureServices中添加Session的引用(并進(jìn)行配置):

?
1
2
3
services.AddCaching(); // 這兩個(gè)必須同時(shí)添加,因?yàn)镾ession依賴于Caching
services.AddSession();
//services.ConfigureSession(null); 可以在這里配置,也可以再后面進(jìn)行配置

最后在Configure方法中,開啟使用Session的模式,如果在上面已經(jīng)配置過了,則可以不再傳入配置信息,否則還是要像上面的配置信息一樣,傳入Session的配置信息,代碼如下:

?
1
2
3
4
5
app.UseInMemorySession(configure:s => { s.IdleTimeout = TimeSpan.FromMinutes(30); });
//app.UseSession(o => { o.IdleTimeout = TimeSpan.FromSeconds(30); });
//app.UseInMemorySession(null, null); //開啟內(nèi)存Session
//app.UseDistributedSession(null, null);//開啟分布式Session,也即持久化Session
//app.UseDistributedSession(new RedisCache(new RedisCacheOptions() { Configuration = "localhost" }));

對于UseInMemorySession方法,接收2個(gè)可選參數(shù),分別是:IMemoryCache可用于修改Session數(shù)據(jù)的默認(rèn)保存地址;Action<SessionOptions>委托則可以讓你修改默認(rèn)選項(xiàng),比如Session cookie的路徑、默認(rèn)的過期時(shí)間等。本例中,我們修改默認(rèn)過期時(shí)間為30分鐘。

注意:該方法必須在app.UseMvc之前調(diào)用,否則在Mvc里獲取不到Session,而且會出錯。

獲取和設(shè)置Session

獲取和設(shè)置Session對象,一般是在Controller的action里通過this.Context.Session來獲取的,其獲取的是一個(gè)基于接口ISessionCollection的實(shí)例。該接口可以通過索引、Set、TryGetValue等方法進(jìn)行Session值的獲取和設(shè)置,但我們發(fā)現(xiàn)在獲取和設(shè)置Session的時(shí)候,我們只能使用byte[]類型,而不能像之前版本的Session一樣可以設(shè)置任意類型的數(shù)據(jù)。原因是因?yàn)椋掳姹镜腟ession要支持在遠(yuǎn)程服務(wù)器上存儲,就需要支持序列化,所以才強(qiáng)制要求保存為byte[]類型。所以我們在保存Session的時(shí)候,需要將其轉(zhuǎn)換為byte[]才能進(jìn)行保存,并且獲取以后要再次將byte[]轉(zhuǎn)換為自己的原有的類型才行。這種形式太麻煩了,好在微軟在Microsoft.AspNet.Http命名空間(所屬Microsoft.AspNet.Http.Extensions.dll中)下,為我們添加了幾個(gè)擴(kuò)展方法,分別用于設(shè)置和保存byte[]類型、int類型、以及string類型,代碼如下:

?
1
2
3
4
5
6
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命名空間以后,我們就可以通過如下代碼進(jìn)行Session的設(shè)置和獲取了:

?
1
2
3
4
5
Context.Session.SetString("Name", "Mike");
Context.Session.SetInt("Age", 21);
 
ViewBag.Name = Context.Session.GetString("Name");
ViewBag.Age = Context.Session.GetInt("Age");

自定義類型的Session設(shè)置和獲取

前面我們說了,要保存自定義類型的Session,需要將其類型轉(zhuǎn)換成byte[]數(shù)組才行,在本例中,我們對bool類型的Session數(shù)據(jù)進(jìn)行設(shè)置和獲取的代碼,示例如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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)行使用了,示例如下:

?
1
2
Context.Session.SetBoolean("Liar", true);
ViewBag.Liar = Context.Session.GetBoolean("Liar");

另外,ISessionCollection接口上還提供了Remove(string key)和Clear()兩個(gè)方法分別用于刪除某個(gè)Session值和清空所有的Session值的功能。但同時(shí)也需要注意,該接口并沒提供之前版本中的Abandon方法功能。

基于Redis的Session管理

使用分布式Session,其主要工作就是將Session保存的地方從原來的內(nèi)存換到分布式存儲上,本節(jié),我們以Redis存儲為例來講解分布式Session的處理。

先查看使用分布式Session的擴(kuò)展方法,示例如下,我們可以看到,其Session容器需要是一個(gè)支持IDistributedCache的接口示例。

?
1
public static IApplicationBuilder UseDistributedSession([NotNullAttribute]this IApplicationBuilder app, IDistributedCache cache, Action<SessionOptions> configure = null);

該接口是緩存Caching的通用接口,也就是說,只要我們實(shí)現(xiàn)了緩存接口,就可以將其用于Session的管理。進(jìn)一步查看該接口發(fā)現(xiàn),該接口中定義的Set方法還需要實(shí)現(xiàn)一個(gè)ICacheContext類型的緩存上下文(以便在調(diào)用的時(shí)候讓其它程序進(jìn)行委托調(diào)用),接口定義分別如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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);
}

接下來,我們基于Redis來實(shí)現(xiàn)上述功能,創(chuàng)建RedisCache類,并繼承IDistributedCache,引用StackExchange.Redis程序集,然后實(shí)現(xiàn)IDistributedCache接口的所有方法和屬性,代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
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)
  {
   var ignored = GetAndRefresh(key, getData: false);
  }
 
  private Stream GetAndRefresh(string key, bool getData)
  {
   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 new MemoryStream(results[2], writable: false);
   }
   return null;
  }
 
  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)
  {
   // 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
   }
  }
 
  public void Remove(string key)
  {
   Connect();
 
   _cache.KeyDelete(_instance + key);
   // TODO: Error handling
  }
 }
}

在上述代碼中,我們使用了自定義類RedisCacheOptions作為Redis的配置信息類,為了實(shí)現(xiàn)基于POCO的配置定義,我們還繼承了IOptions接口,該類的定義如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class RedisCacheOptions : IOptions<RedisCacheOptions>
{
 public string Configuration { get; set; }
 
 public string InstanceName { get; set; }
 
 RedisCacheOptions IOptions<RedisCacheOptions>.Options
 {
  get { return this; }
 }
 
 RedisCacheOptions IOptions<RedisCacheOptions>.GetNamedOptions(string name)
 {
  return this;
 }
}

第三部,定義委托調(diào)用時(shí)使用的緩存上下文類CacheContext,具體代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
using Microsoft.Framework.Cache.Distributed;
using System;
using System.IO;
 
namespace Microsoft.Framework.Caching.Redis
{
 internal class CacheContext : ICacheContext
 {
  private readonly MemoryStream _data = new MemoryStream();
 
  internal CacheContext(string key)
  {
   Key = key;
   CreationTime = DateTimeOffset.UtcNow;
  }
 
  /// <summary>
  /// The key identifying this entry.
  /// </summary>
  public string Key { get; internal set; }
 
  /// <summary>
  /// The state passed into Set. This can be used to avoid closures.
  /// </summary>
  public object State { get; internal set; }
 
  public Stream Data { get { return _data; } }
 
  internal DateTimeOffset CreationTime { get; set; } // 可以讓委托設(shè)置創(chuàng)建時(shí)間
 
  internal DateTimeOffset? AbsoluteExpiration { get; private set; }
 
  internal TimeSpan? SlidingExpiration { get; private set; }
 
  public void SetAbsoluteExpiration(TimeSpan relative) // 可以讓委托設(shè)置相對過期時(shí)間
  {
   if (relative <= TimeSpan.Zero)
   {
    throw new ArgumentOutOfRangeException("relative", relative, "The relative expiration value must be positive.");
   }
   AbsoluteExpiration = CreationTime + relative;
  }
 
  public void SetAbsoluteExpiration(DateTimeOffset absolute) // 可以讓委托設(shè)置絕對過期時(shí)間
  {
   if (absolute <= CreationTime)
   {
    throw new ArgumentOutOfRangeException("absolute", absolute, "The absolute expiration value must be in the future.");
   }
   AbsoluteExpiration = absolute.ToUniversalTime();
  }
 
  public void SetSlidingExpiration(TimeSpan offset) // 可以讓委托設(shè)置offset過期時(shí)間
  {
   if (offset <= TimeSpan.Zero)
   {
    throw new ArgumentOutOfRangeException("offset", offset, "The sliding expiration value must be positive.");
   }
   SlidingExpiration = offset;
  }
 
  internal long? GetExpirationInSeconds()
  {
   if (AbsoluteExpiration.HasValue && SlidingExpiration.HasValue)
   {
    return (long)Math.Min((AbsoluteExpiration.Value - CreationTime).TotalSeconds, SlidingExpiration.Value.TotalSeconds);
   }
   else if (AbsoluteExpiration.HasValue)
   {
    return (long)(AbsoluteExpiration.Value - CreationTime).TotalSeconds;
   }
   else if (SlidingExpiration.HasValue)
   {
    return (long)SlidingExpiration.Value.TotalSeconds;
   }
   return null;
  }
 
  internal byte[] GetBytes()
  {
   return _data.ToArray();
  }
 }
}

最后一步定義,RedisCache中需要的根據(jù)key鍵獲取緩存值的快捷方法,代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
using StackExchange.Redis;
using System;
 
namespace Microsoft.Framework.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 redisMembers = new RedisValue[members.Length];
   for (int i = 0; i < members.Length; i++)
   {
    redisMembers[i] = (RedisValue)members[i];
   }
   var result = cache.ScriptEvaluate(HmGetScript, new RedisKey[] { key }, redisMembers);
   // TODO: Error checking?
   return (RedisValue[])result;
  }
 }
}

至此,所有的工作就完成了,將該緩存實(shí)現(xiàn)注冊為Session的provider的代碼方法如下:

?
1
2
3
4
5
6
7
8
app.UseDistributedSession(new RedisCache(new RedisCacheOptions()
{
 Configuration = "此處填寫 redis的地址",
 InstanceName = "此處填寫自定義實(shí)例名"
}), options =>
{
 options.CookieHttpOnly = true;
});

參考:http://www.mikesdotnetting.com/article/270/sessions-in-asp-net-5

關(guān)于Caching

默認(rèn)情況下,本地緩存使用的是IMemoryCache接口的示例,可以通過獲取該接口的示例來對本地緩存進(jìn)行操作,示例代碼如下:

?
1
2
3
var cache = app.ApplicationServices.GetRequiredService<IMemoryCache>();
var obj1 = cache.Get("key1");
bool obj2 = cache.Get<bool>("key2");

對于,分布式緩存,由于AddCaching,默認(rèn)將IMemoryCache實(shí)例作為分布式緩存的provider了,代碼如下:

?
1
2
3
4
5
6
7
8
9
public static class CachingServicesExtensions
{
 public static IServiceCollection AddCaching(this IServiceCollection collection)
 {
  collection.AddOptions();
  return collection.AddTransient<IDistributedCache, LocalCache>()
   .AddSingleton<IMemoryCache, MemoryCache>();
 }
}

所以,要使用新的分布式Caching實(shí)現(xiàn),我們需要注冊自己的實(shí)現(xiàn),代碼如下:

?
1
2
3
4
5
6
services.AddTransient<IDistributedCache, RedisCache>();
services.Configure<RedisCacheOptions>(opt =>
{
 opt.Configuration = "此處填寫 redis的地址";
 opt.InstanceName = "此處填寫自定義實(shí)例名";
});

基本的使用方法如下:

?
1
2
3
4
var cache = app.ApplicationServices.GetRequiredService<IDistributedCache>();
cache.Connect();
var obj1 = cache.Get("key1"); //該對象是流,需要將其轉(zhuǎn)換為強(qiáng)類型,或自己再編寫擴(kuò)展方法
var bytes = obj1.ReadAllBytes();

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 精品久久久久久中文字幕 | 综合网日日天干夜夜久久 | 手机国产乱子伦精品视频 | 午夜影院a | 91九色视频 | 亚洲av毛片久久久久 | 久久久久久久一区二区三区 | 欧美精品久久久久久久久老牛影院 | 大学生a级毛片免费视频 | 在线播放免费人成毛片乱码 | 午夜色片 | 久久撸视频 | 在线成人免费观看 | 国产免费v片 | 午夜视频免费播放 | 干色视频 | 特级a欧美做爰片毛片 | 国产91九色视频 | 久久成人激情视频 | 亚洲人成在线播放网站 | 欧美视频国产精品 | 国产视频第一区 | 成人黄色在线免费观看 | 草操影院 | 黄色一级电影网 | 精品一区二区在线视频 | 久久久久久久久国产 | 新久草视频 | 国产成人精品视频在线 | 国产精品岛国久久久久久 | 久久久久九九九女人毛片 | 久久99精品久久久久久秒播蜜臀 | 午夜亚洲视频 | 婷婷一区二区三区 | 久久逼逼 | 久久午夜国产 | 校花被肉干高h潮不断 | 国产精品一区久久久久 | 无遮挡一级毛片视频 | 一区国产精品 | 成人在线观看免费观看 |