diff --git a/src/Volo.Abp.Settings/Volo/Abp/Settings/DefaultSettingContributor.cs b/src/Volo.Abp.Settings/Volo/Abp/Settings/DefaultSettingContributor.cs index 4fc8ea3d3f..66c1d50f5f 100644 --- a/src/Volo.Abp.Settings/Volo/Abp/Settings/DefaultSettingContributor.cs +++ b/src/Volo.Abp.Settings/Volo/Abp/Settings/DefaultSettingContributor.cs @@ -12,7 +12,7 @@ namespace Volo.Abp.Settings _settingStore = settingStore; } - public async Task GetOrNull(string name, string entityType, string entityId, bool fallback = true) + public async Task GetOrNullAsync(string name, string entityType, string entityId, bool fallback = true) { //TODO: Optimization: Get all settings and cache it! return await _settingStore.GetOrNullAsync(name, null, null); diff --git a/src/Volo.Abp.Settings/Volo/Abp/Settings/ISettingContributor.cs b/src/Volo.Abp.Settings/Volo/Abp/Settings/ISettingContributor.cs index 6bddcf4794..1fdaa206f3 100644 --- a/src/Volo.Abp.Settings/Volo/Abp/Settings/ISettingContributor.cs +++ b/src/Volo.Abp.Settings/Volo/Abp/Settings/ISettingContributor.cs @@ -4,6 +4,6 @@ namespace Volo.Abp.Settings { public interface ISettingContributor { - Task GetOrNull(string name, string entityType, string entityId, bool fallback = true); + Task GetOrNullAsync(string name, string entityType, string entityId, bool fallback = true); } } \ No newline at end of file diff --git a/src/Volo.Abp.Settings/Volo/Abp/Settings/ISettingDefinitionContext.cs b/src/Volo.Abp.Settings/Volo/Abp/Settings/ISettingDefinitionContext.cs new file mode 100644 index 0000000000..220e40fe59 --- /dev/null +++ b/src/Volo.Abp.Settings/Volo/Abp/Settings/ISettingDefinitionContext.cs @@ -0,0 +1,9 @@ +namespace Volo.Abp.Settings +{ + public interface ISettingDefinitionContext + { + SettingDefinition GetOrNull(string name); + + void Add(params SettingDefinition[] definitions); + } +} \ No newline at end of file diff --git a/src/Volo.Abp.Settings/Volo/Abp/Settings/ISettingDefinitionManager.cs b/src/Volo.Abp.Settings/Volo/Abp/Settings/ISettingDefinitionManager.cs new file mode 100644 index 0000000000..28ec16e185 --- /dev/null +++ b/src/Volo.Abp.Settings/Volo/Abp/Settings/ISettingDefinitionManager.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.Settings +{ + public interface ISettingDefinitionManager + { + SettingDefinition Get(string name); + } +} \ No newline at end of file diff --git a/src/Volo.Abp.Settings/Volo/Abp/Settings/ISettingProvider.cs b/src/Volo.Abp.Settings/Volo/Abp/Settings/ISettingProvider.cs new file mode 100644 index 0000000000..d3334b3b2a --- /dev/null +++ b/src/Volo.Abp.Settings/Volo/Abp/Settings/ISettingProvider.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.Settings +{ + public interface ISettingProvider + { + void Define(ISettingDefinitionContext context); + } +} \ No newline at end of file diff --git a/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingDefinition.cs b/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingDefinition.cs new file mode 100644 index 0000000000..f025276b5e --- /dev/null +++ b/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingDefinition.cs @@ -0,0 +1,16 @@ +namespace Volo.Abp.Settings +{ + public class SettingDefinition + { + public string Name { get; } + + public string DefaultValue { get; set; } + + public SettingDefinition(string name, string defaultValue = null) + { + Name = name; + + DefaultValue = defaultValue; + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingDefinitionContext.cs b/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingDefinitionContext.cs new file mode 100644 index 0000000000..9ab4369b57 --- /dev/null +++ b/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingDefinitionContext.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; + +namespace Volo.Abp.Settings +{ + public class SettingDefinitionContext : ISettingDefinitionContext + { + protected Dictionary Settings { get; } + + public SettingDefinitionContext(Dictionary settings) + { + Settings = settings; + } + + public virtual SettingDefinition GetOrNull(string name) + { + return Settings.GetOrDefault(name); + } + + public virtual void Add(params SettingDefinition[] definitions) + { + if (definitions.IsNullOrEmpty()) + { + return; + } + + foreach (var definition in definitions) + { + Settings[definition.Name] = definition; + } + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingDefinitionManager.cs b/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingDefinitionManager.cs new file mode 100644 index 0000000000..14bb230b91 --- /dev/null +++ b/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingDefinitionManager.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Settings +{ + public class SettingDefinitionManager : ISettingDefinitionManager, ISingletonDependency + { + protected Lazy> Providers { get; } + + protected Lazy> SettingDefinitions { get; } + + protected SettingOptions Options { get; } + + private readonly IServiceProvider _serviceProvider; + + public SettingDefinitionManager( + IOptions options, + IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + Options = options.Value; + + Providers = new Lazy>(CreateSettingProviders, true); + SettingDefinitions = new Lazy>(CreateSettingDefinitions, true); + } + + public virtual SettingDefinition Get(string name) + { + var settingDefinition = GetOrNull(name); + + if (settingDefinition == null) + { + throw new AbpException("Undefined setting: " + name); + } + + return settingDefinition; + } + + public virtual SettingDefinition GetOrNull(string name) + { + return SettingDefinitions.Value.GetOrDefault(name); + } + + private List CreateSettingProviders() + { + return Options + .Providers + .Select(p => _serviceProvider.GetRequiredService(p) as ISettingProvider) + .ToList(); + } + + private IDictionary CreateSettingDefinitions() + { + var settings = new Dictionary(); + + foreach (var provider in Providers.Value) + { + provider.Define(new SettingDefinitionContext(settings)); + } + + return settings; + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingManager.cs b/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingManager.cs index 8fc98c8245..ca042a7108 100644 --- a/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingManager.cs +++ b/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingManager.cs @@ -10,12 +10,18 @@ namespace Volo.Abp.Settings { public class SettingManager : ISettingManager, ISingletonDependency { + protected ISettingDefinitionManager SettingDefinitionManager { get; } + protected Lazy> Contributors { get; } protected SettingOptions Options { get; } - public SettingManager(IOptions options, IServiceProvider serviceProvider) + public SettingManager( + IOptions options, + IServiceProvider serviceProvider, + ISettingDefinitionManager settingDefinitionManager) { + SettingDefinitionManager = settingDefinitionManager; Options = options.Value; Contributors = new Lazy>( @@ -32,18 +38,20 @@ namespace Volo.Abp.Settings return GetOrNullAsync(name, null, null); } - public Task GetOrNullAsync(string name, string entityType, string entityId, bool fallback = true) + public async Task GetOrNullAsync(string name, string entityType, string entityId, bool fallback = true) { + var settingDefinition = SettingDefinitionManager.Get(name); + foreach (var contributor in Contributors.Value) { - var value = contributor.GetOrNull(name, entityType, entityId, fallback); + var value = await contributor.GetOrNullAsync(name, entityType, entityId, fallback); if (value != null) { return value; } } - return null; + return settingDefinition.DefaultValue; } public Task> GetAllAsync() diff --git a/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingOptions.cs b/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingOptions.cs index 595b76aa64..3be5cc2e8d 100644 --- a/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingOptions.cs +++ b/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingOptions.cs @@ -6,12 +6,16 @@ namespace Volo.Abp.Settings { public ITypeList Contributors { get; } + public ITypeList Providers { get; set; } + public SettingOptions() { Contributors = new TypeList { typeof(DefaultSettingContributor) }; + + Providers = new TypeList(); } } } diff --git a/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingProvider.cs b/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingProvider.cs new file mode 100644 index 0000000000..bc5a436682 --- /dev/null +++ b/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingProvider.cs @@ -0,0 +1,9 @@ +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Settings +{ + public abstract class SettingProvider : ISettingProvider, ISingletonDependency + { + public abstract void Define(ISettingDefinitionContext context); + } +} \ No newline at end of file diff --git a/test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/AbpIdentityApplicationTestModule.cs b/test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/AbpIdentityApplicationTestModule.cs index 7015bda9cc..71c49212cf 100644 --- a/test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/AbpIdentityApplicationTestModule.cs +++ b/test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/AbpIdentityApplicationTestModule.cs @@ -32,6 +32,11 @@ namespace Volo.Abp.Settings { options.TransactionBehavior = UnitOfWorkTransactionBehavior.Disabled; //EF in-memory database does not support transactions }); + + services.Configure(options => + { + options.Providers.Add(); + }); } public override void OnApplicationInitialization(ApplicationInitializationContext context) diff --git a/test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/SettingManager_Tests.cs b/test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/SettingManager_Tests.cs index 75f76a6180..383a2f801d 100644 --- a/test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/SettingManager_Tests.cs +++ b/test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/SettingManager_Tests.cs @@ -14,7 +14,22 @@ namespace Volo.Abp.Settings } [Fact] - public async Task Test1() + public async Task Should_Throw_Exception_When_Try_To_Get_An_Undefined_Setting() + { + await Assert.ThrowsAsync( + async () => await _settingManager.GetOrNullAsync("UndefinedSetting") + ); + } + + [Fact] + public async Task Should_Get_Default_Value_If_Not_Set_In_Store() + { + var value = await _settingManager.GetOrNullAsync("SettingNotSetInStore"); + value.ShouldBe("default-value"); + } + + [Fact] + public async Task Should_Get_From_Store_Without_Entity_Props() { var value = await _settingManager.GetOrNullAsync("MySetting1"); value.ShouldBe("42"); diff --git a/test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/TestSettingProvider.cs b/test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/TestSettingProvider.cs new file mode 100644 index 0000000000..ea06231514 --- /dev/null +++ b/test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/TestSettingProvider.cs @@ -0,0 +1,13 @@ +using System; + +namespace Volo.Abp.Settings +{ + public class TestSettingProvider : SettingProvider + { + public override void Define(ISettingDefinitionContext context) + { + context.Add(new SettingDefinition("MySetting1")); + context.Add(new SettingDefinition("SettingNotSetInStore", defaultValue: "default-value")); + } + } +} \ No newline at end of file