Browse Source

Merge pull request #24487 from abpframework/24465

Refactor static definition stores and add dynamic initializers
pull/24494/head
SALİH ÖZKARA 1 month ago
committed by GitHub
parent
commit
153c44bec4
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 9
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/StaticPermissionDefinitionChangedEvent.cs
  2. 90
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/StaticPermissionDefinitionStore.cs
  3. 3
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/InternalServiceCollectionExtensions.cs
  4. 11
      framework/src/Volo.Abp.Core/Volo/Abp/StaticDefinitions/IStaticDefinitionCache.cs
  5. 30
      framework/src/Volo.Abp.Core/Volo/Abp/StaticDefinitions/StaticDefinitionCache.cs
  6. 9
      framework/src/Volo.Abp.Features/Volo/Abp/Features/StaticFeatureDefinitionChangedEvent.cs
  7. 112
      framework/src/Volo.Abp.Features/Volo/Abp/Features/StaticFeatureDefinitionStore.cs
  8. 9
      framework/src/Volo.Abp.Settings/Volo/Abp/Settings/StaticSettingDefinitionChangedEvent.cs
  9. 34
      framework/src/Volo.Abp.Settings/Volo/Abp/Settings/StaticSettingDefinitionStore.cs
  10. 9
      framework/src/Volo.Abp.TextTemplating.Core/Volo/Abp/TextTemplating/StaticTemplateDefinitionChangedEvent.cs
  11. 40
      framework/src/Volo.Abp.TextTemplating.Core/Volo/Abp/TextTemplating/StaticTemplateDefinitionStore.cs
  12. 122
      framework/test/Volo.Abp.Core.Tests/Volo/Abp/StaticDefinitions/StaticDefinitionCache_Tests.cs
  13. 4
      modules/cms-kit/test/Volo.CmsKit.MongoDB.Tests/MongoDB/CmsKitMongoDbTestModule.cs
  14. 1
      modules/cms-kit/test/Volo.CmsKit.MongoDB.Tests/Volo.CmsKit.MongoDB.Tests.csproj
  15. 128
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/AbpFeatureManagementDomainModule.cs
  16. 157
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureDynamicInitializer.cs
  17. 36
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/StaticFeatureDefinitionChangedEventHandler.cs
  18. 123
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/AbpPermissionManagementDomainModule.cs
  19. 159
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDynamicInitializer.cs
  20. 36
      modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/StaticPermissionDefinitionChangedEventHandler.cs
  21. 115
      modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/AbpSettingManagementDomainModule.cs
  22. 158
      modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/SettingDynamicInitializer.cs
  23. 32
      modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/StaticSettingDefinitionChangedEventHandler.cs

9
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/StaticPermissionDefinitionChangedEvent.cs

@ -0,0 +1,9 @@
using System;
namespace Volo.Abp.Authorization.Permissions;
[Serializable]
public class StaticPermissionDefinitionChangedEvent
{
}

90
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/StaticPermissionDefinitionStore.cs

@ -6,44 +6,58 @@ using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.StaticDefinitions;
namespace Volo.Abp.Authorization.Permissions;
public class StaticPermissionDefinitionStore : IStaticPermissionDefinitionStore, ISingletonDependency
{
protected IDictionary<string, PermissionGroupDefinition> PermissionGroupDefinitions => _lazyPermissionGroupDefinitions.Value;
private readonly Lazy<Dictionary<string, PermissionGroupDefinition>> _lazyPermissionGroupDefinitions;
protected IDictionary<string, PermissionDefinition> PermissionDefinitions => _lazyPermissionDefinitions.Value;
private readonly Lazy<Dictionary<string, PermissionDefinition>> _lazyPermissionDefinitions;
protected IServiceProvider ServiceProvider { get; }
protected AbpPermissionOptions Options { get; }
private readonly IServiceProvider _serviceProvider;
protected IStaticDefinitionCache<PermissionGroupDefinition, Dictionary<string, PermissionGroupDefinition>> GroupCache { get; }
protected IStaticDefinitionCache<PermissionDefinition, Dictionary<string, PermissionDefinition>> DefinitionCache { get; }
public StaticPermissionDefinitionStore(
IServiceProvider serviceProvider,
IOptions<AbpPermissionOptions> options)
IOptions<AbpPermissionOptions> options,
IStaticDefinitionCache<PermissionGroupDefinition, Dictionary<string, PermissionGroupDefinition>> groupCache,
IStaticDefinitionCache<PermissionDefinition, Dictionary<string, PermissionDefinition>> definitionCache)
{
_serviceProvider = serviceProvider;
ServiceProvider = serviceProvider;
Options = options.Value;
GroupCache = groupCache;
DefinitionCache = definitionCache;
}
_lazyPermissionDefinitions = new Lazy<Dictionary<string, PermissionDefinition>>(
CreatePermissionDefinitions,
isThreadSafe: true
);
public async Task<PermissionDefinition?> GetOrNullAsync(string name)
{
var defs = await GetPermissionDefinitionsAsync();
return defs.GetOrDefault(name);
}
_lazyPermissionGroupDefinitions = new Lazy<Dictionary<string, PermissionGroupDefinition>>(
CreatePermissionGroupDefinitions,
isThreadSafe: true
);
public virtual async Task<IReadOnlyList<PermissionDefinition>> GetPermissionsAsync()
{
var defs = await GetPermissionDefinitionsAsync();
return defs.Values.ToImmutableList();
}
public async Task<IReadOnlyList<PermissionGroupDefinition>> GetGroupsAsync()
{
var groups = await GetPermissionGroupDefinitionsAsync();
return groups.Values.ToImmutableList();
}
protected virtual async Task<Dictionary<string, PermissionDefinition>> GetPermissionDefinitionsAsync()
{
return await DefinitionCache.GetOrCreateAsync(CreatePermissionDefinitionsAsync);
}
protected virtual Dictionary<string, PermissionDefinition> CreatePermissionDefinitions()
protected virtual async Task<Dictionary<string, PermissionDefinition>> CreatePermissionDefinitionsAsync()
{
var permissions = new Dictionary<string, PermissionDefinition>();
foreach (var groupDefinition in PermissionGroupDefinitions.Values)
var groups = await GetPermissionGroupDefinitionsAsync();
foreach (var groupDefinition in groups.Values)
{
foreach (var permission in groupDefinition.Permissions)
{
@ -71,9 +85,14 @@ public class StaticPermissionDefinitionStore : IStaticPermissionDefinitionStore,
}
}
protected virtual Dictionary<string, PermissionGroupDefinition> CreatePermissionGroupDefinitions()
protected virtual async Task<Dictionary<string, PermissionGroupDefinition>> GetPermissionGroupDefinitionsAsync()
{
return await GroupCache.GetOrCreateAsync(CreatePermissionGroupDefinitionsAsync);
}
protected virtual Task<Dictionary<string, PermissionGroupDefinition>> CreatePermissionGroupDefinitionsAsync()
{
using (var scope = _serviceProvider.CreateScope())
using (var scope = ServiceProvider.CreateScope())
{
var context = new PermissionDefinitionContext(scope.ServiceProvider);
@ -99,29 +118,10 @@ public class StaticPermissionDefinitionStore : IStaticPermissionDefinitionStore,
context.CurrentProvider = provider;
provider.PostDefine(context);
}
context.CurrentProvider = null;
return context.Groups;
return Task.FromResult(context.Groups);
}
}
public Task<PermissionDefinition?> GetOrNullAsync(string name)
{
return Task.FromResult(PermissionDefinitions.GetOrDefault(name));
}
public virtual Task<IReadOnlyList<PermissionDefinition>> GetPermissionsAsync()
{
return Task.FromResult<IReadOnlyList<PermissionDefinition>>(
PermissionDefinitions.Values.ToImmutableList()
);
}
public Task<IReadOnlyList<PermissionGroupDefinition>> GetGroupsAsync()
{
return Task.FromResult<IReadOnlyList<PermissionGroupDefinition>>(
PermissionGroupDefinitions.Values.ToImmutableList()
);
}
}
}

3
framework/src/Volo.Abp.Core/Volo/Abp/Internal/InternalServiceCollectionExtensions.cs

@ -5,6 +5,7 @@ using Volo.Abp.Logging;
using Volo.Abp.Modularity;
using Volo.Abp.Reflection;
using Volo.Abp.SimpleStateChecking;
using Volo.Abp.StaticDefinitions;
namespace Volo.Abp.Internal;
@ -42,7 +43,7 @@ internal static class InternalServiceCollectionExtensions
services.AddAssemblyOf<IAbpApplication>();
services.AddTransient(typeof(ISimpleStateCheckerManager<>), typeof(SimpleStateCheckerManager<>));
services.AddSingleton(typeof(IStaticDefinitionCache<,>), typeof(StaticDefinitionCache<,>));
services.Configure<AbpModuleLifecycleOptions>(options =>
{
options.Contributors.Add<OnPreApplicationInitializationModuleLifecycleContributor>();

11
framework/src/Volo.Abp.Core/Volo/Abp/StaticDefinitions/IStaticDefinitionCache.cs

@ -0,0 +1,11 @@
using System;
using System.Threading.Tasks;
namespace Volo.Abp.StaticDefinitions;
public interface IStaticDefinitionCache<TKey, TValue>
{
Task<TValue> GetOrCreateAsync(Func<Task<TValue>> factory);
Task ClearAsync();
}

30
framework/src/Volo.Abp.Core/Volo/Abp/StaticDefinitions/StaticDefinitionCache.cs

@ -0,0 +1,30 @@
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Volo.Abp.StaticDefinitions;
public class StaticDefinitionCache<TKey, TValue> : IStaticDefinitionCache<TKey, TValue>
{
private Lazy<Task<TValue>>? _lazy;
public virtual async Task<TValue> GetOrCreateAsync(Func<Task<TValue>> factory)
{
var lazy = _lazy;
if (lazy != null)
{
return await lazy.Value;
}
var newLazy = new Lazy<Task<TValue>>(factory, LazyThreadSafetyMode.ExecutionAndPublication);
lazy = Interlocked.CompareExchange(ref _lazy, newLazy, null) ?? newLazy;
return await lazy.Value;
}
public virtual Task ClearAsync()
{
Interlocked.Exchange(ref _lazy, null);
return Task.CompletedTask;
}
}

9
framework/src/Volo.Abp.Features/Volo/Abp/Features/StaticFeatureDefinitionChangedEvent.cs

@ -0,0 +1,9 @@
using System;
namespace Volo.Abp.Features;
[Serializable]
public class StaticFeatureDefinitionChangedEvent
{
}

112
framework/src/Volo.Abp.Features/Volo/Abp/Features/StaticFeatureDefinitionStore.cs

@ -5,37 +5,27 @@ using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.StaticDefinitions;
namespace Volo.Abp.Features;
public class StaticFeatureDefinitionStore: IStaticFeatureDefinitionStore, ISingletonDependency
{
protected IDictionary<string, FeatureGroupDefinition> FeatureGroupDefinitions => _lazyFeatureGroupDefinitions.Value;
private readonly Lazy<Dictionary<string, FeatureGroupDefinition>> _lazyFeatureGroupDefinitions;
protected IDictionary<string, FeatureDefinition> FeatureDefinitions => _lazyFeatureDefinitions.Value;
private readonly Lazy<Dictionary<string, FeatureDefinition>> _lazyFeatureDefinitions;
protected IServiceProvider ServiceProvider { get; }
protected AbpFeatureOptions Options { get; }
private readonly IServiceProvider _serviceProvider;
protected IStaticDefinitionCache<FeatureGroupDefinition, Dictionary<string, FeatureGroupDefinition>> GroupCache { get; }
protected IStaticDefinitionCache<FeatureDefinition, Dictionary<string, FeatureDefinition>> DefinitionCache { get; }
public StaticFeatureDefinitionStore(
IServiceProvider serviceProvider,
IOptions<AbpFeatureOptions> options,
IServiceProvider serviceProvider)
IStaticDefinitionCache<FeatureGroupDefinition, Dictionary<string, FeatureGroupDefinition>> groupCache,
IStaticDefinitionCache<FeatureDefinition, Dictionary<string, FeatureDefinition>> definitionCache)
{
_serviceProvider = serviceProvider;
ServiceProvider = serviceProvider;
Options = options.Value;
_lazyFeatureDefinitions = new Lazy<Dictionary<string, FeatureDefinition>>(
CreateFeatureDefinitions,
isThreadSafe: true
);
_lazyFeatureGroupDefinitions = new Lazy<Dictionary<string, FeatureGroupDefinition>>(
CreateFeatureGroupDefinitions,
isThreadSafe: true
);
GroupCache = groupCache;
DefinitionCache = definitionCache;
}
public virtual async Task<FeatureDefinition> GetAsync(string name)
@ -52,43 +42,39 @@ public class StaticFeatureDefinitionStore: IStaticFeatureDefinitionStore, ISingl
return feature;
}
protected virtual Dictionary<string, FeatureDefinition> CreateFeatureDefinitions()
public virtual async Task<FeatureDefinition?> GetOrNullAsync(string name)
{
var features = new Dictionary<string, FeatureDefinition>();
foreach (var groupDefinition in FeatureGroupDefinitions.Values)
{
foreach (var feature in groupDefinition.Features)
{
AddFeatureToDictionaryRecursively(features, feature);
}
}
var defs = await GetFeatureDefinitionsAsync();
return defs.GetOrDefault(name);
}
return features;
public virtual async Task<IReadOnlyList<FeatureDefinition>> GetFeaturesAsync()
{
var defs = await GetFeatureDefinitionsAsync();
return defs.Values.ToList();
}
protected virtual void AddFeatureToDictionaryRecursively(
Dictionary<string, FeatureDefinition> features,
FeatureDefinition feature)
public virtual async Task<IReadOnlyList<FeatureGroupDefinition>> GetGroupsAsync()
{
if (features.ContainsKey(feature.Name))
{
throw new AbpException("Duplicate feature name: " + feature.Name);
}
var groups = await GetFeatureGroupDefinitionsAsync();
return groups.Values.ToList();
}
features[feature.Name] = feature;
protected virtual async Task<Dictionary<string, FeatureGroupDefinition>> GetFeatureGroupDefinitionsAsync()
{
return await GroupCache.GetOrCreateAsync(CreateFeatureGroupDefinitionsAsync);
}
foreach (var child in feature.Children)
{
AddFeatureToDictionaryRecursively(features, child);
}
protected virtual async Task<Dictionary<string, FeatureDefinition>> GetFeatureDefinitionsAsync()
{
return await DefinitionCache.GetOrCreateAsync(CreateFeatureDefinitionsAsync);
}
protected virtual Dictionary<string, FeatureGroupDefinition> CreateFeatureGroupDefinitions()
protected virtual Task<Dictionary<string, FeatureGroupDefinition>> CreateFeatureGroupDefinitionsAsync()
{
var context = new FeatureDefinitionContext();
using (var scope = _serviceProvider.CreateScope())
using (var scope = ServiceProvider.CreateScope())
{
var providers = Options
.DefinitionProviders
@ -101,21 +87,39 @@ public class StaticFeatureDefinitionStore: IStaticFeatureDefinitionStore, ISingl
}
}
return context.Groups;
return Task.FromResult(context.Groups);
}
public virtual Task<FeatureDefinition?> GetOrNullAsync(string name)
protected virtual async Task<Dictionary<string, FeatureDefinition>> CreateFeatureDefinitionsAsync()
{
return Task.FromResult(FeatureDefinitions.GetOrDefault(name));
}
var features = new Dictionary<string, FeatureDefinition>();
public virtual Task<IReadOnlyList<FeatureDefinition>> GetFeaturesAsync()
{
return Task.FromResult<IReadOnlyList<FeatureDefinition>>(FeatureDefinitions.Values.ToList());
var groups = await GetFeatureGroupDefinitionsAsync();
foreach (var groupDefinition in groups.Values)
{
foreach (var feature in groupDefinition.Features)
{
AddFeatureToDictionaryRecursively(features, feature);
}
}
return features;
}
public virtual Task<IReadOnlyList<FeatureGroupDefinition>> GetGroupsAsync()
protected virtual void AddFeatureToDictionaryRecursively(
Dictionary<string, FeatureDefinition> features,
FeatureDefinition feature)
{
return Task.FromResult<IReadOnlyList<FeatureGroupDefinition>>(FeatureGroupDefinitions.Values.ToList());
if (features.ContainsKey(feature.Name))
{
throw new AbpException("Duplicate feature name: " + feature.Name);
}
features[feature.Name] = feature;
foreach (var child in feature.Children)
{
AddFeatureToDictionaryRecursively(features, child);
}
}
}

9
framework/src/Volo.Abp.Settings/Volo/Abp/Settings/StaticSettingDefinitionChangedEvent.cs

@ -0,0 +1,9 @@
using System;
namespace Volo.Abp.Settings;
[Serializable]
public class StaticSettingDefinitionChangedEvent
{
}

34
framework/src/Volo.Abp.Settings/Volo/Abp/Settings/StaticSettingDefinitionStore.cs

@ -6,23 +6,24 @@ using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.StaticDefinitions;
namespace Volo.Abp.Settings;
public class StaticSettingDefinitionStore : IStaticSettingDefinitionStore, ISingletonDependency
{
protected Lazy<IDictionary<string, SettingDefinition>> SettingDefinitions { get; }
protected AbpSettingOptions Options { get; }
protected IServiceProvider ServiceProvider { get; }
protected AbpSettingOptions Options { get; }
protected IStaticDefinitionCache<SettingDefinition, Dictionary<string, SettingDefinition>> DefinitionCache { get; }
public StaticSettingDefinitionStore(IOptions<AbpSettingOptions> options, IServiceProvider serviceProvider)
public StaticSettingDefinitionStore(
IServiceProvider serviceProvider,
IOptions<AbpSettingOptions> options,
IStaticDefinitionCache<SettingDefinition, Dictionary<string, SettingDefinition>> definitionCache)
{
ServiceProvider = serviceProvider;
Options = options.Value;
SettingDefinitions = new Lazy<IDictionary<string, SettingDefinition>>(CreateSettingDefinitions, true);
DefinitionCache = definitionCache;
}
public virtual async Task<SettingDefinition> GetAsync(string name)
@ -39,17 +40,24 @@ public class StaticSettingDefinitionStore : IStaticSettingDefinitionStore, ISing
return setting;
}
public virtual Task<IReadOnlyList<SettingDefinition>> GetAllAsync()
public virtual async Task<IReadOnlyList<SettingDefinition>> GetAllAsync()
{
var defs = await GetSettingDefinitionsAsync();
return defs.Values.ToImmutableList();
}
public virtual async Task<SettingDefinition?> GetOrNullAsync(string name)
{
return Task.FromResult<IReadOnlyList<SettingDefinition>>(SettingDefinitions.Value.Values.ToImmutableList());
var defs = await GetSettingDefinitionsAsync();
return defs.GetOrDefault(name);
}
public virtual Task<SettingDefinition?> GetOrNullAsync(string name)
protected virtual async Task<Dictionary<string, SettingDefinition>> GetSettingDefinitionsAsync()
{
return Task.FromResult(SettingDefinitions.Value.GetOrDefault(name));
return await DefinitionCache.GetOrCreateAsync(CreateSettingDefinitionsAsync);
}
protected virtual IDictionary<string, SettingDefinition> CreateSettingDefinitions()
protected virtual Task<Dictionary<string, SettingDefinition>> CreateSettingDefinitionsAsync()
{
var settings = new Dictionary<string, SettingDefinition>();
@ -66,6 +74,6 @@ public class StaticSettingDefinitionStore : IStaticSettingDefinitionStore, ISing
}
}
return settings;
return Task.FromResult(settings);
}
}

9
framework/src/Volo.Abp.TextTemplating.Core/Volo/Abp/TextTemplating/StaticTemplateDefinitionChangedEvent.cs

@ -0,0 +1,9 @@
using System;
namespace Volo.Abp.TextTemplating;
[Serializable]
public class StaticTemplateDefinitionChangedEvent
{
}

40
framework/src/Volo.Abp.TextTemplating.Core/Volo/Abp/TextTemplating/StaticTemplateDefinitionStore.cs

@ -6,50 +6,58 @@ using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.StaticDefinitions;
namespace Volo.Abp.TextTemplating;
public class StaticTemplateDefinitionStore : IStaticTemplateDefinitionStore, ISingletonDependency
{
protected Lazy<IDictionary<string, TemplateDefinition>> TemplateDefinitions { get; }
protected AbpTextTemplatingOptions Options { get; }
protected IServiceProvider ServiceProvider { get; }
protected AbpTextTemplatingOptions Options { get; }
protected IStaticDefinitionCache<TemplateDefinition, Dictionary<string, TemplateDefinition>> DefinitionCache { get; }
public StaticTemplateDefinitionStore(IOptions<AbpTextTemplatingOptions> options, IServiceProvider serviceProvider)
public StaticTemplateDefinitionStore(
IServiceProvider serviceProvider,
IOptions<AbpTextTemplatingOptions> options,
IStaticDefinitionCache<TemplateDefinition, Dictionary<string, TemplateDefinition>> definitionCache)
{
ServiceProvider = serviceProvider;
Options = options.Value;
TemplateDefinitions = new Lazy<IDictionary<string, TemplateDefinition>>(CreateTextTemplateDefinitions, true);
DefinitionCache = definitionCache;
}
public virtual Task<TemplateDefinition> GetAsync(string name)
public virtual async Task<TemplateDefinition> GetAsync(string name)
{
Check.NotNull(name, nameof(name));
var template = GetOrNullAsync(name);
var template = await GetOrNullAsync(name);
if (template == null)
{
throw new AbpException("Undefined template: " + name);
}
return template!;
return template;
}
public virtual async Task<IReadOnlyList<TemplateDefinition>> GetAllAsync()
{
var defs = await GetTemplateDefinitionsAsync();
return defs.Values.ToImmutableList();
}
public virtual Task<IReadOnlyList<TemplateDefinition>> GetAllAsync()
public virtual async Task<TemplateDefinition?> GetOrNullAsync(string name)
{
return Task.FromResult<IReadOnlyList<TemplateDefinition>>(TemplateDefinitions.Value.Values.ToImmutableList());
var defs = await GetTemplateDefinitionsAsync();
return defs.GetOrDefault(name);
}
public virtual Task<TemplateDefinition?> GetOrNullAsync(string name)
protected virtual async Task<Dictionary<string, TemplateDefinition>> GetTemplateDefinitionsAsync()
{
return Task.FromResult(TemplateDefinitions.Value.GetOrDefault(name));
return await DefinitionCache.GetOrCreateAsync(CreateTextTemplateDefinitionsAsync);
}
protected virtual IDictionary<string, TemplateDefinition> CreateTextTemplateDefinitions()
protected virtual Task<Dictionary<string, TemplateDefinition>> CreateTextTemplateDefinitionsAsync()
{
var templates = new Dictionary<string, TemplateDefinition>();
@ -78,6 +86,6 @@ public class StaticTemplateDefinitionStore : IStaticTemplateDefinitionStore, ISi
}
}
return templates;
return Task.FromResult(templates);
}
}

122
framework/test/Volo.Abp.Core.Tests/Volo/Abp/StaticDefinitions/StaticDefinitionCache_Tests.cs

@ -0,0 +1,122 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Testing;
using Xunit;
namespace Volo.Abp.StaticDefinitions;
public class StaticDefinitionCache_Tests : AbpIntegratedTest<AbpTestModule>
{
protected readonly IStaticDefinitionCache<StaticDefinition1, List<StaticDefinition1>> _staticDefinitionCache1;
protected readonly IStaticDefinitionCache<StaticDefinition2, List<StaticDefinition2>> _staticDefinitionCache2;
public StaticDefinitionCache_Tests()
{
_staticDefinitionCache1 = GetRequiredService<IStaticDefinitionCache<StaticDefinition1, List<StaticDefinition1>>>();
_staticDefinitionCache2 = GetRequiredService<IStaticDefinitionCache<StaticDefinition2, List<StaticDefinition2>>>();
}
[Fact]
public async Task GetOrCreate_Test()
{
var definition1 = new StaticDefinition1 { Name = "Definition1", Value = 1 };
var definition2 = new StaticDefinition1 { Name = "Definition2", Value = 2 };
var definitionsFirstRetrieval = await _staticDefinitionCache1.GetOrCreateAsync(() =>
{
return Task.FromResult(new List<StaticDefinition1> { definition1, definition2 });
});
var definitionsSecondRetrieval = await _staticDefinitionCache1.GetOrCreateAsync(() =>
{
throw new AbpException("Factory should not be called on second retrieval");
});
definitionsFirstRetrieval.ShouldBe(definitionsSecondRetrieval);
definitionsSecondRetrieval.Count.ShouldBe(2);
definitionsSecondRetrieval[0].Name.ShouldBe("Definition1");
definitionsSecondRetrieval[0].Value.ShouldBe(1);
definitionsSecondRetrieval[1].Name.ShouldBe("Definition2");
definitionsSecondRetrieval[1].Value.ShouldBe(2);
}
[Fact]
public async Task Separate_Caches_For_Different_Types_Test()
{
var definitions1 = await _staticDefinitionCache1.GetOrCreateAsync(() =>
{
return Task.FromResult(new List<StaticDefinition1> { new StaticDefinition1 {Name = "Definition1", Value = 1} });
});
var definitions2 = await _staticDefinitionCache2.GetOrCreateAsync(() =>
{
return Task.FromResult(new List<StaticDefinition2> { new StaticDefinition2 {Name = "DefinitionA", Value = 100} });
});
definitions1.Count.ShouldBe(1);
definitions1[0].Name.ShouldBe("Definition1");
definitions1[0].Value.ShouldBe(1);
definitions2.Count.ShouldBe(1);
definitions2[0].Name.ShouldBe("DefinitionA");
definitions2[0].Value.ShouldBe(100);
}
[Fact]
public async Task Clear_Test()
{
var definitions1 = await _staticDefinitionCache1.GetOrCreateAsync(() =>
{
return Task.FromResult(new List<StaticDefinition1> { new StaticDefinition1 {Name = "Definition1", Value = 1} });
});
var definitions2 = await _staticDefinitionCache2.GetOrCreateAsync(() =>
{
return Task.FromResult(new List<StaticDefinition2> { new StaticDefinition2 {Name = "DefinitionA", Value = 100} });
});
definitions1.Count.ShouldBe(1);
definitions1[0].Name.ShouldBe("Definition1");
definitions1[0].Value.ShouldBe(1);
definitions2.Count.ShouldBe(1);
definitions2[0].Name.ShouldBe("DefinitionA");
definitions2[0].Value.ShouldBe(100);
await _staticDefinitionCache1.ClearAsync();
await _staticDefinitionCache2.ClearAsync();
var definitions1AfterClear = await _staticDefinitionCache1.GetOrCreateAsync(() =>
{
return Task.FromResult(new List<StaticDefinition1> { new StaticDefinition1 {Name = "DefinitionNew", Value = 10} });
});
var definitions2AfterClear = await _staticDefinitionCache2.GetOrCreateAsync(() =>
{
return Task.FromResult(new List<StaticDefinition2> {new StaticDefinition2 {Name = "DefinitionNewA", Value = 200}});
});
definitions1AfterClear.Count.ShouldBe(1);
definitions1AfterClear[0].Name.ShouldBe("DefinitionNew");
definitions1AfterClear[0].Value.ShouldBe(10);
definitions2AfterClear.Count.ShouldBe(1);
definitions2AfterClear[0].Name.ShouldBe("DefinitionNewA");
definitions2AfterClear[0].Value.ShouldBe(200);
}
public class StaticDefinition1
{
public string Name { get; set; }
public int Value { get; set; }
}
public class StaticDefinition2
{
public string Name { get; set; }
public int Value { get; set; }
}
}

4
modules/cms-kit/test/Volo.CmsKit.MongoDB.Tests/MongoDB/CmsKitMongoDbTestModule.cs

@ -1,13 +1,15 @@
using System;
using Volo.Abp.Data;
using Volo.Abp.Modularity;
using Volo.Abp.SettingManagement.MongoDB;
using Volo.Abp.Uow;
namespace Volo.CmsKit.MongoDB;
[DependsOn(
typeof(CmsKitTestBaseModule),
typeof(CmsKitMongoDbModule)
typeof(CmsKitMongoDbModule),
typeof(AbpSettingManagementMongoDbModule)
)]
public class CmsKitMongoDbTestModule : AbpModule
{

1
modules/cms-kit/test/Volo.CmsKit.MongoDB.Tests/Volo.CmsKit.MongoDB.Tests.csproj

@ -11,6 +11,7 @@
<PackageReference Include="MongoSandbox8.runtime.linux-x64" Condition="$([MSBuild]::IsOSPlatform('Linux'))" />
<PackageReference Include="MongoSandbox8.runtime.osx-arm64" Condition="$([MSBuild]::IsOSPlatform('OSX'))" />
<PackageReference Include="MongoSandbox8.runtime.win-x64" Condition="$([MSBuild]::IsOSPlatform('Windows'))" />
<ProjectReference Include="..\..\..\..\modules\setting-management\src\Volo.Abp.SettingManagement.MongoDB/Volo.Abp.SettingManagement.MongoDB.csproj" />
<ProjectReference Include="..\..\src\Volo.CmsKit.MongoDB\Volo.CmsKit.MongoDB.csproj" />
<ProjectReference Include="..\Volo.CmsKit.TestBase\Volo.CmsKit.TestBase.csproj" />
</ItemGroup>

128
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/AbpFeatureManagementDomainModule.cs

@ -24,6 +24,9 @@ namespace Volo.Abp.FeatureManagement;
)]
public class AbpFeatureManagementDomainModule : AbpModule
{
private readonly CancellationTokenSource _cancellationTokenSource = new();
private Task _initializeDynamicFeaturesTask;
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<FeatureManagementOptions>(options =>
@ -51,18 +54,17 @@ public class AbpFeatureManagementDomainModule : AbpModule
}
}
private readonly CancellationTokenSource _cancellationTokenSource = new();
private Task _initializeDynamicFeaturesTask;
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
AsyncHelper.RunSync(() => OnApplicationInitializationAsync(context));
}
public override Task OnApplicationInitializationAsync(ApplicationInitializationContext context)
public override async Task OnApplicationInitializationAsync(ApplicationInitializationContext context)
{
InitializeDynamicFeatures(context);
return Task.CompletedTask;
var rootServiceProvider = context.ServiceProvider.GetRequiredService<IRootServiceProvider>();
var initializer = rootServiceProvider.GetRequiredService<FeatureDynamicInitializer>();
await initializer.InitializeAsync(true, _cancellationTokenSource.Token);
_initializeDynamicFeaturesTask = initializer.GetInitializationTask();
}
public override Task OnApplicationShutdownAsync(ApplicationShutdownContext context)
@ -75,118 +77,4 @@ public class AbpFeatureManagementDomainModule : AbpModule
{
return _initializeDynamicFeaturesTask ?? Task.CompletedTask;
}
private void InitializeDynamicFeatures(ApplicationInitializationContext context)
{
var options = context
.ServiceProvider
.GetRequiredService<IOptions<FeatureManagementOptions>>()
.Value;
if (!options.SaveStaticFeaturesToDatabase && !options.IsDynamicFeatureStoreEnabled)
{
return;
}
var rootServiceProvider = context.ServiceProvider.GetRequiredService<IRootServiceProvider>();
_initializeDynamicFeaturesTask = Task.Run(async () =>
{
using var scope = rootServiceProvider.CreateScope();
var applicationLifetime = scope.ServiceProvider.GetService<IHostApplicationLifetime>();
var cancellationTokenProvider = scope.ServiceProvider.GetRequiredService<ICancellationTokenProvider>();
var cancellationToken = applicationLifetime?.ApplicationStopping ?? _cancellationTokenSource.Token;
try
{
using (cancellationTokenProvider.Use(cancellationToken))
{
if (cancellationTokenProvider.Token.IsCancellationRequested)
{
return;
}
await SaveStaticFeaturesToDatabaseAsync(options, scope, cancellationTokenProvider);
if (cancellationTokenProvider.Token.IsCancellationRequested)
{
return;
}
await PreCacheDynamicFeaturesAsync(options, scope);
}
}
// ReSharper disable once EmptyGeneralCatchClause (No need to log since it is logged above)
catch { }
});
}
private static async Task SaveStaticFeaturesToDatabaseAsync(
FeatureManagementOptions options,
IServiceScope scope,
ICancellationTokenProvider cancellationTokenProvider)
{
if (!options.SaveStaticFeaturesToDatabase)
{
return;
}
await Policy
.Handle<Exception>()
.WaitAndRetryAsync(
8,
retryAttempt => TimeSpan.FromSeconds(
RandomHelper.GetRandom(
(int)Math.Pow(2, retryAttempt) * 8,
(int)Math.Pow(2, retryAttempt) * 12)
)
)
.ExecuteAsync(async _ =>
{
try
{
// ReSharper disable once AccessToDisposedClosure
await scope
.ServiceProvider
.GetRequiredService<IStaticFeatureSaver>()
.SaveAsync();
}
catch (Exception ex)
{
// ReSharper disable once AccessToDisposedClosure
scope.ServiceProvider
.GetService<ILogger<AbpFeatureManagementDomainModule>>()?
.LogException(ex);
throw; // Polly will catch it
}
}, cancellationTokenProvider.Token);
}
private static async Task PreCacheDynamicFeaturesAsync(FeatureManagementOptions options, IServiceScope scope)
{
if (!options.IsDynamicFeatureStoreEnabled)
{
return;
}
try
{
// Pre-cache features, so first request doesn't wait
await scope
.ServiceProvider
.GetRequiredService<IDynamicFeatureDefinitionStore>()
.GetGroupsAsync();
}
catch (Exception ex)
{
// ReSharper disable once AccessToDisposedClosure
scope
.ServiceProvider
.GetService<ILogger<AbpFeatureManagementDomainModule>>()?
.LogException(ex);
throw; // It will be cached in InitializeDynamicFeatures
}
}
}

157
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureDynamicInitializer.cs

@ -0,0 +1,157 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Polly;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Features;
using Volo.Abp.Threading;
namespace Volo.Abp.FeatureManagement;
public class FeatureDynamicInitializer : ITransientDependency
{
private Task _initializeDynamicFeaturesTask;
public ILogger<FeatureDynamicInitializer> Logger { get; set; }
protected IServiceProvider ServiceProvider { get; }
protected IOptions<FeatureManagementOptions> Options { get; }
[CanBeNull]
protected IHostApplicationLifetime ApplicationLifetime { get; }
protected ICancellationTokenProvider CancellationTokenProvider { get; }
protected IDynamicFeatureDefinitionStore DynamicFeatureDefinitionStore { get; }
protected IStaticFeatureSaver StaticFeatureSaver { get; }
public FeatureDynamicInitializer(
IServiceProvider serviceProvider,
IOptions<FeatureManagementOptions> options,
ICancellationTokenProvider cancellationTokenProvider,
IDynamicFeatureDefinitionStore dynamicFeatureDefinitionStore,
IStaticFeatureSaver staticFeatureSaver)
{
Logger = NullLogger<FeatureDynamicInitializer>.Instance;
ServiceProvider = serviceProvider;
Options = options;
ApplicationLifetime = ServiceProvider.GetService<IHostApplicationLifetime>();
CancellationTokenProvider = cancellationTokenProvider;
DynamicFeatureDefinitionStore = dynamicFeatureDefinitionStore;
StaticFeatureSaver = staticFeatureSaver;
}
public virtual Task InitializeAsync(bool runInBackground, CancellationToken cancellationToken = default)
{
var options = Options.Value;
if (!options.SaveStaticFeaturesToDatabase && !options.IsDynamicFeatureStoreEnabled)
{
return Task.CompletedTask;
}
if (runInBackground)
{
_initializeDynamicFeaturesTask = Task.Run(async () =>
{
if (cancellationToken == default && ApplicationLifetime?.ApplicationStopping != null)
{
cancellationToken = ApplicationLifetime.ApplicationStopping;
}
await ExecuteInitializationAsync(options, cancellationToken);
}, cancellationToken);
return Task.CompletedTask;
}
_initializeDynamicFeaturesTask = ExecuteInitializationAsync(options, cancellationToken);
return _initializeDynamicFeaturesTask;
}
public virtual Task GetInitializationTask()
{
return _initializeDynamicFeaturesTask ?? Task.CompletedTask;
}
protected virtual async Task ExecuteInitializationAsync(FeatureManagementOptions options, CancellationToken cancellationToken)
{
try
{
using (CancellationTokenProvider.Use(cancellationToken))
{
if (CancellationTokenProvider.Token.IsCancellationRequested)
{
return;
}
await SaveStaticFeaturesToDatabaseAsync(options, cancellationToken);
if (CancellationTokenProvider.Token.IsCancellationRequested)
{
return;
}
await PreCacheDynamicFeaturesAsync(options);
}
}
catch
{
// No need to log here since inner calls log
}
}
protected virtual async Task SaveStaticFeaturesToDatabaseAsync(
FeatureManagementOptions options,
CancellationToken cancellationToken)
{
if (!options.SaveStaticFeaturesToDatabase)
{
return;
}
await Policy
.Handle<Exception>()
.WaitAndRetryAsync(
8,
retryAttempt => TimeSpan.FromSeconds(
Volo.Abp.RandomHelper.GetRandom(
(int)Math.Pow(2, retryAttempt) * 8,
(int)Math.Pow(2, retryAttempt) * 12)
)
)
.ExecuteAsync(async _ =>
{
try
{
await StaticFeatureSaver.SaveAsync();
}
catch (Exception ex)
{
Logger.LogException(ex);
throw; // Polly will catch it
}
}, cancellationToken);
}
protected virtual async Task PreCacheDynamicFeaturesAsync(FeatureManagementOptions options)
{
if (!options.IsDynamicFeatureStoreEnabled)
{
return;
}
try
{
// Pre-cache features, so first request doesn't wait
await DynamicFeatureDefinitionStore.GetGroupsAsync();
}
catch (Exception ex)
{
Logger.LogException(ex);
throw; // It will be cached in Initialize()
}
}
}

36
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/StaticFeatureDefinitionChangedEventHandler.cs

@ -0,0 +1,36 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus;
using Volo.Abp.Features;
using Volo.Abp.StaticDefinitions;
using Volo.Abp.Threading;
namespace Volo.Abp.FeatureManagement;
public class StaticFeatureDefinitionChangedEventHandler : ILocalEventHandler<StaticFeatureDefinitionChangedEvent>, ITransientDependency
{
protected IStaticDefinitionCache<FeatureGroupDefinition, Dictionary<string, FeatureGroupDefinition>> GroupCache { get; }
protected IStaticDefinitionCache<FeatureDefinition, Dictionary<string, FeatureDefinition>> DefinitionCache { get; }
protected FeatureDynamicInitializer FeatureDynamicInitializer { get; }
protected ICancellationTokenProvider CancellationTokenProvider { get; }
public StaticFeatureDefinitionChangedEventHandler(
IStaticDefinitionCache<FeatureGroupDefinition, Dictionary<string, FeatureGroupDefinition>> groupCache,
IStaticDefinitionCache<FeatureDefinition, Dictionary<string, FeatureDefinition>> definitionCache,
FeatureDynamicInitializer featureDynamicInitializer,
ICancellationTokenProvider cancellationTokenProvider)
{
GroupCache = groupCache;
DefinitionCache = definitionCache;
FeatureDynamicInitializer = featureDynamicInitializer;
CancellationTokenProvider = cancellationTokenProvider;
}
public virtual async Task HandleEventAsync(StaticFeatureDefinitionChangedEvent eventData)
{
await GroupCache.ClearAsync();
await DefinitionCache.ClearAsync();
await FeatureDynamicInitializer.InitializeAsync(false, CancellationTokenProvider.Token);
}
}

123
modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/AbpPermissionManagementDomainModule.cs

@ -27,6 +27,7 @@ public class AbpPermissionManagementDomainModule : AbpModule
{
private readonly CancellationTokenSource _cancellationTokenSource = new();
private Task _initializeDynamicPermissionsTask;
public override void ConfigureServices(ServiceConfigurationContext context)
{
if (context.Services.IsDataMigrationEnvironment())
@ -44,10 +45,12 @@ public class AbpPermissionManagementDomainModule : AbpModule
AsyncHelper.RunSync(() => OnApplicationInitializationAsync(context));
}
public override Task OnApplicationInitializationAsync(ApplicationInitializationContext context)
public override async Task OnApplicationInitializationAsync(ApplicationInitializationContext context)
{
InitializeDynamicPermissions(context);
return Task.CompletedTask;
var rootServiceProvider = context.ServiceProvider.GetRequiredService<IRootServiceProvider>();
var initializer = rootServiceProvider.GetRequiredService<PermissionDynamicInitializer>();
await initializer.InitializeAsync(true, _cancellationTokenSource.Token);
_initializeDynamicPermissionsTask = initializer.GetInitializationTask();
}
public override Task OnApplicationShutdownAsync(ApplicationShutdownContext context)
@ -60,118 +63,4 @@ public class AbpPermissionManagementDomainModule : AbpModule
{
return _initializeDynamicPermissionsTask ?? Task.CompletedTask;
}
private void InitializeDynamicPermissions(ApplicationInitializationContext context)
{
var options = context
.ServiceProvider
.GetRequiredService<IOptions<PermissionManagementOptions>>()
.Value;
if (!options.SaveStaticPermissionsToDatabase && !options.IsDynamicPermissionStoreEnabled)
{
return;
}
var rootServiceProvider = context.ServiceProvider.GetRequiredService<IRootServiceProvider>();
_initializeDynamicPermissionsTask = Task.Run(async () =>
{
using var scope = rootServiceProvider.CreateScope();
var applicationLifetime = scope.ServiceProvider.GetService<IHostApplicationLifetime>();
var cancellationTokenProvider = scope.ServiceProvider.GetRequiredService<ICancellationTokenProvider>();
var cancellationToken = applicationLifetime?.ApplicationStopping ?? _cancellationTokenSource.Token;
try
{
using (cancellationTokenProvider.Use(cancellationToken))
{
if (cancellationTokenProvider.Token.IsCancellationRequested)
{
return;
}
await SaveStaticPermissionsToDatabaseAsync(options, scope, cancellationTokenProvider);
if (cancellationTokenProvider.Token.IsCancellationRequested)
{
return;
}
await PreCacheDynamicPermissionsAsync(options, scope);
}
}
// ReSharper disable once EmptyGeneralCatchClause (No need to log since it is logged above)
catch { }
});
}
private async static Task SaveStaticPermissionsToDatabaseAsync(
PermissionManagementOptions options,
IServiceScope scope,
ICancellationTokenProvider cancellationTokenProvider)
{
if (!options.SaveStaticPermissionsToDatabase)
{
return;
}
await Policy
.Handle<Exception>()
.WaitAndRetryAsync(
8,
retryAttempt => TimeSpan.FromSeconds(
RandomHelper.GetRandom(
(int)Math.Pow(2, retryAttempt) * 8,
(int)Math.Pow(2, retryAttempt) * 12)
)
)
.ExecuteAsync(async _ =>
{
try
{
// ReSharper disable once AccessToDisposedClosure
await scope
.ServiceProvider
.GetRequiredService<IStaticPermissionSaver>()
.SaveAsync();
}
catch (Exception ex)
{
// ReSharper disable once AccessToDisposedClosure
scope.ServiceProvider
.GetService<ILogger<AbpPermissionManagementDomainModule>>()?
.LogException(ex);
throw; // Polly will catch it
}
}, cancellationTokenProvider.Token);
}
private async static Task PreCacheDynamicPermissionsAsync(PermissionManagementOptions options, IServiceScope scope)
{
if (!options.IsDynamicPermissionStoreEnabled)
{
return;
}
try
{
// Pre-cache permissions, so first request doesn't wait
await scope
.ServiceProvider
.GetRequiredService<IDynamicPermissionDefinitionStore>()
.GetGroupsAsync();
}
catch (Exception ex)
{
// ReSharper disable once AccessToDisposedClosure
scope
.ServiceProvider
.GetService<ILogger<AbpPermissionManagementDomainModule>>()?
.LogException(ex);
throw; // It will be cached in InitializeDynamicPermissions
}
}
}

159
modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDynamicInitializer.cs

@ -0,0 +1,159 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Polly;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Threading;
namespace Volo.Abp.PermissionManagement;
public class PermissionDynamicInitializer : ITransientDependency
{
private Task _initializeDynamicPermissionsTask;
public ILogger<PermissionDynamicInitializer> Logger { get; set; }
protected IServiceProvider ServiceProvider { get; }
protected IOptions<PermissionManagementOptions> Options { get; }
[CanBeNull]
protected IHostApplicationLifetime ApplicationLifetime { get; }
protected ICancellationTokenProvider CancellationTokenProvider { get; }
protected IDynamicPermissionDefinitionStore DynamicPermissionDefinitionStore { get; }
protected IStaticPermissionSaver StaticPermissionSaver { get; }
public PermissionDynamicInitializer(
IServiceProvider serviceProvider,
IOptions<PermissionManagementOptions> options,
ICancellationTokenProvider cancellationTokenProvider,
IDynamicPermissionDefinitionStore dynamicPermissionDefinitionStore,
IStaticPermissionSaver staticPermissionSaver)
{
Logger = NullLogger<PermissionDynamicInitializer>.Instance;
ServiceProvider = serviceProvider;
Options = options;
ApplicationLifetime = ServiceProvider.GetService<IHostApplicationLifetime>();
CancellationTokenProvider = cancellationTokenProvider;
DynamicPermissionDefinitionStore = dynamicPermissionDefinitionStore;
StaticPermissionSaver = staticPermissionSaver;
}
public virtual Task InitializeAsync(bool runInBackground, CancellationToken cancellationToken = default)
{
var options = Options.Value;
if (!options.SaveStaticPermissionsToDatabase && !options.IsDynamicPermissionStoreEnabled)
{
return Task.CompletedTask;
}
if (runInBackground)
{
_initializeDynamicPermissionsTask = Task.Run(async () =>
{
if (cancellationToken == default && ApplicationLifetime?.ApplicationStopping != null)
{
cancellationToken = ApplicationLifetime.ApplicationStopping;
}
await ExecuteInitializationAsync(options, cancellationToken);
}, cancellationToken);
return Task.CompletedTask;
}
_initializeDynamicPermissionsTask = ExecuteInitializationAsync(options, cancellationToken);
return _initializeDynamicPermissionsTask;
}
public virtual Task GetInitializationTask()
{
return _initializeDynamicPermissionsTask ?? Task.CompletedTask;
}
protected virtual async Task ExecuteInitializationAsync(PermissionManagementOptions options, CancellationToken cancellationToken)
{
try
{
using (CancellationTokenProvider.Use(cancellationToken))
{
if (CancellationTokenProvider.Token.IsCancellationRequested)
{
return;
}
await SaveStaticPermissionsToDatabaseAsync(options, cancellationToken);
if (CancellationTokenProvider.Token.IsCancellationRequested)
{
return;
}
await PreCacheDynamicPermissionsAsync(options);
}
}
catch
{
// No need to log here since inner calls log
}
}
protected virtual async Task SaveStaticPermissionsToDatabaseAsync(
PermissionManagementOptions options,
CancellationToken cancellationToken)
{
if (!options.SaveStaticPermissionsToDatabase)
{
return;
}
await Policy
.Handle<Exception>()
.WaitAndRetryAsync(
8,
retryAttempt => TimeSpan.FromSeconds(
Volo.Abp.RandomHelper.GetRandom(
(int)Math.Pow(2, retryAttempt) * 8,
(int)Math.Pow(2, retryAttempt) * 12)
)
)
.ExecuteAsync(async _ =>
{
try
{
await StaticPermissionSaver.SaveAsync();
}
catch (Exception ex)
{
Logger.LogException(ex);
throw; // Polly will catch it
}
}, cancellationToken);
}
protected virtual async Task PreCacheDynamicPermissionsAsync(PermissionManagementOptions options)
{
if (!options.IsDynamicPermissionStoreEnabled)
{
return;
}
try
{
// Pre-cache permissions, so first request doesn't wait
await DynamicPermissionDefinitionStore.GetGroupsAsync();
}
catch (Exception ex)
{
Logger.LogException(ex);
throw; // It will be cached in Initialize()
}
}
}

36
modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/StaticPermissionDefinitionChangedEventHandler.cs

@ -0,0 +1,36 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.StaticDefinitions;
using Volo.Abp.Threading;
namespace Volo.Abp.PermissionManagement;
public class StaticPermissionDefinitionChangedEventHandler : ILocalEventHandler<StaticPermissionDefinitionChangedEvent>, ITransientDependency
{
protected IStaticDefinitionCache<PermissionGroupDefinition, Dictionary<string, PermissionGroupDefinition>> GroupCache { get; }
protected IStaticDefinitionCache<PermissionDefinition, Dictionary<string, PermissionDefinition>> DefinitionCache { get; }
protected PermissionDynamicInitializer PermissionDynamicInitializer { get; }
protected ICancellationTokenProvider CancellationTokenProvider { get; }
public StaticPermissionDefinitionChangedEventHandler(
IStaticDefinitionCache<PermissionGroupDefinition, Dictionary<string, PermissionGroupDefinition>> groupCache,
IStaticDefinitionCache<PermissionDefinition, Dictionary<string, PermissionDefinition>> definitionCache,
PermissionDynamicInitializer permissionDynamicInitializer,
ICancellationTokenProvider cancellationTokenProvider)
{
GroupCache = groupCache;
DefinitionCache = definitionCache;
PermissionDynamicInitializer = permissionDynamicInitializer;
CancellationTokenProvider = cancellationTokenProvider;
}
public virtual async Task HandleEventAsync(StaticPermissionDefinitionChangedEvent eventData)
{
await GroupCache.ClearAsync();
await DefinitionCache.ClearAsync();
await PermissionDynamicInitializer.InitializeAsync(false, CancellationTokenProvider.Token);
}
}

115
modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/AbpSettingManagementDomainModule.cs

@ -53,10 +53,12 @@ public class AbpSettingManagementDomainModule : AbpModule
AsyncHelper.RunSync(() => OnApplicationInitializationAsync(context));
}
public override Task OnApplicationInitializationAsync(ApplicationInitializationContext context)
public override async Task OnApplicationInitializationAsync(ApplicationInitializationContext context)
{
InitializeDynamicSettings(context);
return Task.CompletedTask;
var rootServiceProvider = context.ServiceProvider.GetRequiredService<IRootServiceProvider>();
var initializer = rootServiceProvider.GetRequiredService<SettingDynamicInitializer>();
await initializer.InitializeAsync(true, _cancellationTokenSource.Token);
_initializeDynamicSettingsTask = initializer.GetInitializationTask();
}
public override Task OnApplicationShutdownAsync(ApplicationShutdownContext context)
@ -69,111 +71,4 @@ public class AbpSettingManagementDomainModule : AbpModule
{
return _initializeDynamicSettingsTask ?? Task.CompletedTask;
}
private void InitializeDynamicSettings(ApplicationInitializationContext context)
{
var options = context
.ServiceProvider
.GetRequiredService<IOptions<SettingManagementOptions>>()
.Value;
if (!options.SaveStaticSettingsToDatabase && !options.IsDynamicSettingStoreEnabled)
{
return;
}
var rootServiceProvider = context.ServiceProvider.GetRequiredService<IRootServiceProvider>();
_initializeDynamicSettingsTask = Task.Run(async () =>
{
using var scope = rootServiceProvider.CreateScope();
var applicationLifetime = scope.ServiceProvider.GetService<IHostApplicationLifetime>();
var cancellationTokenProvider = scope.ServiceProvider.GetRequiredService<ICancellationTokenProvider>();
var cancellationToken = applicationLifetime?.ApplicationStopping ?? _cancellationTokenSource.Token;
try
{
using (cancellationTokenProvider.Use(cancellationToken))
{
if (cancellationTokenProvider.Token.IsCancellationRequested)
{
return;
}
await SaveStaticSettingsToDatabaseAsync(options, scope, cancellationTokenProvider);
if (cancellationTokenProvider.Token.IsCancellationRequested)
{
return;
}
await PreCacheDynamicSettingsAsync(options, scope);
}
}
// ReSharper disable once EmptyGeneralCatchClause (No need to log since it is logged above)
catch { }
});
}
private async static Task SaveStaticSettingsToDatabaseAsync(
SettingManagementOptions options,
IServiceScope scope,
ICancellationTokenProvider cancellationTokenProvider)
{
if (!options.SaveStaticSettingsToDatabase)
{
return;
}
await Policy
.Handle<Exception>()
.WaitAndRetryAsync(8, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt) * 10))
.ExecuteAsync(async _ =>
{
try
{
// ReSharper disable once AccessToDisposedClosure
await scope
.ServiceProvider
.GetRequiredService<IStaticSettingSaver>()
.SaveAsync();
}
catch (Exception ex)
{
// ReSharper disable once AccessToDisposedClosure
scope.ServiceProvider
.GetService<ILogger<AbpSettingManagementDomainModule>>()?
.LogException(ex);
throw; // Polly will catch it
}
}, cancellationTokenProvider.Token);
}
private async static Task PreCacheDynamicSettingsAsync(SettingManagementOptions options, IServiceScope scope)
{
if (!options.IsDynamicSettingStoreEnabled)
{
return;
}
try
{
// Pre-cache settings, so first request doesn't wait
await scope
.ServiceProvider
.GetRequiredService<IDynamicSettingDefinitionStore>()
.GetAllAsync();
}
catch (Exception ex)
{
// ReSharper disable once AccessToDisposedClosure
scope
.ServiceProvider
.GetService<ILogger<AbpSettingManagementDomainModule>>()?
.LogException(ex);
throw; // It will be cached in InitializeDynamicSettings
}
}
}

158
modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/SettingDynamicInitializer.cs

@ -0,0 +1,158 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Polly;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Settings;
using Volo.Abp.Threading;
namespace Volo.Abp.SettingManagement;
public class SettingDynamicInitializer : ITransientDependency
{
private Task _initializeDynamicSettingsTask;
public ILogger<SettingDynamicInitializer> Logger { get; set; }
protected IServiceProvider ServiceProvider { get; }
protected IOptions<SettingManagementOptions> Options { get; }
[CanBeNull]
protected IHostApplicationLifetime ApplicationLifetime { get; }
protected ICancellationTokenProvider CancellationTokenProvider { get; }
protected IDynamicSettingDefinitionStore DynamicSettingDefinitionStore { get; }
protected IStaticSettingSaver StaticSettingSaver { get; }
public SettingDynamicInitializer(
IServiceProvider serviceProvider,
IOptions<SettingManagementOptions> options,
ICancellationTokenProvider cancellationTokenProvider,
IDynamicSettingDefinitionStore dynamicSettingDefinitionStore,
IStaticSettingSaver staticSettingSaver)
{
Logger = NullLogger<SettingDynamicInitializer>.Instance;
ServiceProvider = serviceProvider;
Options = options;
ApplicationLifetime = ServiceProvider.GetService<IHostApplicationLifetime>();
CancellationTokenProvider = cancellationTokenProvider;
DynamicSettingDefinitionStore = dynamicSettingDefinitionStore;
StaticSettingSaver = staticSettingSaver;
}
public virtual Task InitializeAsync(bool runInBackground, CancellationToken cancellationToken = default)
{
var options = Options.Value;
if (!options.SaveStaticSettingsToDatabase && !options.IsDynamicSettingStoreEnabled)
{
return Task.CompletedTask;
}
if (runInBackground)
{
_initializeDynamicSettingsTask = Task.Run(async () =>
{
if (cancellationToken == default && ApplicationLifetime?.ApplicationStopping != null)
{
cancellationToken = ApplicationLifetime.ApplicationStopping;
}
await ExecuteInitializationAsync(options, cancellationToken);
}, cancellationToken);
return Task.CompletedTask;
}
_initializeDynamicSettingsTask = ExecuteInitializationAsync(options, cancellationToken);
return _initializeDynamicSettingsTask;
}
public virtual Task GetInitializationTask()
{
return _initializeDynamicSettingsTask ?? Task.CompletedTask;
}
protected virtual async Task ExecuteInitializationAsync(SettingManagementOptions options, CancellationToken cancellationToken)
{
try
{
using (CancellationTokenProvider.Use(cancellationToken))
{
if (CancellationTokenProvider.Token.IsCancellationRequested)
{
return;
}
await SaveStaticSettingsToDatabaseAsync(options, cancellationToken);
if (CancellationTokenProvider.Token.IsCancellationRequested)
{
return;
}
await PreCacheDynamicSettingsAsync(options);
}
}
catch
{
// No need to log here since inner calls log
}
}
protected virtual async Task SaveStaticSettingsToDatabaseAsync(
SettingManagementOptions options,
CancellationToken cancellationToken)
{
if (!options.SaveStaticSettingsToDatabase)
{
return;
}
await Policy
.Handle<Exception>()
.WaitAndRetryAsync(
8,
retryAttempt => TimeSpan.FromSeconds(
Volo.Abp.RandomHelper.GetRandom(
(int)Math.Pow(2, retryAttempt) * 8,
(int)Math.Pow(2, retryAttempt) * 12)
)
)
.ExecuteAsync(async _ =>
{
try
{
await StaticSettingSaver.SaveAsync();
}
catch (Exception ex)
{
Logger.LogException(ex);
throw; // Polly will catch it
}
}, cancellationToken);
}
protected virtual async Task PreCacheDynamicSettingsAsync(SettingManagementOptions options)
{
if (!options.IsDynamicSettingStoreEnabled)
{
return;
}
try
{
// Pre-cache settings, so first request doesn't wait
await DynamicSettingDefinitionStore.GetAllAsync();
}
catch (Exception ex)
{
Logger.LogException(ex);
throw; // It will be cached in Initialize()
}
}
}

32
modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/StaticSettingDefinitionChangedEventHandler.cs

@ -0,0 +1,32 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus;
using Volo.Abp.Settings;
using Volo.Abp.StaticDefinitions;
using Volo.Abp.Threading;
namespace Volo.Abp.SettingManagement;
public class StaticSettingDefinitionChangedEventHandler : ILocalEventHandler<StaticSettingDefinitionChangedEvent>, ITransientDependency
{
protected IStaticDefinitionCache<SettingDefinition, Dictionary<string, SettingDefinition>> DefinitionCache { get; }
protected SettingDynamicInitializer SettingDynamicInitializer { get; }
protected ICancellationTokenProvider CancellationTokenProvider { get; }
public StaticSettingDefinitionChangedEventHandler(
IStaticDefinitionCache<SettingDefinition, Dictionary<string, SettingDefinition>> definitionCache,
SettingDynamicInitializer settingDynamicInitializer,
ICancellationTokenProvider cancellationTokenProvider)
{
DefinitionCache = definitionCache;
SettingDynamicInitializer = settingDynamicInitializer;
CancellationTokenProvider = cancellationTokenProvider;
}
public virtual async Task HandleEventAsync(StaticSettingDefinitionChangedEvent eventData)
{
await DefinitionCache.ClearAsync();
await SettingDynamicInitializer.InitializeAsync(false, CancellationTokenProvider.Token);
}
}
Loading…
Cancel
Save