分析了一下 IAmbientDataContext 的实现,感觉与 ICurrentUnitOfWorkProvider 类似,内部都是通过 AsyncLocal 来进行处理的。
public class AsyncLocalAmbientDataContext : IAmbientDataContext, ISingletonDependency { // 这里的字典是以 ContextKey 与 ScopeItem 的 Id 构成的。 private static readonly ConcurrentDictionary<string, AsyncLocal<object>> AsyncLocalDictionary = new ConcurrentDictionary<string, AsyncLocal<object>>(); public void SetData(string key, object value) { // 设置指定 ContextKey 对应的 Id 值。 var asyncLocal = AsyncLocalDictionary.GetOrAdd(key, (k) => new AsyncLocal<object>()); asyncLocal.Value = value; } public object GetData(string key) { // 获取指定 ContextKey 对应的 Id 值。 var asyncLocal = AsyncLocalDictionary.GetOrAdd(key, (k) => new AsyncLocal<object>()); return asyncLocal.Value; } }从开始到这里使用并行字典的情况来看,这里这么做的原因很简单,是为了处理异步上下文切换的情况,确保 ContextKey 对应的 Id 是一致的,防止在 Get/Set Data 的时候出现 意外的情况。
最后呢在具体的 Session 实现类 ClaimsAbpSession 当中要获取 UserId 会经过下面的步骤:
public override long? UserId { get { // 尝试从临时对象中获取数据。 if (OverridedValue != null) { return OverridedValue.UserId; } // 从 JWT Token 当中获取 UserId 信息。 return userId; } }最后我再贴上 ScopeItem 的定义。
private class ScopeItem { public string Id { get; } public ScopeItem Outer { get; } public T Value { get; } public ScopeItem(T value, ScopeItem outer = null) { Id = Guid.NewGuid().ToString(); Value = value; Outer = outer; } }这个临时值变更可能是 Abp 用法当中最为复杂的一个,牵扯到了异步上下文和 using 语句嵌套的问题。但仔细阅读源码之后,其实有一种豁然开朗的感觉,也加强了对于 C# 程序设计的理解。
三、结语通过学习 Abp 框架,也了解了自己在基础方面的诸多不足。其次也是能够看到一些比较实用新奇的写法,你也可以在自己项目中进行应用,本文主要是起一个抛砖引玉的作用。最近年底了,事情也比较多,博客也是疏于更新。后面会陆续恢复博文更新,尽量 2 天 1 更,新年新气象。