You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

121 lines
4.1KB

  1. using Newtonsoft.Json;
  2. using System.Collections.Concurrent;
  3. using System.Reflection;
  4. using HealthMonitor.Util.Entities.Interfaces;
  5. namespace HealthMonitor.Core.Cache
  6. {
  7. public class EntityCacheHandler : IEntityCacheHandler
  8. {
  9. /// <summary>
  10. /// 结构示例
  11. /// {
  12. /// "id_xxx": //实体的id值
  13. /// [
  14. /// "key_yyy_1", //缓存有该实体的缓存键
  15. /// "key_yyy_2",
  16. /// ...
  17. /// ],
  18. /// ...
  19. /// }
  20. /// </summary>
  21. private ConcurrentDictionary<string, List<Tuple<string, DateTime>>> _mapper;
  22. private readonly MethodInfo _m_GetPrimaryKey;
  23. public int DurableSecond { get; private set; }
  24. public Type EntityType { get; private set; }
  25. public EntityCacheHandler(Type entityType, int durableSeconds)
  26. {
  27. EntityType = entityType;
  28. DurableSecond = durableSeconds;
  29. _mapper = new ConcurrentDictionary<string, List<Tuple<string, DateTime>>>();
  30. _m_GetPrimaryKey = entityType.GetMethod(nameof(IEntity.GetPrimaryKey))!;
  31. }
  32. public IEnumerable<object>? GetEntitiesCache(string key)
  33. {
  34. string json = RedisHelper.Get(key);
  35. if (string.IsNullOrEmpty(json)) return null;
  36. var t = typeof(IEnumerable<>);
  37. return JsonConvert.DeserializeObject(json, t.MakeGenericType(EntityType)) as IEnumerable<object>;
  38. }
  39. public void SetEntityCache(string key, object param, bool isEnumerable = false)
  40. {
  41. if (param == null) return;
  42. IEnumerable<object> value;
  43. if (isEnumerable) value = (param as IEnumerable<object>)!;
  44. else value = new List<object> { param };
  45. RedisHelper.SetAsync(key, value, DurableSecond);
  46. MapKeyToEntity(key, value!);
  47. }
  48. public void DeleteEntityCache(string key)
  49. {
  50. RedisHelper.DelAsync(key);
  51. }
  52. public IEnumerable<string>? UnmapKeyFromEntity(object entity)
  53. {
  54. if (entity == null) return null;
  55. string id = _m_GetPrimaryKey.Invoke(entity, null) + "";
  56. if (!_mapper.TryRemove(id, out List<Tuple<string, DateTime>> rels)) return null;
  57. return rels.Select(e => e.Item1).Distinct();
  58. }
  59. public void CleanUpExpiredMapper()
  60. {
  61. if (_mapper.Keys.Count == 0)
  62. {
  63. //释放内存
  64. _mapper.Clear();
  65. _mapper = new ConcurrentDictionary<string, List<Tuple<string, DateTime>>>();
  66. return;
  67. }
  68. foreach (var id in _mapper.Keys)
  69. {
  70. if (!_mapper.TryRemove(id, out List<Tuple<string, DateTime>> rels)) continue;
  71. var availableList = rels.Where(t => DateTime.Now.Subtract(t.Item2).TotalSeconds < DurableSecond).ToList();
  72. _mapper.AddOrUpdate(id,
  73. _ => availableList,
  74. (_, _presentValue) =>
  75. {
  76. _presentValue.AddRange(availableList);
  77. return _presentValue;
  78. });
  79. }
  80. }
  81. /// <summary>
  82. /// 附加缓存键和实体主键(或实体主键列表)的映射关系
  83. /// </summary>
  84. /// <param name="key"></param>
  85. /// <param name="entities">实体列表</param>
  86. private void MapKeyToEntity(string key, IEnumerable<object> entities)
  87. {
  88. if (entities == null || entities.Count() == 0) return;
  89. foreach (var entity in entities)
  90. {
  91. string id = _m_GetPrimaryKey.Invoke(entity, null) + "";
  92. _mapper.AddOrUpdate(id,
  93. _ => new List<Tuple<string, DateTime>> { new Tuple<string, DateTime>(key, DateTime.Now) },
  94. (_, _presentValue) =>
  95. {
  96. _presentValue.Add(new Tuple<string, DateTime>(key, DateTime.Now));
  97. return _presentValue;
  98. });
  99. }
  100. }
  101. }
  102. }