From 2cd8311792de2505f3aca422cd348738205f241b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Sun, 17 May 2020 00:11:19 +0300 Subject: [PATCH] #3979: Allow to set custom "default value" for extra properties. --- docs/en/Object-Extensions.md | 38 ++++++++++++++ .../ObjectExtending/ExtensionPropertyDto.cs | 2 + .../CachedObjectExtensionsDtoService.cs | 1 + .../Abp/Data/HasExtraPropertiesExtensions.cs | 9 +++- .../ExtensionPropertyHelper.cs | 16 +++++- .../ExtensionPropertyConfiguration.cs | 20 ++++++++ .../ModuleExtensionConfigurationHelper.cs | 2 + .../ObjectExtensionPropertyInfo.cs | 20 ++++++++ .../ObjectExtensionManager_Tests.cs | 50 +++++++++++++++++++ 9 files changed, 155 insertions(+), 3 deletions(-) diff --git a/docs/en/Object-Extensions.md b/docs/en/Object-Extensions.md index 21391170d3..40a7ddd5ea 100644 --- a/docs/en/Object-Extensions.md +++ b/docs/en/Object-Extensions.md @@ -174,6 +174,44 @@ ObjectExtensionManager.Instance The following sections explain the fundamental property configuration options. +#### Default Value + +A default value is automatically set for the new property, which is the natural default value for the property type, like `null` for `string`, `false` for `bool` or `0` for `int`. + +There are two ways to override the default value: + +##### DefaultValue Option + +`DefaultValue` option can be set to any value: + +````csharp +ObjectExtensionManager.Instance + .AddOrUpdateProperty( + "MyIntProperty", + options => + { + options.DefaultValue = 42; + }); +```` + +##### DefaultValueFactory Options + +`DefaultValueFactory` can be set to a function that returns the default value: + +````csharp +ObjectExtensionManager.Instance + .AddOrUpdateProperty( + "MyIntProperty", + options => + { + options.DefaultValueFactory = () => 42; + }); +```` + +`options.DefaultValueFactory` has a higher priority than the `options.DefaultValue` . + +> Tip: Use `DefaultValueFactory` option only if the default value may change over the time. If it is a constant value, then use the `DefaultValue` option. + #### CheckPairDefinitionOnMapping Controls how to check property definitions while mapping two extensible objects. See the "Object to Object Mapping" section to understand the `CheckPairDefinitionOnMapping` option better. diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyDto.cs index 74214914b9..b1ba6f7ea9 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionPropertyDto.cs @@ -21,5 +21,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending public List Attributes { get; set; } public Dictionary Configuration { get; set; } + + public object DefaultValue { get; set; } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/CachedObjectExtensionsDtoService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/CachedObjectExtensionsDtoService.cs index 6e72a6863e..c6e200f45d 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/CachedObjectExtensionsDtoService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/CachedObjectExtensionsDtoService.cs @@ -108,6 +108,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending Attributes = new List(), DisplayName = CreateDisplayNameDto(propertyConfig), Configuration = new Dictionary(), + DefaultValue = propertyConfig.GetDefaultValue(), Api = new ExtensionPropertyApiDto { OnGet = new ExtensionPropertyApiGetDto diff --git a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/HasExtraPropertiesExtensions.cs b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/HasExtraPropertiesExtensions.cs index ca9cc0a4c1..6abb350328 100644 --- a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/HasExtraPropertiesExtensions.cs +++ b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/HasExtraPropertiesExtensions.cs @@ -65,8 +65,13 @@ namespace Volo.Abp.Data public static TSource SetDefaultsForExtraProperties(this TSource source, Type objectType = null) where TSource : IHasExtraProperties { + if (objectType == null) + { + objectType = typeof(TSource); + } + var properties = ObjectExtensionManager.Instance - .GetProperties(objectType ?? typeof(TSource)); + .GetProperties(objectType); foreach (var property in properties) { @@ -75,7 +80,7 @@ namespace Volo.Abp.Data continue; } - source.ExtraProperties[property.Name] = TypeHelper.GetDefaultValue(property.Type); + source.ExtraProperties[property.Name] = property.GetDefaultValue(); } return source; diff --git a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensionPropertyHelper.cs b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensionPropertyHelper.cs index 507f67997a..ee36b397c8 100644 --- a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensionPropertyHelper.cs +++ b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensionPropertyHelper.cs @@ -5,7 +5,7 @@ using Volo.Abp.Reflection; namespace Volo.Abp.ObjectExtending { - public static class ExtensionPropertyHelper + internal static class ExtensionPropertyHelper { public static IEnumerable GetDefaultAttributes(Type type) { @@ -19,5 +19,19 @@ namespace Volo.Abp.ObjectExtending yield return new EnumDataTypeAttribute(type); } } + + public static object GetDefaultValue( + Type propertyType, + Func defaultValueFactory, + object defaultValue) + { + if (defaultValueFactory != null) + { + return defaultValueFactory(); + } + + return defaultValue ?? + TypeHelper.GetDefaultValue(propertyType); + } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ExtensionPropertyConfiguration.cs b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ExtensionPropertyConfiguration.cs index 47882fdf93..6d66e51337 100644 --- a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ExtensionPropertyConfiguration.cs +++ b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ExtensionPropertyConfiguration.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using JetBrains.Annotations; using Volo.Abp.Localization; +using Volo.Abp.Reflection; namespace Volo.Abp.ObjectExtending.Modularity { @@ -45,6 +46,19 @@ namespace Volo.Abp.ObjectExtending.Modularity [NotNull] public ExtensionPropertyApiConfiguration Api { get; } + /// + /// Uses as the default value if was not set. + /// + [CanBeNull] + public object DefaultValue { get; set; } + + /// + /// Used with the first priority to create the default value for the property. + /// Uses to the if this was not set. + /// + [CanBeNull] + public Func DefaultValueFactory { get; set; } + public ExtensionPropertyConfiguration( [NotNull] EntityExtensionConfiguration entityExtensionConfiguration, [NotNull] Type type, @@ -63,6 +77,12 @@ namespace Volo.Abp.ObjectExtending.Modularity Api = new ExtensionPropertyApiConfiguration(); Attributes.AddRange(ExtensionPropertyHelper.GetDefaultAttributes(Type)); + DefaultValue = TypeHelper.GetDefaultValue(Type); + } + + public object GetDefaultValue() + { + return ExtensionPropertyHelper.GetDefaultValue(Type, DefaultValueFactory, DefaultValue); } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ModuleExtensionConfigurationHelper.cs b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ModuleExtensionConfigurationHelper.cs index d819918ac5..cb5be17c1f 100644 --- a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ModuleExtensionConfigurationHelper.cs +++ b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ModuleExtensionConfigurationHelper.cs @@ -152,6 +152,8 @@ namespace Volo.Abp.ObjectExtending.Modularity property.Attributes.AddRange(propertyConfig.Attributes); property.DisplayName = propertyConfig.DisplayName; property.Validators.AddRange(propertyConfig.Validators); + property.DefaultValue = propertyConfig.DefaultValue; + property.DefaultValueFactory = propertyConfig.DefaultValueFactory; } ); } diff --git a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfo.cs b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfo.cs index e89598e6d2..12b32ab241 100644 --- a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfo.cs +++ b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfo.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using JetBrains.Annotations; using Volo.Abp.Localization; +using Volo.Abp.Reflection; namespace Volo.Abp.ObjectExtending { @@ -47,6 +48,19 @@ namespace Volo.Abp.ObjectExtending [NotNull] public Dictionary Configuration { get; } + /// + /// Uses as the default value if was not set. + /// + [CanBeNull] + public object DefaultValue { get; set; } + + /// + /// Used with the first priority to create the default value for the property. + /// Uses to the if this was not set. + /// + [CanBeNull] + public Func DefaultValueFactory { get; set; } + public ObjectExtensionPropertyInfo( [NotNull] ObjectExtensionInfo objectExtension, [NotNull] Type type, @@ -62,6 +76,12 @@ namespace Volo.Abp.ObjectExtending Validators = new List>(); Attributes.AddRange(ExtensionPropertyHelper.GetDefaultAttributes(Type)); + DefaultValue = TypeHelper.GetDefaultValue(Type); + } + + public object GetDefaultValue() + { + return ExtensionPropertyHelper.GetDefaultValue(Type, DefaultValueFactory, DefaultValue); } } } diff --git a/framework/test/Volo.Abp.ObjectExtending.Tests/Volo/Abp/ObjectExtending/ObjectExtensionManager_Tests.cs b/framework/test/Volo.Abp.ObjectExtending.Tests/Volo/Abp/ObjectExtending/ObjectExtensionManager_Tests.cs index cb5b308c64..6386dc41ff 100644 --- a/framework/test/Volo.Abp.ObjectExtending.Tests/Volo/Abp/ObjectExtending/ObjectExtensionManager_Tests.cs +++ b/framework/test/Volo.Abp.ObjectExtending.Tests/Volo/Abp/ObjectExtending/ObjectExtensionManager_Tests.cs @@ -119,6 +119,56 @@ namespace Volo.Abp.ObjectExtending .ShouldNotContain(x => x is RequiredAttribute); } + [Fact] + public void Should_Set_DefaultValues() + { + _objectExtensionManager + .AddOrUpdateProperty("IntProp") + .AddOrUpdateProperty("IntPropWithCustomDefaultValue", property => + { + property.DefaultValue = 42; + }) + .AddOrUpdateProperty("BoolProp") + .AddOrUpdateProperty("NullableIntProp") + .AddOrUpdateProperty("NullableIntPropWithCustomDefaultValueFactory", property => + { + property.DefaultValueFactory = () => 2; + }) + .AddOrUpdateProperty("StringProp") + .AddOrUpdateProperty("StringPropWithCustomDefaultValue", property => + { + property.DefaultValue = "custom-value"; + }); + + _objectExtensionManager + .GetPropertyOrNull("IntProp") + .DefaultValue.ShouldBe(0); + + _objectExtensionManager + .GetPropertyOrNull("IntPropWithCustomDefaultValue") + .DefaultValue.ShouldBe(42); + + _objectExtensionManager + .GetPropertyOrNull("BoolProp") + .DefaultValue.ShouldBe(false); + + _objectExtensionManager + .GetPropertyOrNull("NullableIntProp") + .DefaultValue.ShouldBeNull(); + + var propWithDefaultValueFactory = _objectExtensionManager + .GetPropertyOrNull("NullableIntPropWithCustomDefaultValueFactory"); + propWithDefaultValueFactory.GetDefaultValue().ShouldBe(2); + + _objectExtensionManager + .GetPropertyOrNull("StringProp") + .DefaultValue.ShouldBeNull(); + + _objectExtensionManager + .GetPropertyOrNull("StringPropWithCustomDefaultValue") + .DefaultValue.ShouldBe("custom-value"); + } + private class MyExtensibleObject : ExtensibleObject {