ObjectExtending 拓展
namespace Volo.Abp.ObjectExtending;
[DependsOn(
typeof(AbpLocalizationAbstractionsModule),
typeof(AbpValidationAbstractionsModule)
)]
public class AbpObjectExtendingModule : AbpModule
{
}
IHasExtraProperties 基础接口
public interface IHasExtraProperties
{
//保存拓展属性的名及值
ExtraPropertyDictionary ExtraProperties { get; }
}
//额外增加的属性用字典去保存
[Serializable]
public class ExtraPropertyDictionary : Dictionary<string, object?>
{
public ExtraPropertyDictionary()
{
}
public ExtraPropertyDictionary(IDictionary<string, object?> dictionary)
: base(dictionary)
{
}
}
ExtensibleObject 扩展对象,用户想要扩展属性继承此类
[Serializable]
public class ExtensibleObject : IHasExtraProperties, IValidatableObject
{
public ExtraPropertyDictionary ExtraProperties { get; protected set; }
public ExtensibleObject()
: this(true)
{
}
public ExtensibleObject(bool setDefaultsForExtraProperties)
{
ExtraProperties = new ExtraPropertyDictionary();
//允许加入缺省属性
if (setDefaultsForExtraProperties)
{
// 加入缺省属性, 通过 ObjectExtensionManager GetOrNull 方法传入 对象类型,获取在
// ObjectExtensionManager 里配置的 普通类 和 ExtensionInfo 字典里找到 ExtensionInfo
// ExtensionInfo 的内部字典里找到 所有的 ObjectExtensionPropertyInfo
// ProxyHelper.UnProxy(this).GetType() 如果类被代理了可以返回被被代理的类
// 此处设置的值是默认值
this.SetDefaultsForExtraProperties(ProxyHelper.UnProxy(this).GetType());
}
}
//传递一个验证上下文可以对此扩展对象的属性进行验证
public virtual IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
return ExtensibleObjectValidator.GetValidationErrors(
this,
validationContext
);
}
}
IBasicObjectExtensionPropertyInfo 拓展属性类
public interface IBasicObjectExtensionPropertyInfo
{
// 定义的属性名字
[NotNull]
public string Name { get; }
//类型
[NotNull]
public Type Type { get; }
// 给此属性增加的 Attributes
[NotNull]
public List<Attribute> Attributes { get; }
//验证器
[NotNull]
public List<Action<ObjectExtensionPropertyValidationContext>> Validators { get; }
public ILocalizableString? DisplayName { get; }
//缺省值
public object? DefaultValue { get; set; }
//缺省值生成工厂
public Func<object>? DefaultValueFactory { get; set; }
}
实现类
public class ObjectExtensionPropertyInfo :
IHasNameWithLocalizableDisplayName, IBasicObjectExtensionPropertyInfo
{
[NotNull]
public ObjectExtensionInfo ObjectExtension { get; }
[NotNull]
public string Name { get; }
[NotNull]
public Type Type { get; }
[NotNull]
public List<Attribute> Attributes { get; }
[NotNull]
public List<Action<ObjectExtensionPropertyValidationContext>> Validators { get; }
public ILocalizableString? DisplayName { get; set; }
public bool? CheckPairDefinitionOnMapping { get; set; }
[NotNull]
public Dictionary<object, object> Configuration { get; }
public object? DefaultValue { get; set; }
public Func<object>? DefaultValueFactory { get; set; }
[NotNull]
public ExtensionPropertyLookupConfiguration Lookup { get; set; }
public ExtensionPropertyUI UI { get; set; }
public ObjectExtensionPropertyInfo(
[NotNull] ObjectExtensionInfo objectExtension,
[NotNull] Type type,
[NotNull] string name)
{
ObjectExtension = Check.NotNull(objectExtension, nameof(objectExtension));
Type = Check.NotNull(type, nameof(type));
Name = Check.NotNull(name, nameof(name));
Configuration = new Dictionary<object, object>();
Attributes = new List<Attribute>();
Validators = new List<Action<ObjectExtensionPropertyValidationContext>>();
Attributes.AddRange(ExtensionPropertyHelper.GetDefaultAttributes(Type));
DefaultValue = TypeHelper.GetDefaultValue(Type);
Lookup = new ExtensionPropertyLookupConfiguration();
UI = new ExtensionPropertyUI();
}
//获取缺省值,如果没有配置缺省值,通过反射制造一个
public object? GetDefaultValue()
{
return ExtensionPropertyHelper.GetDefaultValue(Type, DefaultValueFactory,
DefaultValue);
}
//内部类,目前不知道在什么情况下有用
public class ExtensionPropertyUI
{
public int Order { get; set; }
public ExtensionPropertyUIEditModal EditModal { get; set; }
public ExtensionPropertyUI()
{
EditModal = new ExtensionPropertyUIEditModal();
}
}
public class ExtensionPropertyUIEditModal
{
public bool IsReadOnly { get; set; }
}
}
ObjectExtensionInfo 拓展对象类
public class ObjectExtensionInfo
{
//被拓展的类
[NotNull]
public Type Type { get; }
// 拓展属性保存的字典
[NotNull]
protected ConcurrentDictionary<string, ObjectExtensionPropertyInfo> Properties { get; }
// 拓展类配置 包括的是 属性名 和 ObjectExtensionPropertyInfo 键值对
[NotNull]
public ConcurrentDictionary<object, object> Configuration { get; }
//验证器集合,因为一个拓展类有多个拓展属性
[NotNull]
public List<Action<ObjectExtensionValidationContext>> Validators { get; }
public ObjectExtensionInfo([NotNull] Type type)
{
Type = Check.NotNull(type, nameof(type));
Properties = new ConcurrentDictionary<string, ObjectExtensionPropertyInfo>();
Configuration = new ConcurrentDictionary<object, object>();
Validators = new List<Action<ObjectExtensionValidationContext>>();
}
public virtual bool HasProperty(string propertyName)
{
return Properties.ContainsKey(propertyName);
}
[NotNull]
public virtual ObjectExtensionInfo AddOrUpdateProperty<TProperty>(
[NotNull] string propertyName,
Action<ObjectExtensionPropertyInfo>? configureAction = null)
{
return AddOrUpdateProperty(
typeof(TProperty),
propertyName,
configureAction
);
}
[NotNull]
public virtual ObjectExtensionInfo AddOrUpdateProperty(
[NotNull] Type propertyType,
[NotNull] string propertyName,
Action<ObjectExtensionPropertyInfo>? configureAction = null)
{
Check.NotNull(propertyType, nameof(propertyType));
Check.NotNull(propertyName, nameof(propertyName));
var propertyInfo = Properties.GetOrAdd(
propertyName,
_ => new ObjectExtensionPropertyInfo(this, propertyType, propertyName)
);
//对属性信息进行自定义配置
configureAction?.Invoke(propertyInfo);
return this;
}
[NotNull]
public virtual ImmutableList<ObjectExtensionPropertyInfo> GetProperties()
{
return Properties.OrderBy(t => t.Value.UI.Order).Select(t => t.Value)
.ToImmutableList();
}
public virtual ObjectExtensionPropertyInfo? GetPropertyOrNull(
[NotNull] string propertyName)
{
Check.NotNullOrEmpty(propertyName, nameof(propertyName));
return Properties.GetOrDefault(propertyName);
}
}
ObjectExtensionManager
通过它可以为 扩展类绑定一个 ObjectExtensionInfo,通过 ObjectExtensionInfo,有可以绑定多个 多个 ObjectExtensionPropertyInfo
public class ObjectExtensionManager
{
public static ObjectExtensionManager Instance { get; protected set; } =
new ObjectExtensionManager();
[NotNull]
public ConcurrentDictionary<object, object> Configuration { get; }
//保存一个类和它的拓展类
protected ConcurrentDictionary<Type, ObjectExtensionInfo> ObjectsExtensions { get; }
protected internal ObjectExtensionManager()
{
ObjectsExtensions = new ConcurrentDictionary<Type, ObjectExtensionInfo>();
Configuration = new ConcurrentDictionary<object, object>();
}
[NotNull]
public virtual ObjectExtensionManager AddOrUpdate<TObject>(
Action<ObjectExtensionInfo>? configureAction = null)
{
return AddOrUpdate(typeof(TObject), configureAction);
}
[NotNull]
public virtual ObjectExtensionManager AddOrUpdate(
[NotNull] Type[] types,
Action<ObjectExtensionInfo>? configureAction = null)
{
Check.NotNull(types, nameof(types));
foreach (var type in types)
{
AddOrUpdate(type, configureAction);
}
return this;
}
[NotNull]
public virtual ObjectExtensionManager AddOrUpdate(
[NotNull] Type type,
Action<ObjectExtensionInfo>? configureAction = null)
{
//对字典进行添加
var extensionInfo = ObjectsExtensions.GetOrAdd(
type,
_ => new ObjectExtensionInfo(type)
);
//对添加的 extensionInfo 进行自定义配置
configureAction?.Invoke(extensionInfo);
return this;
}
public virtual ObjectExtensionInfo? GetOrNull<TObject>()
{
return GetOrNull(typeof(TObject));
}
public virtual ObjectExtensionInfo? GetOrNull([NotNull] Type type)
{
return ObjectsExtensions.GetOrDefault(type);
}
[NotNull]
public virtual ImmutableList<ObjectExtensionInfo> GetExtendedObjects()
{
return ObjectsExtensions.Values.ToImmutableList();
}
}
对象属性扩展的重要扩展方法
public static class HasExtraPropertiesExtensions
{
public static bool HasProperty(this IHasExtraProperties source, string name)
{
return source.ExtraProperties.ContainsKey(name);
}
public static object? GetProperty(this IHasExtraProperties source,
string name, object? defaultValue = null)
{
return source.ExtraProperties.GetOrDefault(name)
?? defaultValue;
}
public static TProperty? GetProperty<TProperty>(
this IHasExtraProperties source, string name, TProperty? defaultValue = default)
{
var value = source.GetProperty(name);
if (value == null)
{
return defaultValue;
}
//判断是否是原始类型,在 原始类型的基础上 abp 有增加的 string , decimal ,guid ,datetime
if (TypeHelper.IsPrimitiveExtended(typeof(TProperty), includeEnums: true))
{
var conversionType = typeof(TProperty);
//是否是可空类型
if (TypeHelper.IsNullable(conversionType))
{
//获取可空类型的范型参数
conversionType = conversionType.GetFirstGenericArgumentIfNullable();
}
//类型是guid
if (conversionType == typeof(Guid))
{
//把字符串转换为指定的类型
return (TProperty)TypeDescriptor.
GetConverter(conversionType).ConvertFromInvariantString(value.ToString()!)!;
}
if (conversionType.IsEnum)
{
return (TProperty)value;
}
//转换为指定的类型
return (TProperty)Convert.
ChangeType(value, conversionType, CultureInfo.InvariantCulture);
}
throw new AbpException("GetProperty<TProperty>
does not support non-primitive types. Use non-generic GetProperty
method and handle type casting manually.");
}
public static TSource SetProperty<TSource>(
this TSource source,
string name,
object? value,
bool validate = true)
where TSource : IHasExtraProperties
{
if (validate)
{
ExtensibleObjectValidator.CheckValue(source, name, value);
}
source.ExtraProperties[name] = value;
return source;
}
public static TSource RemoveProperty<TSource>(this TSource source, string name)
where TSource : IHasExtraProperties
{
source.ExtraProperties.Remove(name);
return source;
}
// 执行 ObjectExtensionManager 之前给 source 设置的信息
public static TSource SetDefaultsForExtraProperties<TSource>(
this TSource source, Type? objectType = null)
where TSource : IHasExtraProperties
{
if (objectType == null)
{
objectType = typeof(TSource);
}
var properties = ObjectExtensionManager.Instance
.GetProperties(objectType);
foreach (var property in properties)
{
if (source.HasProperty(property.Name))
{
continue;
}
source.ExtraProperties[property.Name] = property.GetDefaultValue();
}
return source;
}
public static void SetDefaultsForExtraProperties(object source, Type objectType)
{
if (!(source is IHasExtraProperties))
{
throw new ArgumentException($"Given {nameof(source)}
object does not implement the {nameof(IHasExtraProperties)}
interface!", nameof(source));
}
((IHasExtraProperties)source).SetDefaultsForExtraProperties(objectType);
}
// 把额外属性的值设置给常规属性 ,并且移出相应的额外属性
public static void SetExtraPropertiesToRegularProperties(this IHasExtraProperties source)
{
var properties = source.GetType().GetProperties()
.Where(x => source.ExtraProperties.ContainsKey(x.Name)
&& x.GetSetMethod(true) != null)
.ToList();
foreach (var property in properties)
{
property.SetValue(source, source.ExtraProperties[property.Name]);
source.RemoveProperty(property.Name);
}
}
//判断来个扩展对象是否有相同名字的额外属性
public static bool HasSameExtraProperties(
[NotNull] this IHasExtraProperties source,
[NotNull] IHasExtraProperties other)
{
Check.NotNull(source, nameof(source));
Check.NotNull(other, nameof(other));
return source.ExtraProperties.HasSameItems(other.ExtraProperties);
}
}
ValidationContext
ObjectExtensionPropertyValidationContext 对 .net ValidationContext 进行的包装 在用户自定 义扩展类的扩散属性验证的使用作为上下文传入
public class ObjectExtensionPropertyValidationContext
{
//验证的属性
[NotNull]
public ObjectExtensionPropertyInfo ExtensionPropertyInfo { get; }
// 验证对象
[NotNull]
public IHasExtraProperties ValidatingObject { get; }
// 验证错误结果
[NotNull]
public List<ValidationResult> ValidationErrors { get; }
//验证上下文,来自 net 提供 System.ComponentModel.DataAnnotations
[NotNull]
public ValidationContext ValidationContext { get; }
//验证的值
public object? Value { get; }
//通过验证上下文可以获取 IServiceProvider
public IServiceProvider? ServiceProvider => ValidationContext;
//提供验证值
public ObjectExtensionPropertyValidationContext(
[NotNull] ObjectExtensionPropertyInfo objectExtensionPropertyInfo,
[NotNull] IHasExtraProperties validatingObject,
[NotNull] List<ValidationResult> validationErrors,
[NotNull] ValidationContext validationContext,
object? value)
{
ExtensionPropertyInfo = Check.NotNull(
objectExtensionPropertyInfo, nameof(objectExtensionPropertyInfo));
ValidatingObject = Check.NotNull(validatingObject, nameof(validatingObject));
ValidationErrors = Check.NotNull(validationErrors, nameof(validationErrors));
ValidationContext = Check.NotNull(validationContext, nameof(validationContext));
Value = value;
}
}
ExtensibleObjectValidator
扩展对象验证程序,验证程序验证俩种 :
- 继承 ValidationAttribute 验证
-
用户自定义验证,通过 ObjectExtensionInfo 的属性
List<Action
> Validators 自己验证行为
public static class ExtensibleObjectValidator
{
public static void CheckValue(
[NotNull] IHasExtraProperties extensibleObject,
[NotNull] string propertyName,
object? value)
{
//对扩展对象指定的属性名及给定的值进行验证看是否满足
var validationErrors = GetValidationErrors(
extensibleObject,
propertyName,
value
);
if (validationErrors.Any())
{
throw new AbpValidationException(validationErrors);
}
}
public static bool IsValid(
[NotNull] IHasExtraProperties extensibleObject,
ValidationContext? objectValidationContext = null)
{
return GetValidationErrors(
extensibleObject,
objectValidationContext
).Any();
}
public static bool IsValid(
[NotNull] IHasExtraProperties extensibleObject,
[NotNull] string propertyName,
object? value,
ValidationContext? objectValidationContext = null)
{
return GetValidationErrors(
extensibleObject,
propertyName,
value,
objectValidationContext
).Any();
}
[NotNull]
public static List<ValidationResult> GetValidationErrors(
[NotNull] IHasExtraProperties extensibleObject,
ValidationContext? objectValidationContext = null)
{
var validationErrors = new List<ValidationResult>();
AddValidationErrors(
extensibleObject,
validationErrors,
objectValidationContext
);
return validationErrors;
}
[NotNull]
public static List<ValidationResult> GetValidationErrors(
[NotNull] IHasExtraProperties extensibleObject,
[NotNull] string propertyName,
object? value,
ValidationContext? objectValidationContext = null)
{
var validationErrors = new List<ValidationResult>();
AddValidationErrors(
extensibleObject,
validationErrors,
propertyName,
value,
objectValidationContext
);
return validationErrors;
}
//对扩展对象的所有扩展属性进行验证
public static void AddValidationErrors(
[NotNull] IHasExtraProperties extensibleObject,
[NotNull] List<ValidationResult> validationErrors,
ValidationContext? objectValidationContext = null)
{
Check.NotNull(extensibleObject, nameof(extensibleObject));
Check.NotNull(validationErrors, nameof(validationErrors));
//形成初始的验证上下文
if (objectValidationContext == null)
{
objectValidationContext = new ValidationContext(
extensibleObject,
null,
new Dictionary<object, object?>()
);
}
// 获取代理类的原始类型
var objectType = ProxyHelper.UnProxy(extensibleObject).GetType();
//首先的在 ObjectExtensionManager 获取 之前有没有通过 ObjectExtensionManager
//给扩展类型设置扩展属性,如果有就能获取到 objectExtensionInfo
var objectExtensionInfo = ObjectExtensionManager.Instance
.GetOrNull(objectType);
//没有直接不验证
if (objectExtensionInfo == null)
{
return;
}
//对扩展对象的扩展属性进行验证,实体通过 ValidationAttribute 进行的验证
AddPropertyValidationErrors(
extensibleObject,
validationErrors,
objectValidationContext,
objectExtensionInfo
);
// 扩展对象的用户自定义验证
ExecuteCustomObjectValidationActions(
extensibleObject,
validationErrors,
objectValidationContext,
objectExtensionInfo
);
}
public static void AddValidationErrors(
[NotNull] IHasExtraProperties extensibleObject,
[NotNull] List<ValidationResult> validationErrors,
[NotNull] string propertyName,
object? value,
ValidationContext? objectValidationContext = null)
{
Check.NotNull(extensibleObject, nameof(extensibleObject));
Check.NotNull(validationErrors, nameof(validationErrors));
Check.NotNullOrWhiteSpace(propertyName, nameof(propertyName));
if (objectValidationContext == null)
{
objectValidationContext = new ValidationContext(
extensibleObject,
null,
new Dictionary<object, object?>()
);
}
var objectType = ProxyHelper.UnProxy(extensibleObject).GetType();
var objectExtensionInfo = ObjectExtensionManager.Instance
.GetOrNull(objectType);
if (objectExtensionInfo == null)
{
return;
}
var property = objectExtensionInfo.GetPropertyOrNull(propertyName);
if (property == null)
{
return;
}
AddPropertyValidationErrors(
extensibleObject,
validationErrors,
objectValidationContext,
property,
value
);
}
private static void AddPropertyValidationErrors(
IHasExtraProperties extensibleObject,
List<ValidationResult> validationErrors,
ValidationContext objectValidationContext,
ObjectExtensionInfo objectExtensionInfo)
{
//获取扩展属性
var properties = objectExtensionInfo.GetProperties();
// objectExtensionInfo 里是否配置过 properties
if (!properties.Any())
{
return;
}
foreach (var property in properties)
{
//属性 ObjectExtensionPropertyInfo
//及属性的值, 进行验证
//ObjectExtensionPropertyInfo 里有验证器
AddPropertyValidationErrors(
extensibleObject,
validationErrors,
objectValidationContext,
property,
extensibleObject.GetProperty(property.Name)
);
}
}
private static void AddPropertyValidationErrors(
IHasExtraProperties extensibleObject,
List<ValidationResult> validationErrors,
ValidationContext objectValidationContext,
ObjectExtensionPropertyInfo property,
object? value)
{
//扩展属性的 给定值验证
AddPropertyValidationAttributeErrors(
extensibleObject,
validationErrors,
objectValidationContext,
property,
value
);
//用户验证给定值验证
ExecuteCustomPropertyValidationActions(
extensibleObject,
validationErrors,
objectValidationContext,
property,
value
);
}
//额外增加的属性验证
private static void AddPropertyValidationAttributeErrors(
IHasExtraProperties extensibleObject,
List<ValidationResult> validationErrors,
ValidationContext objectValidationContext,
ObjectExtensionPropertyInfo property,
object? value)
{
//获取此属性所有绑定的验证 ValidationAttribute 特性
var validationAttributes = property.GetValidationAttributes();
//没有任何 ValidationAttribute 特性
if (!validationAttributes.Any())
{
return;
}
// 针对一个自定义的属性验证的上下文
var propertyValidationContext = new ValidationContext(extensibleObject,
objectValidationContext, null)
{
DisplayName = property.Name,
MemberName = property.Name
};
foreach (var attribute in validationAttributes)
{
//使用微软的验证
var result = attribute.GetValidationResult(
value,
propertyValidationContext
);
if (result != null)
{
validationErrors.Add(result);
}
}
}
//客户验证行为,给定值的
private static void ExecuteCustomPropertyValidationActions(
IHasExtraProperties extensibleObject,
List<ValidationResult> validationErrors,
ValidationContext objectValidationContext,
ObjectExtensionPropertyInfo property,
object? value)
{
if (!property.Validators.Any())
{
return;
}
var context = new ObjectExtensionPropertyValidationContext(
property,
extensibleObject,
validationErrors,
objectValidationContext,
value
);
foreach (var validator in property.Validators)
{
validator(context);
}
}
//可以验证的行为
private static void ExecuteCustomObjectValidationActions(
IHasExtraProperties extensibleObject,
List<ValidationResult> validationErrors,
ValidationContext objectValidationContext,
ObjectExtensionInfo objectExtensionInfo)
{
// objectExtensionInfo 配置的 Validators
if (!objectExtensionInfo.Validators.Any())
{
return;
}
var context = new ObjectExtensionValidationContext(
objectExtensionInfo,
extensibleObject,
validationErrors,
objectValidationContext
);
//执行验证
foreach (var validator in objectExtensionInfo.Validators)
{
validator(context);
}
}
}
👍🎉🎊