diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs
index 59166d3e1d..55998a512f 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs
@@ -5,6 +5,7 @@ using Volo.Abp.AspNetCore.Mvc.Conventions;
using Volo.Abp.AspNetCore.Mvc.ExceptionHandling;
using Volo.Abp.AspNetCore.Mvc.Features;
using Volo.Abp.AspNetCore.Mvc.ModelBinding;
+using Volo.Abp.AspNetCore.Mvc.ModelBinding.Metadata;
using Volo.Abp.AspNetCore.Mvc.Response;
using Volo.Abp.AspNetCore.Mvc.Uow;
using Volo.Abp.AspNetCore.Mvc.Validation;
@@ -56,6 +57,7 @@ namespace Volo.Abp.AspNetCore.Mvc
options.ModelMetadataDetailsProviders.Add(
new AbpDataAnnotationAutoLocalizationMetadataDetailsProvider(services)
);
+ options.ModelMetadataDetailsProviders.Add(new AbpExtraPropertyValidationMetadataProvider());
}
}
}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ModelBinding/AbpExtraPropertyModelBinder.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ModelBinding/AbpExtraPropertyModelBinder.cs
index da199cae90..d3f570b1ce 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ModelBinding/AbpExtraPropertyModelBinder.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ModelBinding/AbpExtraPropertyModelBinder.cs
@@ -47,7 +47,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ModelBinding
return value;
}
- var propertyName = ExtractExtraPropertyName(bindingContext.ModelName);
+ var propertyName = ExtraPropertyBindingHelper.ExtractExtraPropertyName(bindingContext.ModelName);
if (propertyName == null)
{
return value;
@@ -61,19 +61,5 @@ namespace Volo.Abp.AspNetCore.Mvc.ModelBinding
return Convert.ChangeType(value, propertyInfo.Type);
}
-
- /* modelName is a string like "UserInfo.ExtraProperties[SocialSecurityNumber]"
- * This method returns "SocialSecurityNumber" for this example. */
- protected virtual string ExtractExtraPropertyName(string modelName)
- {
- //TODO: Use regex(?) and add unit test (by extracting to a helper class)
- var index = modelName.IndexOf(".ExtraProperties[", StringComparison.Ordinal);
- if (index < 0)
- {
- return null;
- }
-
- return modelName.Substring(index + 17, modelName.Length - index - 18);
- }
}
}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ModelBinding/ExtraPropertyBindingHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ModelBinding/ExtraPropertyBindingHelper.cs
new file mode 100644
index 0000000000..cfc8754415
--- /dev/null
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ModelBinding/ExtraPropertyBindingHelper.cs
@@ -0,0 +1,37 @@
+using System;
+
+namespace Volo.Abp.AspNetCore.Mvc.ModelBinding
+{
+ public static class ExtraPropertyBindingHelper
+ {
+ ///
+ /// modelName is a string like "UserInfo.ExtraProperties[SocialSecurityNumber]"
+ /// This method returns "SocialSecurityNumber" for this example. */
+ ///
+ public static string ExtractExtraPropertyName(string modelName)
+ {
+ var index = modelName.IndexOf(".ExtraProperties[", StringComparison.Ordinal);
+ if (index < 0)
+ {
+ return null;
+ }
+
+ return modelName.Substring(index + 17, modelName.Length - index - 18);
+ }
+
+ ///
+ /// modelName is a string like "UserInfo.ExtraProperties[SocialSecurityNumber]"
+ /// This method returns "UserInfo" for this example.
+ ///
+ public static string ExtractContainerName(string modelName)
+ {
+ var index = modelName.IndexOf(".ExtraProperties[", StringComparison.Ordinal);
+ if (index < 0)
+ {
+ return null;
+ }
+
+ return modelName.Left(index);
+ }
+ }
+}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ModelBinding/Metadata/AbpExtraPropertyValidationMetadataProvider.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ModelBinding/Metadata/AbpExtraPropertyValidationMetadataProvider.cs
new file mode 100644
index 0000000000..2d47882d13
--- /dev/null
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ModelBinding/Metadata/AbpExtraPropertyValidationMetadataProvider.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Threading;
+using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
+using Volo.Abp.ObjectExtending;
+
+namespace Volo.Abp.AspNetCore.Mvc.ModelBinding.Metadata
+{
+ public class AbpExtraPropertyValidationMetadataProvider : IValidationMetadataProvider
+ {
+ public static AsyncLocal CurrentModeInfo { get; } = new AsyncLocal();
+
+ public void CreateValidationMetadata(ValidationMetadataProviderContext context)
+ {
+ if (CurrentModeInfo.Value == null)
+ {
+ return;
+ }
+
+ var currentModel = CurrentModeInfo.Value;
+
+ CurrentModeInfo.Value = null;
+
+ var propertyInfo = ObjectExtensionManager.Instance.GetPropertyOrNull(
+ currentModel.ExtensibleObjectType,
+ currentModel.PropertyName
+ );
+
+ if (propertyInfo == null)
+ {
+ return;
+ }
+
+ foreach (var validationAttribute in propertyInfo.ValidationAttributes)
+ {
+ context.ValidationMetadata.ValidatorMetadata.Add(validationAttribute);
+ }
+ }
+
+ public class ExtraPropertyModelInfo
+ {
+ public Type ExtensibleObjectType { get; }
+ public string PropertyName { get; }
+
+ public ExtraPropertyModelInfo(
+ Type extensibleObjectType,
+ string propertyName)
+ {
+ ExtensibleObjectType = extensibleObjectType;
+ PropertyName = propertyName;
+ }
+ }
+ }
+}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ModelBinding/MyModelExpressionProvider.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ModelBinding/MyModelExpressionProvider.cs
new file mode 100644
index 0000000000..7499d1ce85
--- /dev/null
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ModelBinding/MyModelExpressionProvider.cs
@@ -0,0 +1,72 @@
+using System;
+using System.Linq;
+using System.Linq.Expressions;
+using Microsoft.AspNetCore.Mvc.ViewFeatures;
+using Volo.Abp.AspNetCore.Mvc.ModelBinding.Metadata;
+using Volo.Abp.Data;
+using Volo.Abp.DependencyInjection;
+
+namespace Volo.Abp.AspNetCore.Mvc.ModelBinding
+{
+ [Dependency(ReplaceServices = true)]
+ public class AbpModelExpressionProvider : IModelExpressionProvider, ITransientDependency
+ {
+ private readonly ModelExpressionProvider _modelExpressionProvider;
+
+ public AbpModelExpressionProvider(ModelExpressionProvider modelExpressionProvider)
+ {
+ _modelExpressionProvider = modelExpressionProvider;
+ }
+
+ public ModelExpression CreateModelExpression(
+ ViewDataDictionary viewData,
+ Expression> expression)
+ {
+ var result = _modelExpressionProvider.CreateModelExpression(
+ viewData,
+ expression
+ );
+
+ if (result.ModelExplorer.Container == null)
+ {
+ return result;
+ }
+
+ var extraPropertyName = ExtraPropertyBindingHelper.ExtractExtraPropertyName(result.Name);
+ if (extraPropertyName == null)
+ {
+ return result;
+ }
+
+ var containerName = ExtraPropertyBindingHelper.ExtractContainerName(result.Name);
+ if (containerName == null)
+ {
+ return result;
+ }
+
+ var containerModelExpolorer = result
+ .ModelExplorer
+ .Container
+ .Properties
+ .FirstOrDefault(p => p.Metadata.Name == containerName);
+
+ if (containerModelExpolorer == null)
+ {
+ return result;
+ }
+
+ if (!containerModelExpolorer.ModelType.IsAssignableTo())
+ {
+ return result;
+ }
+
+ AbpExtraPropertyValidationMetadataProvider.CurrentModeInfo.Value
+ = new AbpExtraPropertyValidationMetadataProvider.ExtraPropertyModelInfo(
+ containerModelExpolorer.ModelType,
+ extraPropertyName
+ );
+
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionManagerExtensions.cs b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionManagerExtensions.cs
index 21d139913b..1dd8bfb6b1 100644
--- a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionManagerExtensions.cs
+++ b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionManagerExtensions.cs
@@ -79,5 +79,19 @@ namespace Volo.Abp.ObjectExtending
);
});
}
+
+ public static ObjectExtensionPropertyInfo GetPropertyOrNull(
+ [NotNull] this ObjectExtensionManager objectExtensionManager,
+ [NotNull] Type objectType,
+ [NotNull] string propertyName)
+ {
+ Check.NotNull(objectExtensionManager, nameof(objectExtensionManager));
+ Check.NotNull(objectType, nameof(objectType));
+ Check.NotNull(propertyName, nameof(propertyName));
+
+ return objectExtensionManager
+ .GetOrNull(objectType)?
+ .GetPropertyOrNull(propertyName);
+ }
}
}
\ No newline at end of file