Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

121 lines
4.2KB

  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. if (!_mapper.TryRemove(id, out var rels)) return null;
  58. return rels.Select(e => e.Item1).Distinct();
  59. }
  60. public void CleanUpExpiredMapper()
  61. {
  62. if (_mapper.Keys.Count == 0)
  63. {
  64. //释放内存
  65. _mapper.Clear();
  66. _mapper = new ConcurrentDictionary<string, List<Tuple<string, DateTime>>>();
  67. return;
  68. }
  69. foreach (var id in _mapper.Keys)
  70. {
  71. // if (!_mapper.TryRemove(id, out List<Tuple<string, DateTime>> rels)) continue;
  72. if (!_mapper.TryRemove(id, out var rels)) continue;
  73. var availableList = rels.Where(t => DateTime.Now.Subtract(t.Item2).TotalSeconds < DurableSecond).ToList();
  74. _mapper.AddOrUpdate(id,
  75. _ => availableList,
  76. (_, _presentValue) =>
  77. {
  78. _presentValue.AddRange(availableList);
  79. return _presentValue;
  80. });
  81. }
  82. }
  83. /// <summary>
  84. /// 附加缓存键和实体主键(或实体主键列表)的映射关系
  85. /// </summary>
  86. /// <param name="key"></param>
  87. /// <param name="entities">实体列表</param>
  88. private void MapKeyToEntity(string key, IEnumerable<object> entities)
  89. {
  90. if (entities == null || entities.Count() == 0) return;
  91. foreach (var entity in entities)
  92. {
  93. string id = _m_GetPrimaryKey.Invoke(entity, null) + "";
  94. _mapper.AddOrUpdate(id,
  95. _ => new List<Tuple<string, DateTime>> { new Tuple<string, DateTime>(key, DateTime.Now) },
  96. (_, _presentValue) =>
  97. {
  98. _presentValue.Add(new Tuple<string, DateTime>(key, DateTime.Now));
  99. return _presentValue;
  100. });
  101. }
  102. }
  103. }
  104. }