Browse Source

refactoring DynamicNotificationDefinitionStore

pull/765/head
cKey 3 years ago
parent
commit
0cb6f0a979
  1. 4
      aspnet-core/modules/common/LINGYUN.Abp.Notifications.Common/LINGYUN/Abp/Notifications/NotificationsCommonNotificationDefinitionProvider.cs
  2. 1
      aspnet-core/modules/common/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/INotificationDefinitionContext.cs
  3. 5
      aspnet-core/modules/common/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/NotificationDefinition.cs
  4. 3
      aspnet-core/modules/common/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/NotificationDefinitionContext.cs
  5. 12
      aspnet-core/modules/common/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/NotificationGroupDefinition.cs
  6. 24
      aspnet-core/modules/notifications/LINGYUN.Abp.Notifications.Domain/LINGYUN/Abp/Notifications/AbpNotificationsManagementOptions.cs
  7. 326
      aspnet-core/modules/notifications/LINGYUN.Abp.Notifications.Domain/LINGYUN/Abp/Notifications/DynamicNotificationDefinitionCache.cs
  8. 113
      aspnet-core/modules/notifications/LINGYUN.Abp.Notifications.Domain/LINGYUN/Abp/Notifications/DynamicNotificationDefinitionInMemoryCache.cs
  9. 156
      aspnet-core/modules/notifications/LINGYUN.Abp.Notifications.Domain/LINGYUN/Abp/Notifications/DynamicNotificationDefinitionStore.cs
  10. 13
      aspnet-core/modules/notifications/LINGYUN.Abp.Notifications.Domain/LINGYUN/Abp/Notifications/IDynamicNotificationDefinitionCache.cs
  11. 25
      aspnet-core/modules/notifications/LINGYUN.Abp.Notifications.Domain/LINGYUN/Abp/Notifications/IDynamicNotificationDefinitionStoreCache.cs

4
aspnet-core/modules/common/LINGYUN.Abp.Notifications.Common/LINGYUN/Abp/Notifications/NotificationsCommonNotificationDefinitionProvider.cs

@ -12,7 +12,7 @@ public class NotificationsCommonNotificationDefinitionProvider : NotificationDef
var commonGroup = context.AddGroup(
NotificationsCommonNotificationNames.GroupName,
L("Notifications:Primitives"),
false);
allowSubscriptionToClients: false);
commonGroup.AddNotification(
name: NotificationsCommonNotificationNames.ExceptionHandling,
@ -33,7 +33,7 @@ public class NotificationsCommonNotificationDefinitionProvider : NotificationDef
var tenantsGroup = context.AddGroup(
TenantNotificationNames.GroupName,
L("Notifications:MultiTenancy"),
false);
allowSubscriptionToClients: false);
tenantsGroup.AddNotification(
TenantNotificationNames.NewTenantRegistered,

1
aspnet-core/modules/common/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/INotificationDefinitionContext.cs

@ -8,6 +8,7 @@ namespace LINGYUN.Abp.Notifications
NotificationGroupDefinition AddGroup(
[NotNull] string name,
ILocalizableString displayName = null,
ILocalizableString description = null,
bool allowSubscriptionToClients = true);
NotificationGroupDefinition GetGroupOrNull(string name);

5
aspnet-core/modules/common/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/NotificationDefinition.cs

@ -64,6 +64,11 @@ namespace LINGYUN.Abp.Notifications
[NotNull]
public Dictionary<string, object> Properties { get; }
public object this[string name] {
get => Properties.GetOrDefault(name);
set => Properties[name] = value;
}
public NotificationDefinition(
string name,
ILocalizableString displayName = null,

3
aspnet-core/modules/common/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/NotificationDefinitionContext.cs

@ -17,6 +17,7 @@ namespace LINGYUN.Abp.Notifications
public NotificationGroupDefinition AddGroup(
[NotNull] string name,
ILocalizableString displayName = null,
ILocalizableString description = null,
bool allowSubscriptionToClients = true)
{
Check.NotNull(name, nameof(name));
@ -26,7 +27,7 @@ namespace LINGYUN.Abp.Notifications
throw new AbpException($"There is already an existing notification group with name: {name}");
}
return Groups[name] = new NotificationGroupDefinition(name, displayName, allowSubscriptionToClients);
return Groups[name] = new NotificationGroupDefinition(name, displayName, description, allowSubscriptionToClients);
}
public NotificationGroupDefinition GetGroupOrNull(string name)

12
aspnet-core/modules/common/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/NotificationGroupDefinition.cs

@ -32,23 +32,31 @@ namespace LINGYUN.Abp.Notifications
public IReadOnlyList<NotificationDefinition> Notifications => _notifications.ToImmutableList();
private readonly List<NotificationDefinition> _notifications;
public Dictionary<string, object> Properties { get; }
public Dictionary<string, object> Properties { get; }
public object this[string name] {
get => Properties.GetOrDefault(name);
set => Properties[name] = value;
}
public static NotificationGroupDefinition Create(
string name,
ILocalizableString displayName = null,
ILocalizableString description = null,
bool allowSubscriptionToClients = false)
{
return new NotificationGroupDefinition(name, displayName, allowSubscriptionToClients);
return new NotificationGroupDefinition(name, displayName, description, allowSubscriptionToClients);
}
protected internal NotificationGroupDefinition(
string name,
ILocalizableString displayName = null,
ILocalizableString description = null,
bool allowSubscriptionToClients = false)
{
Name = name;
DisplayName = displayName ?? new FixedLocalizableString(Name);
Description = description ?? DisplayName;
AllowSubscriptionToClients = allowSubscriptionToClients;
_notifications = new List<NotificationDefinition>();

24
aspnet-core/modules/notifications/LINGYUN.Abp.Notifications.Domain/LINGYUN/Abp/Notifications/AbpNotificationsManagementOptions.cs

@ -1,12 +1,34 @@
namespace LINGYUN.Abp.Notifications;
using System;
namespace LINGYUN.Abp.Notifications;
public class AbpNotificationsManagementOptions
{
public bool SaveStaticNotificationsToDatabase { get; set; }
public bool IsDynamicNotificationsStoreEnabled { get; set; }
/// <summary>
/// 缓存刷新时间
/// default: 30 seconds
/// </summary>
public TimeSpan NotificationsCacheRefreshInterval { get; set; }
/// <summary>
/// 申请时间戳超时时间
/// default: 2 minutes
/// </summary>
public TimeSpan NotificationsCacheStampTimeOut { get; set; }
/// <summary>
/// 时间戳过期时间
/// default: 30 days
/// </summary>
public TimeSpan NotificationsCacheStampExpiration { get; set; }
public AbpNotificationsManagementOptions()
{
SaveStaticNotificationsToDatabase = true;
IsDynamicNotificationsStoreEnabled = true;
NotificationsCacheRefreshInterval = TimeSpan.FromSeconds(30);
NotificationsCacheStampTimeOut = TimeSpan.FromMinutes(2);
// 30天过期重新刷新缓存
NotificationsCacheStampExpiration = TimeSpan.FromDays(30);
}
}

326
aspnet-core/modules/notifications/LINGYUN.Abp.Notifications.Domain/LINGYUN/Abp/Notifications/DynamicNotificationDefinitionCache.cs

@ -1,326 +0,0 @@
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Localization;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Localization;
using Volo.Abp.Threading;
namespace LINGYUN.Abp.Notifications;
public class DynamicNotificationDefinitionCache : IDynamicNotificationDefinitionCache, ISingletonDependency
{
private readonly static ConcurrentDictionary<string, NotificationGroupDefinition> _dynamicNotificationGroupL1Cache;
private readonly static ConcurrentDictionary<string, NotificationDefinition> _dynamicNotificationsL1Cache;
private readonly static SemaphoreSlim _l2CacheSyncLock;
protected IMemoryCache DynamicNotificationL2Cache { get; }
protected IDistributedCache<NotificationDefinitionGroupsCacheItem> DynamicNotificationGroupL3Cache { get; }
protected IDistributedCache<NotificationDefinitionsCacheItem> DynamicNotificationsL3Cache { get; }
protected INotificationDefinitionGroupRecordRepository NotificationDefinitionGroupRecordRepository { get; }
protected INotificationDefinitionRecordRepository NotificationDefinitionRecordRepository { get; }
protected ILocalizableStringSerializer LocalizableStringSerializer { get; }
protected IStringLocalizerFactory StringLocalizerFactory { get; }
static DynamicNotificationDefinitionCache()
{
_l2CacheSyncLock = new SemaphoreSlim(1, 1);
_dynamicNotificationsL1Cache = new ConcurrentDictionary<string, NotificationDefinition>();
_dynamicNotificationGroupL1Cache = new ConcurrentDictionary<string, NotificationGroupDefinition>();
}
public DynamicNotificationDefinitionCache(
IMemoryCache dynamicNotificationL2Cache,
IDistributedCache<NotificationDefinitionGroupsCacheItem> dynamicNotificationGroupL3Cache,
IDistributedCache<NotificationDefinitionsCacheItem> dynamicNotificationsL3Cache,
INotificationDefinitionGroupRecordRepository notificationDefinitionGroupRecordRepository,
INotificationDefinitionRecordRepository notificationDefinitionRecordRepository,
ILocalizableStringSerializer localizableStringSerializer,
IStringLocalizerFactory stringLocalizerFactory)
{
DynamicNotificationL2Cache = dynamicNotificationL2Cache;
DynamicNotificationGroupL3Cache = dynamicNotificationGroupL3Cache;
DynamicNotificationsL3Cache = dynamicNotificationsL3Cache;
NotificationDefinitionGroupRecordRepository = notificationDefinitionGroupRecordRepository;
NotificationDefinitionRecordRepository = notificationDefinitionRecordRepository;
LocalizableStringSerializer = localizableStringSerializer;
StringLocalizerFactory = stringLocalizerFactory;
}
public async virtual Task<IReadOnlyList<NotificationGroupDefinition>> GetGroupsAsync()
{
var cacheKey = NotificationDefinitionGroupsCacheItem.CalculateCacheKey(CultureInfo.CurrentCulture.Name);
if (!_dynamicNotificationGroupL1Cache.Any())
{
var l2GroupCache = await GetGroupsFormL2CacheAsync(cacheKey);
var notifications = await GetNotificationsFormL2CacheAsync(
NotificationDefinitionsCacheItem.CalculateCacheKey(CultureInfo.CurrentCulture.Name));
foreach (var group in l2GroupCache.Groups)
{
var groupGroup = NotificationGroupDefinition
.Create(
group.Name,
LocalizableStringSerializer.Deserialize(group.DisplayName),
group.AllowSubscriptionToClients);
var notificationsInThisGroup = notifications.Notifications
.Where(p => p.GroupName == groupGroup.Name);
foreach (var notification in notificationsInThisGroup)
{
var notificationDefine = groupGroup.AddNotification(
notification.Name,
LocalizableStringSerializer.Deserialize(notification.DisplayName),
LocalizableStringSerializer.Deserialize(notification.Description),
notification.NotificationType,
notification.Lifetime,
notification.ContentType,
notification.AllowSubscriptionToClients);
notificationDefine.Properties.AddIfNotContains(notification.Properties);
}
_dynamicNotificationGroupL1Cache.TryAdd(group.Name, groupGroup);
}
//NotificationGroupDefinition
// .Create(g.Name, new FixedLocalizableString(g.DisplayName), g.AllowSubscriptionToClients)
}
return _dynamicNotificationGroupL1Cache.Values.ToImmutableList();
}
public async virtual Task<IReadOnlyList<NotificationDefinition>> GetNotificationsAsync()
{
var cacheKey = NotificationDefinitionsCacheItem.CalculateCacheKey(CultureInfo.CurrentCulture.Name);
if (!_dynamicNotificationsL1Cache.Any())
{
var l2cache = await GetNotificationsFormL2CacheAsync(cacheKey);
foreach (var notification in l2cache.Notifications)
{
var notificationDefinition = new NotificationDefinition(
notification.Name,
LocalizableStringSerializer.Deserialize(notification.DisplayName),
LocalizableStringSerializer.Deserialize(notification.Description),
notification.NotificationType,
notification.Lifetime,
notification.ContentType,
notification.AllowSubscriptionToClients);
notificationDefinition.WithProviders(notification.Providers.ToArray());
notificationDefinition.Properties.AddIfNotContains(notification.Properties);
_dynamicNotificationsL1Cache.TryAdd(notification.Name, notificationDefinition);
}
}
return _dynamicNotificationsL1Cache.Values.ToImmutableList();
}
public async virtual Task<NotificationDefinition> GetOrNullAsync(string name)
{
if (_dynamicNotificationsL1Cache.Any())
{
return _dynamicNotificationsL1Cache.GetOrDefault(name);
}
var notifications = await GetNotificationsAsync();
return notifications.FirstOrDefault(n => n.Name.Equals(name));
}
protected async virtual Task<NotificationDefinitionGroupsCacheItem> GetGroupsFormL2CacheAsync(string cacheKey)
{
var cacheItem = DynamicNotificationL2Cache.Get<NotificationDefinitionGroupsCacheItem>(cacheKey);
if (cacheItem == null)
{
using (await _l2CacheSyncLock.LockAsync())
{
var l2cache = await GetGroupsFormL3CacheAsync(cacheKey);
var options = new MemoryCacheEntryOptions();
options.SetAbsoluteExpiration(TimeSpan.FromMinutes(5));
options.SetPriority(CacheItemPriority.High);
options.RegisterPostEvictionCallback((key, value, reason, state) =>
{
_dynamicNotificationGroupL1Cache.Clear();
});
DynamicNotificationL2Cache.Set(cacheKey, l2cache, options);
return l2cache;
}
}
return cacheItem;
}
protected async virtual Task<NotificationDefinitionGroupsCacheItem> GetGroupsFormL3CacheAsync(string cacheKey)
{
var cacheItem = await DynamicNotificationGroupL3Cache.GetAsync(cacheKey);
if (cacheItem == null)
{
var records = await GetGroupsFormRepositoryAsync();
var recordCaches = new List<NotificationDefinitionGroupCacheItem>();
foreach (var record in records)
{
var displayName = record.DisplayName;
if (!displayName.IsNullOrWhiteSpace())
{
displayName = await LocalizableStringSerializer
.Deserialize(displayName)
.LocalizeAsync(StringLocalizerFactory);
}
var description = record.Description;
if (!description.IsNullOrWhiteSpace())
{
description = await LocalizableStringSerializer
.Deserialize(description)
.LocalizeAsync(StringLocalizerFactory);
}
recordCaches.Add(new NotificationDefinitionGroupCacheItem(
record.Name,
displayName,
description,
record.AllowSubscriptionToClients));
}
cacheItem = new NotificationDefinitionGroupsCacheItem(recordCaches.ToArray());
await DynamicNotificationGroupL3Cache.SetAsync(
cacheKey,
cacheItem,
new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(2),
SlidingExpiration = TimeSpan.FromHours(1),
});
}
return cacheItem;
}
protected async virtual Task<List<NotificationDefinitionGroupRecord>> GetGroupsFormRepositoryAsync()
{
var records = await NotificationDefinitionGroupRecordRepository.GetListAsync();
return records;
}
protected async virtual Task<NotificationDefinitionsCacheItem> GetNotificationsFormL2CacheAsync(string cacheKey)
{
var cacheItem = DynamicNotificationL2Cache.Get<NotificationDefinitionsCacheItem>(cacheKey);
if (cacheItem == null)
{
using (await _l2CacheSyncLock.LockAsync())
{
var l2cache = await GetNotificationsFormL3CacheAsync(cacheKey);
var options = new MemoryCacheEntryOptions();
options.SetAbsoluteExpiration(TimeSpan.FromMinutes(5));
options.SetPriority(CacheItemPriority.High);
options.RegisterPostEvictionCallback((key, value, reason, state) =>
{
_dynamicNotificationsL1Cache.Clear();
});
DynamicNotificationL2Cache.Set(cacheKey, l2cache, options);
return l2cache;
}
}
return cacheItem;
}
protected async virtual Task<NotificationDefinitionsCacheItem> GetNotificationsFormL3CacheAsync(string cacheKey)
{
var cacheItem = await DynamicNotificationsL3Cache.GetAsync(cacheKey);
if (cacheItem == null)
{
var records = await GetNotificationsFormRepositoryAsync();
var recordCaches = new List<NotificationDefinitionCacheItem>();
foreach (var record in records)
{
var displayName = record.DisplayName;
if (!displayName.IsNullOrWhiteSpace())
{
displayName = await LocalizableStringSerializer
.Deserialize(displayName)
.LocalizeAsync(StringLocalizerFactory);
}
var description = record.Description;
if (!description.IsNullOrWhiteSpace())
{
description = await LocalizableStringSerializer
.Deserialize(description)
.LocalizeAsync(StringLocalizerFactory);
}
var providers = new List<string>();
if (!record.Providers.IsNullOrWhiteSpace())
{
providers = record.Providers.Split(';').ToList();
}
var recordCache = new NotificationDefinitionCacheItem(
record.Name,
record.GroupName,
displayName,
description,
record.NotificationLifetime,
record.NotificationType,
record.ContentType,
providers,
record.AllowSubscriptionToClients);
recordCache.Properties.AddIfNotContains(record.ExtraProperties);
recordCaches.Add(recordCache);
}
cacheItem = new NotificationDefinitionsCacheItem(recordCaches.ToArray());
await DynamicNotificationsL3Cache.SetAsync(
cacheKey,
cacheItem,
new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(2),
SlidingExpiration = TimeSpan.FromHours(1),
});
}
return cacheItem;
}
protected async virtual Task<List<NotificationDefinitionRecord>> GetNotificationsFormRepositoryAsync()
{
var records = await NotificationDefinitionRecordRepository.GetListAsync();
return records;
}
}

113
aspnet-core/modules/notifications/LINGYUN.Abp.Notifications.Domain/LINGYUN/Abp/Notifications/DynamicNotificationDefinitionInMemoryCache.cs

@ -0,0 +1,113 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Localization;
namespace LINGYUN.Abp.Notifications;
[ExposeServices(
typeof(IDynamicNotificationDefinitionStoreCache),
typeof(DynamicNotificationDefinitionInMemoryCache))]
public class DynamicNotificationDefinitionInMemoryCache : IDynamicNotificationDefinitionStoreCache, ISingletonDependency
{
public string CacheStamp { get; set; }
protected IDictionary<string, NotificationGroupDefinition> NotificationGroupDefinitions { get; }
protected IDictionary<string, NotificationDefinition> NotificationDefinitions { get; }
protected ILocalizableStringSerializer LocalizableStringSerializer { get; }
public SemaphoreSlim SyncSemaphore { get; } = new(1, 1);
public DateTime? LastCheckTime { get; set; }
public DynamicNotificationDefinitionInMemoryCache(
ILocalizableStringSerializer localizableStringSerializer)
{
LocalizableStringSerializer = localizableStringSerializer;
NotificationGroupDefinitions = new Dictionary<string, NotificationGroupDefinition>();
NotificationDefinitions = new Dictionary<string, NotificationDefinition>();
}
public Task FillAsync(
List<NotificationDefinitionGroupRecord> notificationGroupRecords,
List<NotificationDefinitionRecord> notificationRecords)
{
NotificationGroupDefinitions.Clear();
NotificationDefinitions.Clear();
var context = new NotificationDefinitionContext();
foreach (var notificationGroupRecord in notificationGroupRecords)
{
var notificationGroup = context.AddGroup(
notificationGroupRecord.Name,
LocalizableStringSerializer.Deserialize(notificationGroupRecord.DisplayName),
LocalizableStringSerializer.Deserialize(notificationGroupRecord.Description),
notificationGroupRecord.AllowSubscriptionToClients
);
NotificationGroupDefinitions[notificationGroup.Name] = notificationGroup;
foreach (var property in notificationGroupRecord.ExtraProperties)
{
notificationGroup[property.Key] = property.Value;
}
var notificationRecordsInThisGroup = notificationRecords
.Where(p => p.GroupName == notificationGroup.Name);
foreach (var notificationRecord in notificationRecordsInThisGroup)
{
AddNotification(notificationGroup, notificationRecord);
}
}
return Task.CompletedTask;
}
public NotificationDefinition GetNotificationOrNull(string name)
{
return NotificationDefinitions.GetOrDefault(name);
}
public IReadOnlyList<NotificationDefinition> GetNotifications()
{
return NotificationDefinitions.Values.ToList();
}
public IReadOnlyList<NotificationGroupDefinition> GetGroups()
{
return NotificationGroupDefinitions.Values.ToList();
}
private void AddNotification(
NotificationGroupDefinition notificationGroup,
NotificationDefinitionRecord notificationRecord)
{
var notification = notificationGroup.AddNotification(
notificationRecord.Name,
LocalizableStringSerializer.Deserialize(notificationRecord.DisplayName),
LocalizableStringSerializer.Deserialize(notificationRecord.Description),
notificationRecord.NotificationType,
notificationRecord.NotificationLifetime,
notificationRecord.ContentType,
notificationRecord.AllowSubscriptionToClients
);
NotificationDefinitions[notification.Name] = notification;
if (!notificationRecord.Providers.IsNullOrWhiteSpace())
{
notification.Providers.AddRange(notificationRecord.Providers.Split(',', ';'));
}
foreach (var property in notificationRecord.ExtraProperties)
{
notification[property.Key] = property.Value;
}
}
}

156
aspnet-core/modules/notifications/LINGYUN.Abp.Notifications.Domain/LINGYUN/Abp/Notifications/DynamicNotificationDefinitionStore.cs

@ -1,49 +1,171 @@
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.DistributedLocking;
using Volo.Abp.Threading;
namespace LINGYUN.Abp.Notifications;
[Dependency(ReplaceServices = true)]
public class DynamicNotificationDefinitionStore : IDynamicNotificationDefinitionStore, ITransientDependency
{
private readonly AbpNotificationsManagementOptions _notificationsManagementOptions;
private readonly IDynamicNotificationDefinitionCache _dynamicNotificationDefinitionCache;
protected INotificationDefinitionGroupRecordRepository NotificationGroupRepository { get; }
protected INotificationDefinitionRecordRepository NotificationRepository { get; }
protected INotificationDefinitionSerializer NotificationDefinitionSerializer { get; }
protected IDynamicNotificationDefinitionStoreCache StoreCache { get; }
protected IDistributedCache DistributedCache { get; }
protected IAbpDistributedLock DistributedLock { get; }
protected AbpNotificationsManagementOptions NotificationManagementOptions { get; }
protected AbpDistributedCacheOptions CacheOptions { get; }
public DynamicNotificationDefinitionStore(
IDynamicNotificationDefinitionCache dynamicNotificationDefinitionCache,
IOptions<AbpNotificationsManagementOptions> notificationsManagementOptions)
INotificationDefinitionGroupRecordRepository notificationGroupRepository,
INotificationDefinitionRecordRepository notificationRepository,
INotificationDefinitionSerializer notificationDefinitionSerializer,
IDynamicNotificationDefinitionStoreCache storeCache,
IDistributedCache distributedCache,
IOptions<AbpDistributedCacheOptions> cacheOptions,
IOptions<AbpNotificationsManagementOptions> notificationManagementOptions,
IAbpDistributedLock distributedLock)
{
_dynamicNotificationDefinitionCache = dynamicNotificationDefinitionCache;
_notificationsManagementOptions = notificationsManagementOptions.Value;
NotificationGroupRepository = notificationGroupRepository;
NotificationRepository = notificationRepository;
NotificationDefinitionSerializer = notificationDefinitionSerializer;
StoreCache = storeCache;
DistributedCache = distributedCache;
DistributedLock = distributedLock;
NotificationManagementOptions = notificationManagementOptions.Value;
CacheOptions = cacheOptions.Value;
}
public async virtual Task<IReadOnlyList<NotificationGroupDefinition>> GetGroupsAsync()
public async virtual Task<NotificationDefinition> GetOrNullAsync(string name)
{
if (!_notificationsManagementOptions.IsDynamicNotificationsStoreEnabled)
if (!NotificationManagementOptions.IsDynamicNotificationsStoreEnabled)
{
return Array.Empty<NotificationGroupDefinition>();
return null;
}
using (await StoreCache.SyncSemaphore.LockAsync())
{
await EnsureCacheIsUptoDateAsync();
return StoreCache.GetNotificationOrNull(name);
}
return await _dynamicNotificationDefinitionCache.GetGroupsAsync();
}
public async virtual Task<IReadOnlyList<NotificationDefinition>> GetNotificationsAsync()
{
if (!_notificationsManagementOptions.IsDynamicNotificationsStoreEnabled)
if (!NotificationManagementOptions.IsDynamicNotificationsStoreEnabled)
{
return Array.Empty<NotificationDefinition>();
}
return await _dynamicNotificationDefinitionCache.GetNotificationsAsync();
using (await StoreCache.SyncSemaphore.LockAsync())
{
await EnsureCacheIsUptoDateAsync();
return StoreCache.GetNotifications().ToImmutableList();
}
}
public async virtual Task<NotificationDefinition> GetOrNullAsync(string name)
public async virtual Task<IReadOnlyList<NotificationGroupDefinition>> GetGroupsAsync()
{
if (!_notificationsManagementOptions.IsDynamicNotificationsStoreEnabled)
if (!NotificationManagementOptions.IsDynamicNotificationsStoreEnabled)
{
return null;
return Array.Empty<NotificationGroupDefinition>();
}
using (await StoreCache.SyncSemaphore.LockAsync())
{
await EnsureCacheIsUptoDateAsync();
return StoreCache.GetGroups().ToImmutableList();
}
}
protected async virtual Task EnsureCacheIsUptoDateAsync()
{
if (StoreCache.LastCheckTime.HasValue &&
DateTime.Now.Subtract(StoreCache.LastCheckTime.Value) < NotificationManagementOptions.NotificationsCacheRefreshInterval)
{
/* We get the latest Notification with a small delay for optimization */
return;
}
var stampInDistributedCache = await GetOrSetStampInDistributedCache();
if (stampInDistributedCache == StoreCache.CacheStamp)
{
StoreCache.LastCheckTime = DateTime.Now;
return;
}
return await _dynamicNotificationDefinitionCache.GetOrNullAsync(name);
await UpdateInMemoryStoreCache();
StoreCache.CacheStamp = stampInDistributedCache;
StoreCache.LastCheckTime = DateTime.Now;
}
protected async virtual Task UpdateInMemoryStoreCache()
{
var NotificationGroupRecords = await NotificationGroupRepository.GetListAsync();
var NotificationRecords = await NotificationRepository.GetListAsync();
await StoreCache.FillAsync(NotificationGroupRecords, NotificationRecords);
}
protected async virtual Task<string> GetOrSetStampInDistributedCache()
{
var cacheKey = GetCommonStampCacheKey();
var stampInDistributedCache = await DistributedCache.GetStringAsync(cacheKey);
if (stampInDistributedCache != null)
{
return stampInDistributedCache;
}
await using (var commonLockHandle = await DistributedLock
.TryAcquireAsync(GetCommonDistributedLockKey(), NotificationManagementOptions.NotificationsCacheStampTimeOut))
{
if (commonLockHandle == null)
{
/* This request will fail */
throw new AbpException(
"Could not acquire distributed lock for Notification definition common stamp check!"
);
}
stampInDistributedCache = await DistributedCache.GetStringAsync(cacheKey);
if (stampInDistributedCache != null)
{
return stampInDistributedCache;
}
stampInDistributedCache = Guid.NewGuid().ToString();
await DistributedCache.SetStringAsync(
cacheKey,
stampInDistributedCache,
new DistributedCacheEntryOptions
{
SlidingExpiration = NotificationManagementOptions.NotificationsCacheStampExpiration
}
);
}
return stampInDistributedCache;
}
protected virtual string GetCommonStampCacheKey()
{
return $"{CacheOptions.KeyPrefix}_AbpInMemoryNotificationCacheStamp";
}
protected virtual string GetCommonDistributedLockKey()
{
return $"{CacheOptions.KeyPrefix}_Common_AbpNotificationUpdateLock";
}
}

13
aspnet-core/modules/notifications/LINGYUN.Abp.Notifications.Domain/LINGYUN/Abp/Notifications/IDynamicNotificationDefinitionCache.cs

@ -1,13 +0,0 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace LINGYUN.Abp.Notifications;
public interface IDynamicNotificationDefinitionCache
{
Task<NotificationDefinition> GetOrNullAsync(string name);
Task<IReadOnlyList<NotificationDefinition>> GetNotificationsAsync();
Task<IReadOnlyList<NotificationGroupDefinition>> GetGroupsAsync();
}

25
aspnet-core/modules/notifications/LINGYUN.Abp.Notifications.Domain/LINGYUN/Abp/Notifications/IDynamicNotificationDefinitionStoreCache.cs

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace LINGYUN.Abp.Notifications;
public interface IDynamicNotificationDefinitionStoreCache
{
string CacheStamp { get; set; }
SemaphoreSlim SyncSemaphore { get; }
DateTime? LastCheckTime { get; set; }
Task FillAsync(
List<NotificationDefinitionGroupRecord> webhookGroupRecords,
List<NotificationDefinitionRecord> webhookRecords);
NotificationDefinition GetNotificationOrNull(string name);
IReadOnlyList<NotificationDefinition> GetNotifications();
IReadOnlyList<NotificationGroupDefinition> GetGroups();
}
Loading…
Cancel
Save