# Miao.NetCore
**Repository Path**: miaolove/miao.-net-core
## Basic Information
- **Project Name**: Miao.NetCore
- **Description**: 用于NetCore的各种框架和工具编写
- **Primary Language**: C#
- **License**: MulanPSL-2.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 4
- **Forks**: 1
- **Created**: 2022-03-28
- **Last Updated**: 2025-12-22
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 反射抽象泛型调度器
### 一、设计思想
#### 1.1 抽象类是什么
C#语言中,用abstract 关键字来修饰一个类时,这个类叫作抽象类。抽象类是它的所有子类的公共属性的集合,是包含一个或多个抽象方法的类。抽象类可以看作是对类的进一步抽象。在面向对象领域,抽象类主要用来进行类型隐藏
#### 1.2 为什么要用抽象类
1. 无法完整描述一个类,只能抽象化概念来使用;
2. 子类写的方法重写父类的方法abstact。
#### 1.3 为什么要用泛型
1. 保证了类型的安全性:泛型约束了变量的类型,保证了类型的安全性;
2. 避免了不必要的装箱、拆箱操作,提高程序的性能:泛型变量固定了类型,使用的时候就已经知道是值类型还是引用类型,避免了不必要的装箱、拆箱操作
### 二、代码基础实现
#### 1.构建泛型抽象类
经过以上抽象和泛型思想,就有了以下产物,参数可以传入非固定类型,父类处理公共数据,子类处理业务节点细节数据;
父类:
```C#
///
/// 实现基类
///
///
public abstract class BaseRealization : IEventHandler where TEvent : Event
{
public Task HandleAsync(TEvent @event)
{
try
{
return Task.Run(() => Receive(@event));
}
catch (Exception ex)
{
throw;
}
}
///
/// 调度处理中心
///
///
public void Handle(TEvent @Event)
{
try
{
Receive(@Event);
}
catch (Exception ex)
{
throw ex;
}
}
///
/// 节点处理消息
///
/// 消息体
protected abstract void Receive(TEvent eEvent);
}
```
#### 2.构建子类行为实现
子类实现各自的细节行为逻辑
子类代码:
```C#
///
/// 测试实现
///
[FlowNode("FlowNode1")]
public class TestRealization : BaseRealization
{
///
/// Test接收处理实现
///
///
protected override void Receive(TestParameter eEvent)
{
var json = JsonConvert.SerializeObject(eEvent);
Console.WriteLine($"**********Test**********");
Console.WriteLine($"Test 被实现了,参数name为{eEvent.TestName}");
Console.WriteLine($"**********Test**********\r\n");
}
}
```
#### 3.泛型约束
为了使我们泛型类代码有对应公共参数,于是添加了泛型约束类
泛型约束类代码:
```C#
///
/// 事件
///
public class Event : IEvent
{
///
/// 初始化事件
///
public Event()
{
Id = Guid.NewGuid().ToString();
Time = DateTime.Now;
}
///
/// 事件标识
///
public string Id { get; set; }
///
/// 事件时间
///
public DateTime Time { get; }
}
```
#### 4.添加触发事件接口
为了提供给外部使用,于是我们给抽象类提供了调度接口:
抽象泛型接口代码 :
```C#
///
/// 事件处理器
///
/// 事件类型
public interface IEventHandler : IEventHandler where TEvent : IEvent
{
///
/// 异步处理事件
///
/// 事件
Task HandleAsync(TEvent @event);
///
/// 处理事件
///
/// 事件
void Handle(TEvent @event);
}
```
### 三、反射调度
#### 1.添加特性
首先,我们需要知道哪些类属于我们业务需要调度的类,于是我们在反射时添加了特性,用于查找当前业务的节点实现,然后通过在首次启动创建,并在特性中设置节点名称FlowNodeName,方便后续可用于调度,Index表示同节点有多个流程,可使用index进行排序调度,当然也可以根据数据库中进行配置排序
特性代码:
```C#
///
/// 节点特性
///
public class FlowNodeAttribute : Attribute
{
///
/// 节点名称
///
public string FlowNodeName { get; set; }
///
/// 当节点下有排序实现
///
public int Index { get; set; } = 0;
///
///
///
///
public FlowNodeAttribute(string flowNodeName, int index = 0)
{
FlowNodeName = flowNodeName;
Index = index;
}
}
```
#### 2.反射创建实例
通过以上步骤,相当于实现了知道哪些节点需要执行的,那么我们通过反射加载对应的实现到内存之中,后续可通过节点调度对应实现
反射创建对象代码:
```C#
///
/// 节点对应实现字典
/// 使用线程安全字典存放,防止多线程问题
///
public static ConcurrentDictionary RealizationDictionary = new ConcurrentDictionary();
///
/// 参数对应字典
/// 使用线程安全字典存放,防止多线程问题
///
public static ConcurrentDictionary> ParameterDictionary = new ConcurrentDictionary>();
///
/// 初始化
///
private void BaseBusInit()
{
System.Reflection.Assembly[] allAssembly = AppDomain.CurrentDomain.GetAssemblies();
System.Reflection.Assembly.GetExecutingAssembly();
List realizationTypes = new List();
Assembly.GetAssembly(typeof(IEventHandler<>));
foreach (Assembly assembly in allAssembly)
{
var types = assembly.GetTypes().Where(x => (x?.BaseType?.IsGenericType ?? false) && x.GetInterfaces().Count() > 0).ToList();
realizationTypes.AddRange(types);
}
foreach (var realizationType in realizationTypes)
{
var genericArguments = realizationType.BaseType.GetGenericArguments();
if (genericArguments == null || !genericArguments.Any())
continue;
var attributions = realizationType.GetCustomAttributes(typeof(FlowNodeAttribute), false);
if (attributions == null || attributions.Length == 0)
continue;
var attribution = attributions[0] as FlowNodeAttribute;
if (attribution == null || string.IsNullOrWhiteSpace(attribution.FlowNodeName))
continue;
var flowNodeName = attribution.FlowNodeName;
var index = attribution.Index;
var reflectionClassInfo = ReflectionClassInfo.CreateInstance(realizationType, index);
if (!RealizationDictionary.ContainsKey(flowNodeName))
RealizationDictionary.TryAdd(flowNodeName, reflectionClassInfo);
var argFirst = reflectionClassInfo.GenericArguments.FirstOrDefault();
if (argFirst != null)
{
var parameterClassInfos = new List()
{
new ParameterClassInfo()
{
FlowNodeName = flowNodeName,
ReflectionClassInfo = reflectionClassInfo
}
};
var argFirstName = argFirst.FullName;
if (ParameterDictionary.ContainsKey(argFirstName))
{
List value = ParameterDictionary[argFirstName];
if (value == null)
continue;
var valueFirst = value.FirstOrDefault(x => x.FlowNodeName == flowNodeName);
if (valueFirst != null)
continue;
value.AddRange(parameterClassInfos);
ParameterDictionary[argFirstName] = value;
}
else
ParameterDictionary.TryAdd(argFirstName, parameterClassInfos);
}
}
}
```
#### 3.调度发布
经过以上步骤,那我们就可以根据传入参数进行调度了
```C#
static void Main(string[] args)
{
try
{
var baseBus = BaseBus.GetInstance();
baseBus.Publish(new TestParameter() { TestName = "123" });
baseBus.Publish(new Test2Parameter() { TestName = "234" });
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Console.WriteLine("Hello World!");
}
///
/// 消息发布
///
///
///
public void Publish(T msg) where T : Event
{
try
{
var argName = typeof(T).FullName;
if (ParameterDictionary.ContainsKey(argName))
{
List value = ParameterDictionary[argName];
if (value == null || !value.Any()) return;
var parameters = value.OrderBy(x => x.ReflectionClassInfo.Index).ToList();
foreach (var parameter in parameters)
{
var type = new Type[] { typeof(T), parameter.ReflectionClassInfo.ClassType };
this.FastInvoke(type,
x => x.ConsumerTo>(msg, parameter.FlowNodeName),
new object[] { msg, parameter.FlowNodeName });
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine($"*********调度中心数据************");
stringBuilder.AppendLine($"获取到节点{parameter.FlowNodeName}");
stringBuilder.AppendLine($"获取到节点请求参数{JsonConvert.SerializeObject(msg)}");
stringBuilder.AppendLine($"节点运行结束,状态正常");
stringBuilder.AppendLine($"*********调度中心数据************");
Console.WriteLine(stringBuilder);
}
}
//TODO:此处可以获取节点运行的情况,获取对应节点是否执行完成
//也可以根据节点自动更新节点组数组
}
catch (Exception e)
{
Console.WriteLine("节点运行异常\r\n");
///记录节点运行异常
throw e;
}
}
```
### 四、运行结果
运行结果中,我们可以看到只需要一行代码,我们可实现对应的细节实现调度,并且我们可以监控到对应实现的运行状态;
