BaseContainer.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. #region Apache License Version 2.0
  2. /*----------------------------------------------------------------
  3. Copyright 2019 Jeffrey Su & Suzhou Senparc Network Technology Co.,Ltd.
  4. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
  5. except in compliance with the License. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software distributed under the
  8. License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
  9. either express or implied. See the License for the specific language governing permissions
  10. and limitations under the License.
  11. Detail: https://github.com/JeffreySu/WeiXinMPSDK/blob/master/license.md
  12. ----------------------------------------------------------------*/
  13. #endregion Apache License Version 2.0
  14. /*----------------------------------------------------------------
  15. Copyright (C) 2019 Senparc
  16. 文件名:WeixinContainer.cs
  17. 文件功能描述:微信容器(如Ticket、AccessToken)
  18. 创建标识:Senparc - 20151003
  19. 修改标识:Senparc - 20160321
  20. 修改描述:v4.5.18 完善 ItemCollection 中项目删除的方法
  21. 修改标识:Senparc - 20160808
  22. 修改描述:v4.7.0 删除 ItemCollection 属性,直接使用ContainerBag加入到缓存
  23. 修改标识:Senparc - 20160813
  24. 修改描述:v4.7.5 添加TryReRegister()方法,处理分布式缓存重启(丢失)的情况
  25. 修改标识:Senparc - 20170204
  26. 修改描述:v4.10.3 添加RemoveFromCache方法
  27. 修改标识:Senparc - 20180606
  28. 修改描述:缓存工厂重命名为 ContainerCacheStrategyFactory
  29. 修改标识:Senparc - 20180614
  30. 修改描述:CO2NET v0.1.0 ContainerBag 取消属性变动通知机制,使用手动更新缓存
  31. 修改标识:Senparc - 20180707
  32. 修改描述:Update() 方法中记录缓存时间 bag.CacheTime = DateTime.Now;
  33. 修改标识:Senparc - 20180917
  34. 修改描述:BaseContainer.GetFirstOrDefaultAppId() 方法添加 PlatformType 属性
  35. 修改标识:Senparc - 20170522
  36. 修改描述:v6.3.2 修改 DateTime 为 DateTimeOffset
  37. ----------------------------------------------------------------*/
  38. using System;
  39. using System.Collections.Generic;
  40. using System.Linq;
  41. using Senparc.CO2NET.Cache;
  42. using Senparc.Weixin.Cache;
  43. using Senparc.Weixin.Exceptions;
  44. using Senparc.Weixin.Helpers;
  45. namespace Senparc.Weixin.Containers
  46. {
  47. /// <summary>
  48. /// IBaseContainer
  49. /// </summary>
  50. public interface IBaseContainer
  51. {
  52. }
  53. /// <summary>
  54. /// 带IBaseContainerBag泛型的IBaseContainer
  55. /// </summary>
  56. /// <typeparam name="TBag"></typeparam>
  57. public interface IBaseContainer<TBag> : IBaseContainer where TBag : IBaseContainerBag, new()
  58. {
  59. }
  60. /// <summary>
  61. /// 微信容器接口(如Ticket、AccessToken)
  62. /// </summary>
  63. /// <typeparam name="TBag"></typeparam>
  64. [Serializable]
  65. public abstract class BaseContainer<TBag> : IBaseContainer<TBag> where TBag : class, IBaseContainerBag, new()
  66. {
  67. private static IBaseObjectCacheStrategy _baseCache = null;
  68. private static IContainerCacheStrategy _containerCache = null;
  69. /// <summary>
  70. /// 获取符合当前缓存策略配置的缓存的操作对象实例
  71. /// </summary>
  72. protected static IBaseObjectCacheStrategy /*IBaseCacheStrategy<string,Dictionary<string, TBag>>*/ Cache
  73. {
  74. get
  75. {
  76. //使用工厂模式或者配置进行动态加载
  77. //return CacheStrategyFactory.GetContainerCacheStrategyInstance();
  78. //以下代码可以实现缓存“热切换”,损失的效率有限。如果需要追求极致效率,可以禁用type的判断
  79. var containerCacheStrategy = ContainerCacheStrategyFactory.GetContainerCacheStrategyInstance()/*.ContainerCacheStrategy*/;
  80. if (_containerCache == null || _containerCache.GetType() != containerCacheStrategy.GetType())
  81. {
  82. _containerCache = containerCacheStrategy;
  83. }
  84. if (_baseCache == null)
  85. {
  86. _baseCache = _baseCache ?? containerCacheStrategy.BaseCacheStrategy();
  87. }
  88. return _baseCache;
  89. }
  90. }
  91. //2016.8.8注释掉
  92. /// <summary>
  93. /// 获取当前容器的数据项集合
  94. /// </summary>
  95. /// <returns></returns>
  96. //protected static IContainerItemCollection ItemCollection
  97. //{
  98. // get
  99. // {
  100. // var cacheKey = GetContainerCacheKey();
  101. // IContainerItemCollection itemCollection;
  102. // if (!Cache.CheckExisted(cacheKey))
  103. // {
  104. // itemCollection = new ContainerItemCollection();
  105. // //CollectionList[cacheKey] = newItemCollection;
  106. // //直接执行
  107. // //{
  108. // //}
  109. // //var containerCacheStrategy = CacheStrategyFactory.GetContainerCacheStrategyInstance();
  110. // //containerCacheStrategy.InsertToCache(cacheKey, itemCollection);//插入到缓存
  111. // //保存到缓存队列,等待执行
  112. // SenparcMessageQueue mq = new SenparcMessageQueue();
  113. // var mqKey = SenparcMessageQueue.GenerateKey("ContainerItemCollection", typeof(BaseContainer<TBag>), cacheKey, "InsertItemCollection");
  114. // mq.Add(mqKey, () =>
  115. // {
  116. // var containerCacheStrategy = CacheStrategyFactory.GetContainerCacheStrategyInstance();
  117. // containerCacheStrategy.InsertToCache(cacheKey, itemCollection);//插入到缓存
  118. // });
  119. // }
  120. // else
  121. // {
  122. // itemCollection = Cache.Get(cacheKey);
  123. // }
  124. // return itemCollection;
  125. // }
  126. //}
  127. ///// <summary>
  128. ///// 获取Container缓存Key
  129. ///// </summary>
  130. ///// <returns></returns>
  131. //public static string GetContainerCacheKey()
  132. //{
  133. // return ContainerHelper.GetCacheKey(typeof(TBag));
  134. //}
  135. /// <summary>
  136. /// 进行注册过程的委托
  137. /// </summary>
  138. protected static Func<TBag> RegisterFunc { get; set; }
  139. /// <summary>
  140. /// 如果注册不成功,测尝试重新注册(前提是已经进行过注册),这种情况适用于分布式缓存被清空(重启)的情况。
  141. /// </summary>
  142. private static TBag TryReRegister()
  143. {
  144. return RegisterFunc();
  145. //TODO:如果需要校验ContainerBag的正确性,可以从返回值进行判断
  146. }
  147. /// <summary>
  148. /// 返回已经注册的第一个AppId
  149. /// </summary>
  150. /// <returns></returns>
  151. public static string GetFirstOrDefaultAppId(PlatformType platformType)
  152. {
  153. string appId = null;
  154. switch (platformType)
  155. {
  156. case PlatformType.MP:
  157. appId = Senparc.Weixin.Config.SenparcWeixinSetting.WeixinAppId;
  158. break;
  159. case PlatformType.Open:
  160. appId = Senparc.Weixin.Config.SenparcWeixinSetting.WeixinAppId;
  161. break;
  162. case PlatformType.WxOpen:
  163. appId = Senparc.Weixin.Config.SenparcWeixinSetting.WxOpenAppId;
  164. break;
  165. case PlatformType.QY:
  166. break;
  167. case PlatformType.Work:
  168. break;
  169. default:
  170. break;
  171. }
  172. if (appId == null)
  173. {
  174. var firstBag = GetAllItems().FirstOrDefault() as IBaseContainerBag_AppId;
  175. appId = firstBag == null ? null : firstBag.AppId;
  176. }
  177. return appId;
  178. }
  179. /// <summary>
  180. /// 获取ItemCollection缓存Key
  181. /// </summary>
  182. /// <param name="shortKey">最简短的Key,比如AppId,不需要考虑容器前缀</param>
  183. /// <returns></returns>
  184. public static string GetBagCacheKey(string shortKey)
  185. {
  186. return ContainerHelper.GetItemCacheKey(typeof(TBag), shortKey);
  187. }
  188. ///// <summary>
  189. ///// 获取完整的数据集合的列表,包括所有的Container数据在内(建议不要进行任何修改操作)
  190. ///// </summary>
  191. ///// <returns></returns>
  192. //public static IDictionary<string, IContainerItemCollection> GetCollectionList()
  193. //{
  194. // return CollectionList;
  195. //}
  196. /// <summary>
  197. /// 获取所有容器内已经注册的项目
  198. /// (此方法将会遍历Dictionary,当数据项很多的时候效率会明显降低)
  199. /// </summary>
  200. /// <returns></returns>
  201. public static List<TBag> GetAllItems()
  202. {
  203. //return Cache.GetAll<TBag>().Values
  204. return _containerCache.GetAll<TBag>().Values
  205. //如果需要做进一步的筛选,则使用Select或Where,但需要注意效率问题
  206. //.Select(z => z)
  207. .ToList();
  208. }
  209. /// <summary>
  210. /// 尝试获取某一项Bag
  211. /// </summary>
  212. /// <param name="shortKey"></param>
  213. /// <returns></returns>
  214. public static TBag TryGetItem(string shortKey)
  215. {
  216. var cacheKey = GetBagCacheKey(shortKey);
  217. if (Cache.CheckExisted(cacheKey))
  218. {
  219. return Cache.Get<TBag>(cacheKey);
  220. }
  221. return default(TBag);
  222. }
  223. /// <summary>
  224. /// 尝试获取某一项Bag中的具体某个属性
  225. /// </summary>
  226. /// <param name="shortKey"></param>
  227. /// <param name="property">具体某个属性</param>
  228. /// <returns></returns>
  229. public static TK TryGetItem<TK>(string shortKey, Func<TBag, TK> property)
  230. {
  231. var cacheKey = GetBagCacheKey(shortKey);
  232. if (Cache.CheckExisted(cacheKey))
  233. {
  234. var item = Cache.Get<TBag>(cacheKey);
  235. return property(item);
  236. }
  237. return default(TK);
  238. }
  239. /// <summary>
  240. /// 更新数据项
  241. /// </summary>
  242. /// <param name="shortKey">即bag.Key</param>
  243. /// <param name="bag">为null时删除该项</param>
  244. /// <param name="expiry"></param>
  245. public static void Update(string shortKey, TBag bag, TimeSpan? expiry)
  246. {
  247. var cacheKey = GetBagCacheKey(shortKey);
  248. if (bag == null)
  249. {
  250. Cache.RemoveFromCache(cacheKey);
  251. }
  252. else
  253. {
  254. if (string.IsNullOrEmpty(bag.Key))
  255. {
  256. bag.Key = shortKey;//确保Key有值,形如:wx669ef95216eef885,最底层的Key
  257. }
  258. //else
  259. //{
  260. // cacheKey = bag.Key;//统一key
  261. //}
  262. //if (string.IsNullOrEmpty(cacheKey))
  263. //{
  264. // throw new WeixinException("key和value,Key不可以同时为null或空字符串!");
  265. //}
  266. //var c1 = ItemCollection.GetCount();
  267. //ItemCollection[key] = bag;
  268. //var c2 = ItemCollection.GetCount();
  269. }
  270. //var containerCacheKey = GetContainerCacheKey();
  271. bag.CacheTime = SystemTime.Now;
  272. Cache.Update(cacheKey, bag, expiry);//更新到缓存,TODO:有的缓存框架可一直更新Hash中的某个键值对
  273. }
  274. /// <summary>
  275. /// 更新已经添加过的数据项
  276. /// </summary>
  277. /// <param name="bag">为null时删除该项</param>
  278. /// <param name="expiry"></param>
  279. public static void Update(TBag bag, TimeSpan? expiry)
  280. {
  281. if (string.IsNullOrEmpty(bag.Key))
  282. {
  283. throw new WeixinException("ContainerBag 更新时,ey 不能为空!类型:" + bag.GetType());
  284. }
  285. Update(bag.Key, bag, expiry);
  286. }
  287. /// <summary>
  288. /// 更新数据项(本地缓存不会改变原有值的 HashCode)
  289. /// </summary>
  290. /// <param name="shortKey"></param>
  291. /// <param name="partialUpdate">为null时删除该项</param>
  292. /// <param name="expiry"></param>
  293. public static void Update(string shortKey, Action<TBag> partialUpdate, TimeSpan? expiry)
  294. {
  295. var cacheKey = GetBagCacheKey(shortKey);
  296. if (partialUpdate == null)
  297. {
  298. Cache.RemoveFromCache(cacheKey);//移除对象
  299. }
  300. else
  301. {
  302. if (!Cache.CheckExisted(cacheKey))
  303. {
  304. var newBag = new TBag()
  305. {
  306. Key = cacheKey//确保这一项Key已经被记录
  307. };
  308. Cache.Set(cacheKey, newBag, expiry);
  309. }
  310. partialUpdate(TryGetItem(shortKey));//更新对象
  311. }
  312. }
  313. /// <summary>
  314. /// 检查Key是否已经注册
  315. /// </summary>
  316. /// <param name="shortKey"></param>
  317. /// <returns></returns>
  318. public static bool CheckRegistered(string shortKey)
  319. {
  320. var cacheKey = GetBagCacheKey(shortKey);
  321. var registered = Cache.CheckExisted(cacheKey);
  322. if (!registered && RegisterFunc != null)
  323. {
  324. //如果注册不成功,测尝试重新注册(前提是已经进行过注册),这种情况适用于分布式缓存被清空(重启)的情况。
  325. TryReRegister();
  326. }
  327. return Cache.CheckExisted(cacheKey);
  328. }
  329. /// <summary>
  330. /// 从缓存中删除指定项
  331. /// </summary>
  332. /// <param name="shortKey"></param>
  333. public static void RemoveFromCache(string shortKey)
  334. {
  335. var cacheKey = GetBagCacheKey(shortKey);
  336. Cache.RemoveFromCache(cacheKey);
  337. }
  338. }
  339. }