diff --git a/Library/Api/IEnumConvertor.cs b/Library/Api/IEnumConvertor.cs new file mode 100644 index 0000000..d900d9b --- /dev/null +++ b/Library/Api/IEnumConvertor.cs @@ -0,0 +1,13 @@ +namespace Serein.Library.Api +{ + /// + /// 枚举转换器接口 + /// + /// + /// + public interface IEnumConvertor + { + TValue Convertor(TEnum e); + } + +} diff --git a/Library/Attributes/AutoInjectionAttribute.cs b/Library/Attributes/AutoInjectionAttribute.cs new file mode 100644 index 0000000..14be4dc --- /dev/null +++ b/Library/Attributes/AutoInjectionAttribute.cs @@ -0,0 +1,17 @@ +using System; + +namespace Serein.Library +{ + /// + /// 表示该属性为自动注入依赖项。 + /// 使用场景:构造函数中存在互相依赖的情况 + /// 例如ServiceA类构造函数中需要传入ServiceB,ServiceB类构造函数中也需要传入ServiceA + /// 这种情况会导致流程启动时,IOC容器无法注入构造函数并创建类型,导致启动失败。 + /// 解决方法:从ServiceA类的构造函数中移除ServiceB类型的入参,将该类型更改为公开可见的可写属性成员ServiceB serviceB{get;set;},并在该属性上标记[AutoInjection]特性 + /// + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] + public sealed class AutoInjectionAttribute : Attribute + { + } + +} diff --git a/Library/Attributes/AutoRegisterAttribute.cs b/Library/Attributes/AutoRegisterAttribute.cs new file mode 100644 index 0000000..4ae3d34 --- /dev/null +++ b/Library/Attributes/AutoRegisterAttribute.cs @@ -0,0 +1,26 @@ +using Serein.Library; +using System; + +namespace Serein.Library +{ + /// + /// 启动流程时,会将标记了该特性的类自动注册到IOC容器中,从而无需手动进行注册绑定。 + /// 流程启动后,IOC容器会进行5次注册绑定。 + /// 第1次注册绑定:初始化所有节点所属的类([DynamicFlow]标记的类)。 + /// 第2次注册绑定:※初始化所有[AutoRegister(Class=FlowInit)]的类。 + /// 第3次注册绑定:调用所有Init节点后,进行注册绑定。 + /// 第4次注册绑定:※初始化所有[AutoRegister(Class=FlowLoading)]的类 + /// 第5次注册绑定:调用所有Load节点后,进行注册绑定。 + /// 需要注意的是,在第1次进行注册绑定的过程中,如果类的构造函数存在入参,那么也会将入参自动创建实例并托管到IOC容器中。 + /// + [AttributeUsage(AttributeTargets.Class)] + public sealed class AutoRegisterAttribute : Attribute + { + public AutoRegisterAttribute(RegisterSequence Class = RegisterSequence.FlowInit) + { + this.Class = Class; + } + public RegisterSequence Class ; + } + +} diff --git a/Library/Attributes/BindConvertorAttribute.cs b/Library/Attributes/BindConvertorAttribute.cs new file mode 100644 index 0000000..04b6774 --- /dev/null +++ b/Library/Attributes/BindConvertorAttribute.cs @@ -0,0 +1,21 @@ +using System; + +namespace Serein.Library +{ + /// + /// 绑定转换器 + /// + [AttributeUsage(AttributeTargets.Parameter)] + public class BindConvertorAttribute : Attribute + { + public Type EnumType { get; } + public Type ConvertorType { get; } + + public BindConvertorAttribute(Type @enum, Type convertor) + { + EnumType = @enum; + ConvertorType = convertor; + } + } + +} diff --git a/Library/Attributes/BindValueAttribute.cs b/Library/Attributes/BindValueAttribute.cs new file mode 100644 index 0000000..e963f6a --- /dev/null +++ b/Library/Attributes/BindValueAttribute.cs @@ -0,0 +1,16 @@ +using System; + +namespace Serein.Library +{ + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] + public class BindValueAttribute : Attribute + { + public object Value { get; } + + public BindValueAttribute(object value) + { + Value = value; + } + } + +} diff --git a/Library/Attributes/DynamicFlowAttribute.cs b/Library/Attributes/DynamicFlowAttribute.cs new file mode 100644 index 0000000..2512a9e --- /dev/null +++ b/Library/Attributes/DynamicFlowAttribute.cs @@ -0,0 +1,28 @@ +using System; +using System.Text.RegularExpressions; + +namespace Serein.Library +{ + + /// + /// 表示该类中存在节点信息 + /// + [AttributeUsage(AttributeTargets.Class)] + public sealed class DynamicFlowAttribute : Attribute + { + public DynamicFlowAttribute(string name = "",bool scan = true) + { + Name = name; + Scan = scan; + } + /// + /// 补充名称,不影响运行流程 + /// + public string Name { get; set; } + /// + /// 如果设置为false,将忽略该类 + /// + public bool Scan { get; set; } = true; + } + +} diff --git a/Library/Attributes/EnumTypeConvertorAttribute.cs b/Library/Attributes/EnumTypeConvertorAttribute.cs new file mode 100644 index 0000000..c0f40c9 --- /dev/null +++ b/Library/Attributes/EnumTypeConvertorAttribute.cs @@ -0,0 +1,27 @@ +using System; + +namespace Serein.Library +{ + /// + /// 枚举值转换器,要求枚举项标记的BindValueAttribute特性,与搭配的参数类型一致,否则参数不会传入 + /// + + [AttributeUsage(AttributeTargets.Parameter)] + public class EnumTypeConvertorAttribute : Attribute + { + public Type EnumType { get; } + + public EnumTypeConvertorAttribute(Type @enum) + { + if (@enum.IsEnum) + { + EnumType = @enum; + } + else + { + throw new ArgumentException("需要枚举类型"); + } + } + } + +} diff --git a/Library/Attributes/NodeActionAttribute.cs b/Library/Attributes/NodeActionAttribute.cs new file mode 100644 index 0000000..aff2ecd --- /dev/null +++ b/Library/Attributes/NodeActionAttribute.cs @@ -0,0 +1,44 @@ +using System; + +namespace Serein.Library +{ + /// + /// 表示该方法将会生成节点,或是加入到流程运行中 + /// 如果是Task类型的返回值,将会自动进行等待 + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] + public sealed class NodeActionAttribute : Attribute + { + public NodeActionAttribute(NodeType methodDynamicType, + string methodTips = "", + bool scan = true, + string lockName = "") + { + Scan = scan; + MethodDynamicType = methodDynamicType; + AnotherName = methodTips; + LockName = lockName; + } + /// + /// 如果设置为false时将不会生成节点信息 + /// + public bool Scan; + /// + /// 类似于注释的效果 + /// + public string AnotherName; + /// + /// 标记节点行为 + /// + public NodeType MethodDynamicType; + /// + /// 暂无意义 + /// + public string LockName; + /// + /// 分组名称,暂无意义 + /// + public string GroupName; + } + +} diff --git a/Library/Attributes/NodeParamAttribute.cs b/Library/Attributes/NodeParamAttribute.cs new file mode 100644 index 0000000..486f8f7 --- /dev/null +++ b/Library/Attributes/NodeParamAttribute.cs @@ -0,0 +1,24 @@ +using System; + +namespace Serein.Library +{ + /// + /// 节点参数设置 + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class NodeParamAttribute : Attribute + { + /// + /// 显示名称 + /// + public string Name; + + /// + /// 是否显式设置(此设置对于有入参默认值的参数无效) + /// + public bool IsExplicit; + + + } + +} diff --git a/Library/Enums/RegisterSequence.cs b/Library/Enums/RegisterSequence.cs new file mode 100644 index 0000000..7b60023 --- /dev/null +++ b/Library/Enums/RegisterSequence.cs @@ -0,0 +1,21 @@ +namespace Serein.Library +{ + /// + /// 注册顺序 + /// + public enum RegisterSequence + { /// + /// 不自动初始化 + /// + Node, + /// + /// 初始化后 + /// + FlowInit, + /// + /// 加载后 + /// + FlowLoading, + } + +} diff --git a/Library/FlowNode/MethodDetails.cs b/Library/FlowNode/MethodDetails.cs index 4a6001f..5a5b532 100644 --- a/Library/FlowNode/MethodDetails.cs +++ b/Library/FlowNode/MethodDetails.cs @@ -72,7 +72,6 @@ namespace Serein.Library /// [PropertyInfo] private ParameterDetails[] _parameterDetailss; - //private List _parameterDetailss; /// /// 描述该方法是否存在可选参数 diff --git a/Library/FlowNode/ParameterDetails.cs b/Library/FlowNode/ParameterDetails.cs index 056c089..37f0765 100644 --- a/Library/FlowNode/ParameterDetails.cs +++ b/Library/FlowNode/ParameterDetails.cs @@ -36,7 +36,7 @@ namespace Serein.Library /// 如果为 true ,则使用输入的文本值作为入参数据。 /// 如果为 false ,则在当前流程上下文中,根据 ArgDataSourceNodeGuid 查找到对应节点,并根据 ArgDataSourceNodeGuid 判断如何获取其返回的数据,以此作为入参数据。 /// - [PropertyInfo(IsNotification = true)] + [PropertyInfo(IsNotification = true, IsVerify = true)] private bool _isExplicitData ; ///// @@ -160,18 +160,18 @@ namespace Serein.Library ExplicitType = Type.GetType(info.ExplicitTypeFullName); InputType = info.InputType.ConvertEnum(); Items = info.Items; - IsParams = info.IsParams; + IsParams = info.IsParams; } - - partial void OnIsExplicitDataChanged(bool oldValue, bool newValue) + partial void BeforeTheIsExplicitData(ref bool __isAllow, bool newValue) { if(DataType == typeof(IFlowContext)) { - + __isAllow = false; } } + /// /// 转为描述 /// diff --git a/Library/NodeAttribute.cs b/Library/NodeAttribute.cs deleted file mode 100644 index 4eadefc..0000000 --- a/Library/NodeAttribute.cs +++ /dev/null @@ -1,202 +0,0 @@ -using System; -using System.Text.RegularExpressions; - -namespace Serein.Library -{ - /// - /// 表示该属性为自动注入依赖项。 - /// 使用场景:构造函数中存在互相依赖的情况 - /// 例如ServiceA类构造函数中需要传入ServiceB,ServiceB类构造函数中也需要传入ServiceA - /// 这种情况会导致流程启动时,IOC容器无法注入构造函数并创建类型,导致启动失败。 - /// 解决方法:从ServiceA类的构造函数中移除ServiceB类型的入参,将该类型更改为公开可见的可写属性成员ServiceB serviceB{get;set;},并在该属性上标记[AutoInjection]特性 - /// - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] - public sealed class AutoInjectionAttribute : Attribute - { - } - - - /// - /// 注册顺序 - /// - public enum RegisterSequence - { /// - /// 不自动初始化 - /// - Node, - /// - /// 初始化后 - /// - FlowInit, - /// - /// 加载后 - /// - FlowLoading, - } - - - /// - /// 启动流程时,会将标记了该特性的类自动注册到IOC容器中,从而无需手动进行注册绑定。 - /// 流程启动后,IOC容器会进行5次注册绑定。 - /// 第1次注册绑定:初始化所有节点所属的类([DynamicFlow]标记的类)。 - /// 第2次注册绑定:※初始化所有[AutoRegister(Class=FlowInit)]的类。 - /// 第3次注册绑定:调用所有Init节点后,进行注册绑定。 - /// 第4次注册绑定:※初始化所有[AutoRegister(Class=FlowLoading)]的类 - /// 第5次注册绑定:调用所有Load节点后,进行注册绑定。 - /// 需要注意的是,在第1次进行注册绑定的过程中,如果类的构造函数存在入参,那么也会将入参自动创建实例并托管到IOC容器中。 - /// - [AttributeUsage(AttributeTargets.Class)] - public sealed class AutoRegisterAttribute : Attribute - { - public AutoRegisterAttribute(RegisterSequence Class = RegisterSequence.FlowInit) - { - this.Class = Class; - } - public RegisterSequence Class ; - } - - /// - /// 表示该类中存在节点信息 - /// - [AttributeUsage(AttributeTargets.Class)] - public sealed class DynamicFlowAttribute : Attribute - { - public DynamicFlowAttribute(string name = "",bool scan = true) - { - Name = name; - Scan = scan; - } - /// - /// 补充名称,不影响运行流程 - /// - public string Name { get; set; } - /// - /// 如果设置为false,将忽略该类 - /// - public bool Scan { get; set; } = true; - } - - - - /// - /// 表示该方法将会生成节点,或是加入到流程运行中 - /// 如果是Task类型的返回值,将会自动进行等待 - /// - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] - public sealed class NodeActionAttribute : Attribute - { - public NodeActionAttribute(NodeType methodDynamicType, - string methodTips = "", - bool scan = true, - string lockName = "") - { - Scan = scan; - MethodDynamicType = methodDynamicType; - AnotherName = methodTips; - LockName = lockName; - } - /// - /// 如果设置为false时将不会生成节点信息 - /// - public bool Scan; - /// - /// 类似于注释的效果 - /// - public string AnotherName; - /// - /// 标记节点行为 - /// - public NodeType MethodDynamicType; - /// - /// 暂无意义 - /// - public string LockName; - /// - /// 分组名称,暂无意义 - /// - public string GroupName; - } - - - /// - /// 节点参数设置 - /// - [AttributeUsage(AttributeTargets.Parameter)] - public sealed class NodeParamAttribute : Attribute - { - /// - /// 显示名称 - /// - public string Name; - - /// - /// 是否显式设置(此设置对于有入参默认值的参数无效) - /// - public bool IsExplicit; - - - } - - - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] - public class BindValueAttribute : Attribute - { - public object Value { get; } - - public BindValueAttribute(object value) - { - Value = value; - } - } - - - - /// - /// 枚举值转换器,要求枚举项标记的BindValueAttribute特性,与搭配的参数类型一致,否则参数不会传入 - /// - - [AttributeUsage(AttributeTargets.Parameter)] - public class EnumTypeConvertorAttribute : Attribute - { - public Type EnumType { get; } - - public EnumTypeConvertorAttribute(Type @enum) - { - if (@enum.IsEnum) - { - EnumType = @enum; - } - else - { - throw new ArgumentException("需要枚举类型"); - } - } - } - - /// - /// 绑定转换器 - /// - [AttributeUsage(AttributeTargets.Parameter)] - public class BindConvertorAttribute : Attribute - { - public Type EnumType { get; } - public Type ConvertorType { get; } - - public BindConvertorAttribute(Type @enum, Type convertor) - { - this.EnumType = @enum; - this.ConvertorType = convertor; - } - } - - /// - /// 枚举转换器接口 - /// - /// - /// - public interface IEnumConvertor - { - TValue Convertor(TEnum e); - } - -} diff --git a/Library/NodeStaticConfig.cs b/Library/NodeStaticConfig.cs index c3b8ff2..bd92b86 100644 --- a/Library/NodeStaticConfig.cs +++ b/Library/NodeStaticConfig.cs @@ -7,6 +7,9 @@ using System.Threading.Tasks; namespace Serein.Library { + /// + /// 节点静态配置类 + /// public static class NodeStaticConfig { /// diff --git a/NodeFlow/FlowWorkOptions.cs b/NodeFlow/FlowWorkOptions.cs index a144006..171c7af 100644 --- a/NodeFlow/FlowWorkOptions.cs +++ b/NodeFlow/FlowWorkOptions.cs @@ -1,11 +1,5 @@ -using Microsoft.Extensions.ObjectPool; -using Serein.Library; +using Serein.Library; using Serein.Library.Api; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Serein.NodeFlow { diff --git a/NodeFlow/Model/Librarys/FlowLibraryCache.cs b/NodeFlow/Model/Librarys/FlowLibraryCache.cs index fff9e40..58fb0f9 100644 --- a/NodeFlow/Model/Librarys/FlowLibraryCache.cs +++ b/NodeFlow/Model/Librarys/FlowLibraryCache.cs @@ -1,16 +1,6 @@ using Serein.Library; -using Serein.Library.Utils; using Serein.NodeFlow.Tool; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Diagnostics; -using System.Globalization; -using System.IO; -using System.Linq; using System.Reflection; -using System.Text; -using static System.Runtime.InteropServices.JavaScript.JSType; namespace Serein.NodeFlow.Model.Library { diff --git a/NodeFlow/Services/FlowLibraryService.cs b/NodeFlow/Services/FlowLibraryService.cs index 4dcd363..b99ae75 100644 --- a/NodeFlow/Services/FlowLibraryService.cs +++ b/NodeFlow/Services/FlowLibraryService.cs @@ -1,17 +1,10 @@ using Serein.Library; using Serein.Library.Api; -using Serein.Library.FlowNode; -using Serein.Library.Utils; using Serein.NodeFlow.Model.Library; using Serein.NodeFlow.Tool; -using System; using System.Collections.Concurrent; -using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Linq; using System.Reflection; -using System.Text; -using System.Threading.Tasks; using System.Xml.Linq; namespace Serein.NodeFlow.Services diff --git a/NodeFlow/Tool/NodeMethodDetailsHelper.cs b/NodeFlow/Tool/NodeMethodDetailsHelper.cs index a6e884b..681757f 100644 --- a/NodeFlow/Tool/NodeMethodDetailsHelper.cs +++ b/NodeFlow/Tool/NodeMethodDetailsHelper.cs @@ -1,13 +1,9 @@ -using Serein.Library.Api; -using Serein.Library.Utils; -using Serein.Library; +using Serein.Library; +using Serein.Library.Api; using System.Collections.Concurrent; -using System.Reflection; -using Serein.Library.FlowNode; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.ComponentModel.DataAnnotations; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; namespace Serein.NodeFlow.Tool; diff --git a/Serein.Library.MyGenerator/Attribute.cs b/Serein.Library.MyGenerator/Attribute.cs index 0d47c83..acec5db 100644 --- a/Serein.Library.MyGenerator/Attribute.cs +++ b/Serein.Library.MyGenerator/Attribute.cs @@ -69,6 +69,14 @@ namespace Serein.Library /// 是否禁止参数进行修改(初始化后不能再通过 Setter 修改) /// public bool IsProtection = false; + + /// + /// 是否需要验证参数 + /// + public bool IsVerify = false; + + + /* /// /// 自定义代码(属性变更前) diff --git a/Serein.Library.MyGenerator/ParameterDetailsPropertyGenerator.cs b/Serein.Library.MyGenerator/ParameterDetailsPropertyGenerator.cs index 8e550f5..218b484 100644 --- a/Serein.Library.MyGenerator/ParameterDetailsPropertyGenerator.cs +++ b/Serein.Library.MyGenerator/ParameterDetailsPropertyGenerator.cs @@ -146,9 +146,17 @@ namespace Serein.Library.NodeGenerator var attributeInfo = fieldKV.Value; // 缓存的特性信息 var isProtection = attributeInfo.Search(nameof(PropertyInfo), nameof(PropertyInfo.IsProtection), value => bool.Parse(value)); // 是否为保护字段 + var isVerify = attributeInfo.Search(nameof(PropertyInfo), nameof(PropertyInfo.IsVerify), value => bool.Parse(value)); // 是否为保护字段 + //sb.AppendLine(leadingTrivia); sb.AppendLine($" partial void On{propertyName}Changed({fieldType} oldValue, {fieldType} newValue);"); sb.AppendLine($" partial void On{propertyName}Changed({fieldType} value);"); + + if (isVerify) + { + sb.AppendLine($" partial void BeforeThe{propertyName}(ref bool __isAllow, {fieldType} newValue);"); + } + if (isProtection) { sb.AppendLine($" private bool __{propertyName}ProtectionField = false;"); @@ -164,7 +172,12 @@ namespace Serein.Library.NodeGenerator sb.AppendLine( " set"); sb.AppendLine( " {"); //sb.AppendLine($" if ({fieldName} {(isProtection ? "== default" : "!= value")})"); // 非保护的Setter - + if (isVerify) + { + sb.AppendLine($" bool __isAllow = true;"); + sb.AppendLine($" BeforeThe{propertyName}(ref __isAllow, value);"); + sb.AppendLine($" if(!__isAllow) return; // 修改验证失败"); + } sb.AppendLine($" var __oldValue = {fieldName};"); if (isProtection) {