Browse Source

Add validation for extra properties.

pull/3561/head
Halil İbrahim Kalkan 7 years ago
parent
commit
2b211c1073
  1. 18
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/AggregateRoot.cs
  2. 13
      framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/HasExtraPropertiesExtensions.cs
  3. 2
      framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/IHasExtraProperties.cs
  4. 11
      framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensibleObject.cs
  5. 75
      framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensibleObjectValidator.cs
  6. 5
      framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfo.cs
  7. 68
      framework/src/Volo.Abp.Validation/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfoValidationExtensions.cs
  8. 25
      framework/src/Volo.Abp.Validation/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfoValidationOptions.cs
  9. 109
      framework/test/Volo.Abp.ObjectExtending.Tests/Volo/Abp/ObjectExtending/ExtensibleObjectValidator_Tests.cs
  10. 4
      framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/HasExtraPropertiesExtensions_Tests.cs

18
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/AggregateRoot.cs

@ -1,8 +1,10 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Auditing;
using Volo.Abp.Data;
using Volo.Abp.ObjectExtending;
namespace Volo.Abp.Domain.Entities
{
@ -56,6 +58,14 @@ namespace Volo.Abp.Domain.Entities
{
_distributedEvents.Clear();
}
public virtual IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
return ExtensibleObjectValidator.GetValidationErrors(
this,
validationContext
);
}
}
[Serializable]
@ -115,5 +125,13 @@ namespace Volo.Abp.Domain.Entities
{
_distributedEvents.Clear();
}
public virtual IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
return ExtensibleObjectValidator.GetValidationErrors(
this,
validationContext
);
}
}
}

13
framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/HasExtraPropertiesExtensions.cs

@ -5,6 +5,8 @@ using Volo.Abp.Reflection;
namespace Volo.Abp.Data
{
//TODO: Move to Volo.Abp.Data.ObjectExtending namespace at v3.0
public static class HasExtraPropertiesExtensions
{
public static bool HasProperty(this IHasExtraProperties source, string name)
@ -12,17 +14,18 @@ namespace Volo.Abp.Data
return source.ExtraProperties.ContainsKey(name);
}
public static object GetProperty(this IHasExtraProperties source, string name)
public static object GetProperty(this IHasExtraProperties source, string name, object defaultValue = null)
{
return source.ExtraProperties?.GetOrDefault(name);
return source.ExtraProperties?.GetOrDefault(name)
?? defaultValue;
}
public static TProperty GetProperty<TProperty>(this IHasExtraProperties source, string name)
public static TProperty GetProperty<TProperty>(this IHasExtraProperties source, string name, TProperty defaultValue = default)
{
var value = source.GetProperty(name);
if (value == default)
if (value == null)
{
return default;
return defaultValue;
}
if (TypeHelper.IsPrimitiveExtended(typeof(TProperty), includeEnums: true))

2
framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/IHasExtraProperties.cs

@ -2,6 +2,8 @@
namespace Volo.Abp.Data
{
//TODO: Move to Volo.Abp.Data.ObjectExtending namespace at v3.0
public interface IHasExtraProperties
{
Dictionary<string, object> ExtraProperties { get; }

11
framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensibleObject.cs

@ -1,11 +1,12 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Data;
namespace Volo.Abp.ObjectExtending
{
[Serializable]
public class ExtensibleObject : IHasExtraProperties
public class ExtensibleObject : IHasExtraProperties, IValidatableObject
{
public Dictionary<string, object> ExtraProperties { get; protected set; }
@ -13,5 +14,13 @@ namespace Volo.Abp.ObjectExtending
{
ExtraProperties = new Dictionary<string, object>();
}
public virtual IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
return ExtensibleObjectValidator.GetValidationErrors(
this,
validationContext
);
}
}
}

75
framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensibleObjectValidator.cs

@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using JetBrains.Annotations;
using Volo.Abp.Data;
using Volo.Abp.DynamicProxy;
namespace Volo.Abp.ObjectExtending
{
public static class ExtensibleObjectValidator
{
[NotNull]
public static List<ValidationResult> GetValidationErrors(
[NotNull] IHasExtraProperties extensibleObject,
[CanBeNull] IServiceProvider serviceProvider = null)
{
var validationErrors = new List<ValidationResult>();
AddValidationErrors(
extensibleObject,
new ValidationContext(
extensibleObject,
serviceProvider,
new Dictionary<object, object>()
),
validationErrors
);
return validationErrors;
}
public static void AddValidationErrors(
[NotNull] IHasExtraProperties extensibleObject,
[NotNull] ValidationContext validationContext,
[NotNull] List<ValidationResult> validationErrors)
{
Check.NotNull(extensibleObject, nameof(extensibleObject));
Check.NotNull(validationContext, nameof(validationContext));
Check.NotNull(validationErrors, nameof(validationErrors));
var objectType = ProxyHelper.UnProxy(extensibleObject).GetType();
var objectExtensionInfo = ObjectExtensionManager.Instance
.GetOrNull(objectType);
if (objectExtensionInfo == null)
{
return;
}
foreach (var propertyInfo in objectExtensionInfo.GetProperties())
{
if (propertyInfo.ValidationAttributes.IsNullOrEmpty())
{
continue;
}
var validationContext2 = new ValidationContext(extensibleObject, validationContext, null)
{
DisplayName = propertyInfo.Name,
MemberName = propertyInfo.Name
};
foreach (var attribute in propertyInfo.ValidationAttributes)
{
var result = attribute.GetValidationResult(extensibleObject.GetProperty(propertyInfo.Name), validationContext2);
if (result != null)
{
validationErrors.Add(result);
}
}
}
}
}
}

5
framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfo.cs

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using JetBrains.Annotations;
namespace Volo.Abp.ObjectExtending
@ -15,6 +16,9 @@ namespace Volo.Abp.ObjectExtending
[NotNull]
public Type Type { get; }
[NotNull]
public List<ValidationAttribute> ValidationAttributes { get; }
/// <summary>
/// Indicates whether to check the other side of the object mapping
/// if it explicitly defines the property. This property is used in;
@ -42,6 +46,7 @@ namespace Volo.Abp.ObjectExtending
Name = Check.NotNull(name, nameof(name));
Configuration = new Dictionary<object, object>();
ValidationAttributes = new List<ValidationAttribute>();
}
}
}

68
framework/src/Volo.Abp.Validation/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfoValidationExtensions.cs

@ -1,68 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using JetBrains.Annotations;
namespace Volo.Abp.ObjectExtending
{
public static class ObjectExtensionPropertyInfoValidationExtensions
{
public const string ValidationConfigurationName = "Validation";
[NotNull]
public static ObjectExtensionPropertyInfo ConfigureValidation(
[NotNull] this ObjectExtensionPropertyInfo propertyExtension,
[NotNull] Action<ObjectExtensionPropertyInfoValidationOptions> configureAction)
{
Check.NotNull(propertyExtension, nameof(propertyExtension));
Check.NotNull(configureAction, nameof(configureAction));
configureAction(propertyExtension.GetOrAddValidationConfiguration());
return propertyExtension;
}
[NotNull]
public static ObjectExtensionPropertyInfo AddValidationAttributes(
[NotNull] this ObjectExtensionPropertyInfo propertyExtension,
[NotNull] params ValidationAttribute[] validationAttributes)
{
Check.NotNull(propertyExtension, nameof(propertyExtension));
Check.NotNullOrEmpty(validationAttributes, nameof(validationAttributes));
propertyExtension
.GetOrAddValidationConfiguration()
.ValidationAttributes
.AddRange(validationAttributes);
return propertyExtension;
}
[CanBeNull]
public static ObjectExtensionPropertyInfoValidationOptions GetValidationConfigurationOrNull(
[NotNull] this ObjectExtensionPropertyInfo propertyExtension)
{
Check.NotNull(propertyExtension, nameof(propertyExtension));
return propertyExtension.Configuration.GetOrDefault(ValidationConfigurationName)
as ObjectExtensionPropertyInfoValidationOptions;
}
[NotNull]
public static ObjectExtensionPropertyInfoValidationOptions GetOrAddValidationConfiguration(
[NotNull] this ObjectExtensionPropertyInfo propertyExtension)
{
Check.NotNull(propertyExtension, nameof(propertyExtension));
var validationConfiguration = propertyExtension.GetValidationConfigurationOrNull();
if (validationConfiguration == null)
{
validationConfiguration = new ObjectExtensionPropertyInfoValidationOptions(propertyExtension);
propertyExtension.Configuration[ValidationConfigurationName] = validationConfiguration;
}
return validationConfiguration;
}
}
}

25
framework/src/Volo.Abp.Validation/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfoValidationOptions.cs

@ -1,25 +0,0 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using JetBrains.Annotations;
namespace Volo.Abp.ObjectExtending
{
public class ObjectExtensionPropertyInfoValidationOptions
{
[NotNull]
public ObjectExtensionPropertyInfo ExtensionProperty { get; }
[NotNull]
public ObjectExtensionInfo ObjectExtension => ExtensionProperty.ObjectExtension;
[NotNull]
public List<ValidationAttribute> ValidationAttributes { get; }
public ObjectExtensionPropertyInfoValidationOptions(
[NotNull] ObjectExtensionPropertyInfo extensionProperty)
{
ExtensionProperty = Check.NotNull(extensionProperty, nameof(extensionProperty));
ValidationAttributes = new List<ValidationAttribute>();
}
}
}

109
framework/test/Volo.Abp.ObjectExtending.Tests/Volo/Abp/ObjectExtending/ExtensibleObjectValidator_Tests.cs

@ -0,0 +1,109 @@
using System.ComponentModel.DataAnnotations;
using Shouldly;
using Volo.Abp.Data;
using Volo.Abp.Threading;
using Xunit;
namespace Volo.Abp.ObjectExtending
{
public class ExtensibleObjectValidator_Tests
{
private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner();
static ExtensibleObjectValidator_Tests()
{
OneTimeRunner.Run(() =>
{
ObjectExtensionManager.Instance
.AddOrUpdate<ExtensiblePersonObject>(options =>
{
options.AddOrUpdateProperty<string>("Name", propertyInfo =>
{
propertyInfo.ValidationAttributes.Add(new RequiredAttribute());
propertyInfo.ValidationAttributes.Add(new StringLengthAttribute(64) { MinimumLength = 2 });
});
options.AddOrUpdateProperty<string>("Address", propertyInfo =>
{
propertyInfo.ValidationAttributes.Add(new StringLengthAttribute(255));
});
options.AddOrUpdateProperty<byte>("Age", propertyInfo =>
{
propertyInfo.ValidationAttributes.Add(new RequiredAttribute());
propertyInfo.ValidationAttributes.Add(new RangeAttribute(18, 99));
});
options.AddOrUpdateProperty<bool>("IsMarried", propertyInfo =>
{
});
});
});
}
[Fact]
public void Should_Validate_If_The_Properties_Are_Valid()
{
ExtensibleObjectValidator
.GetValidationErrors(
new ExtensiblePersonObject
{
ExtraProperties =
{
{"Name", "John"},
{"Age", "42"},
}
}
).Count.ShouldBe(0); //All Valid
}
[Fact]
public void Should_Not_Validate_If_The_Properties_Are_Not_Valid()
{
ExtensibleObjectValidator
.GetValidationErrors(
new ExtensiblePersonObject()
).Count.ShouldBe(2); //Name & Age
ExtensibleObjectValidator
.GetValidationErrors(
new ExtensiblePersonObject
{
ExtraProperties =
{
{"Address", new string('x', 256) }
}
}
).Count.ShouldBe(3); //Name, Age & Address
ExtensibleObjectValidator
.GetValidationErrors(
new ExtensiblePersonObject
{
ExtraProperties =
{
{"Age", "42" }
}
}
).Count.ShouldBe(1); //Name
ExtensibleObjectValidator
.GetValidationErrors(
new ExtensiblePersonObject
{
ExtraProperties =
{
{"Address", new string('x', 256) },
{"Age", "100" }
}
}
).Count.ShouldBe(3); //Name, Age & Address
}
private class ExtensiblePersonObject : ExtensibleObject
{
}
}
}

4
framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/HasExtraPropertiesExtensions_Tests.cs

@ -16,18 +16,22 @@ namespace Volo.Abp.TestApp.Testing
city.HasProperty("UnknownProperty").ShouldBeFalse();
city.GetProperty("UnknownProperty").ShouldBeNull();
city.GetProperty<int>("UnknownProperty").ShouldBe(0);
city.GetProperty<int>("UnknownProperty", 42).ShouldBe(42);
city.SetProperty("IsHot", true);
city.HasProperty("IsHot").ShouldBeTrue();
city.GetProperty<bool>("IsHot").ShouldBeTrue();
city.GetProperty<bool>("IsHot").ShouldBeTrue();
city.SetProperty("IsHot", false);
city.HasProperty("IsHot").ShouldBeTrue();
city.GetProperty<bool>("IsHot").ShouldBeFalse();
city.GetProperty<bool>("IsHot", true).ShouldBeFalse();
city.RemoveProperty("IsHot");
city.HasProperty("IsHot").ShouldBeFalse();
city.GetProperty<bool>("IsHot").ShouldBeFalse();
city.GetProperty<bool>("IsHot", true).ShouldBeFalse();
}
}
}
Loading…
Cancel
Save