ABP ObjectExtending
ABP ObjectExtending
2023/6/1
➡️

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

扩展对象验证程序,验证程序验证俩种 :

  1. 继承 ValidationAttribute 验证
  2. 用户自定义验证,通过 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);
        }
    }
}
👍🎉🎊