重写了Emit构造委托的执行

This commit is contained in:
fengjiayi
2024-10-10 20:52:19 +08:00
parent ef96b353ac
commit 2d0f354895
17 changed files with 233 additions and 177 deletions

View File

@@ -27,5 +27,35 @@ namespace Serein.Library.Entity
private EmitMethodType _emitMethodType;
public Delegate EmitDelegate { get => _emitDelegate; }
public EmitMethodType EmitMethodType { get => _emitMethodType; }
public async Task<object> Invoke(object instance, object[] args)
{
object result = null;
try
{
if (EmitMethodType == EmitMethodType.HasResultTask && EmitDelegate is Func<object, object[], Task<object>> hasResultTask)
{
result = await hasResultTask(instance, args);
}
else if (EmitMethodType == EmitMethodType.Task && EmitDelegate is Func<object, object[], Task> task)
{
await task.Invoke(instance, args);
result = null;
}
else if (EmitMethodType == EmitMethodType.Func && EmitDelegate is Func<object, object[], object> func)
{
result = func.Invoke(instance, args);
}
else
{
throw new NotImplementedException("创建了非预期委托(应该不会出现)");
}
return result;
}
catch
{
throw;
}
}
}
}

View File

@@ -7,26 +7,69 @@ using System.Net.WebSockets;
namespace Serein.Library.Network.WebSocketCommunication
{
/// <summary>
/// <para>标记该类是处理模板需要获取WebSocketServer/WebSocketClient了实例后使用(Server/Client).MsgHandleHelper.AddModule()进行添加。</para>
/// <para>处理模板需要继承 ISocketHandleModule 接口否则WebSocket接受到数据时将无法进行调用相应的处理模板。</para>
/// <para>使用方式:</para>
/// <para>[AutoSocketModule(ThemeKey = "theme", DataKey = "data")]</para>
/// <para>public class PlcSocketService : ISocketHandleModule</para>
/// <para>类中方法示例void AddUser(string name,age 35)</para>
/// <para>Json示例{ "theme":"AddUser", //【ThemeKey】 </para>
/// <para> "data": { // 【DataKey】 </para>
/// <para> "name":"张三", </para>
/// <para> "age":35, } } </para>
/// <para>WebSocket中收到以上该Json时通过ThemeKey获取到"AddUser"然后找到AddUser()方法,并将"data"作为数据对象,取出相应数据作为入参(args:"张三",35)进行调用(如果有)</para>
/// <para></para>
///
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public sealed class AutoSocketModuleAttribute : Attribute
{
public string ThemeKey;
public string DataKey;
}
/// <summary>
/// <para>作用WebSocket中处理Json时将通过Json中ThemeKey 对应的内容ThemeValue自动路由到相应方法进行处理。</para>
/// <para>如果没有显式设置ThemeValue将默认使用方法名称作为ThemeValue。</para>
/// <para>如果没有显式设置IsReturnValue标记为false当方法顺利完成没有抛出异常且返回对象非null会自动转为json文本发送回去</para>
/// <para>如果返回类型为Task或Task&lt;TResult&gt;将会自动等待异步完成并获取结果无法处理Task&lt;Task&lt;TResult&gt;&gt;的情况)。</para>
/// <para>如果返回了值类型,会自动装箱为引用对象。</para>
/// <para>如果有方法执行过程中发送消息的需求,请在入参中声明以下类型的成员,调用时将传入发送消息的委托。</para>
/// <para>Action&lt;string&gt; : 发送文本内容。</para>
/// <para>Action&lt;object&gt; : 会自动将对象解析为Json字符串发送文本内容。</para>
/// <para>Action&lt;dynamic&gt; : 会自动将对象解析为Json字符串发送文本内容。</para>
/// <para>Func&lt;string,Task&gt; : 异步发送文本内容。</para>
/// <para>Func&lt;object,Task&gt; : 会自动将对象解析为Json字符串异步发送文本内容。</para>
/// <para>Func&lt;dynamic,Task&gt; : 会自动将对象解析为Json字符串异步发送文本内容。</para>
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public sealed class AutoSocketHandleAttribute : Attribute
{
/// <summary>
/// 描述Json业务字段如果不设置将默认使用方法名称。
/// </summary>
public string ThemeValue = string.Empty;
/// <summary>
/// <para>标记方法执行完成后是否需要将结果发送。</para>
/// <para>但以下情况将不会发送:</para>
/// <para>1.返回类型为void</para>
/// <para>2.返回类型为Task</para>
/// <para>3.返回了null</para>
/// <para>补充如果返回类型是Task&lt;TResult&gt;</para>
/// <para>会进行异步等待当Task结束后自动获取TResult进行发送请避免Task&lt;Task&lt;TResult&gt;&gt;诸如此类的Task泛型嵌套</para>
/// </summary>
public bool IsReturnValue = true;
//public Type DataType;
}
public class SocketHandleModel
internal class SocketHandleModel
{
public string ThemeValue { get; set; } = string.Empty;
public bool IsReturnValue { get; set; } = true;
}
[AttributeUsage(AttributeTargets.Class)]
public sealed class AutoSocketModuleAttribute : Attribute
{
public string JsonDataField;
public string JsonThemeField;
}
}

View File

@@ -15,14 +15,14 @@ using System.Threading.Tasks;
namespace Serein.Library.Network.WebSocketCommunication.Handle
{
public class MyHandleConfig
public class WebSocketHandleConfig
{
private readonly Delegate EmitDelegate;
private readonly EmitHelper.EmitMethodType EmitMethodType;
private Action<Exception, Action<object>> OnExceptionTracking;
public MyHandleConfig(SocketHandleModel model,ISocketControlBase instance, MethodInfo methodInfo, Action<Exception, Action<object>> onExceptionTracking)
internal WebSocketHandleConfig(SocketHandleModel model,ISocketHandleModule instance, MethodInfo methodInfo, Action<Exception, Action<object>> onExceptionTracking)
{
EmitMethodType = EmitHelper.CreateDynamicMethod(methodInfo,out EmitDelegate);
this.Model = model;
@@ -36,7 +36,7 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
}
private SocketHandleModel Model;
private ISocketControlBase Instance;
private ISocketHandleModule Instance;
public Guid HandleGuid { get; }
private string[] ParameterName;
private Type[] ParameterType;
@@ -51,7 +51,7 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
var argName = ParameterName[i];
if (type.IsGenericType)
{
if (type.IsAssignableFrom(typeof(Func<object, Task>)))
if (type.IsAssignableFrom(typeof(Func<object, Task>)))
{
args[i] = new Func<object, Task>(async data =>
{

View File

@@ -8,9 +8,9 @@ using System.Threading.Tasks;
namespace Serein.Library.Network.WebSocketCommunication.Handle
{
public class MyHandleModule
public class WebSocketHandleModule
{
public MyHandleModule(string ThemeJsonKey, string DataJsonKey)
public WebSocketHandleModule(string ThemeJsonKey, string DataJsonKey)
{
this.ThemeJsonKey = ThemeJsonKey;
this.DataJsonKey = DataJsonKey;
@@ -21,17 +21,17 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
public ConcurrentDictionary<string, MyHandleConfig> MyHandleConfigs = new ConcurrentDictionary<string, MyHandleConfig>();
public void AddHandleConfigs(SocketHandleModel model, ISocketControlBase instance, MethodInfo methodInfo
public ConcurrentDictionary<string, WebSocketHandleConfig> MyHandleConfigs = new ConcurrentDictionary<string, WebSocketHandleConfig>();
internal void AddHandleConfigs(SocketHandleModel model, ISocketHandleModule instance, MethodInfo methodInfo
, Action<Exception, Action<object>> onExceptionTracking)
{
if (!MyHandleConfigs.ContainsKey(model.ThemeValue))
{
var myHandleConfig = new MyHandleConfig(model,instance, methodInfo, onExceptionTracking);
var myHandleConfig = new WebSocketHandleConfig(model,instance, methodInfo, onExceptionTracking);
MyHandleConfigs[model.ThemeValue] = myHandleConfig;
}
}
public bool ResetConfig(ISocketControlBase socketControlBase)
public bool ResetConfig(ISocketHandleModule socketControlBase)
{
foreach (var kv in MyHandleConfigs.ToArray())
{

View File

@@ -20,13 +20,13 @@ using System.Security.Cryptography;
namespace Serein.Library.Network.WebSocketCommunication.Handle
{
public class SocketMsgHandleHelper
public class WebSocketMsgHandleHelper
{
/// <summary>
/// (Theme Name ,Data Name) - HandleModule
/// </summary>
public ConcurrentDictionary<(string, string), MyHandleModule> MyHandleModuleDict
= new ConcurrentDictionary<(string, string), MyHandleModule>();
public ConcurrentDictionary<(string, string), WebSocketHandleModule> MyHandleModuleDict
= new ConcurrentDictionary<(string, string), WebSocketHandleModule>();
private Action<Exception, Action<object>> _onExceptionTracking;
/// <summary>
@@ -34,18 +34,18 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
/// </summary>
public event Action<Exception, Action<object>> OnExceptionTracking;
private MyHandleModule AddMyHandleModule(string themeKeyName, string dataKeyName)
private WebSocketHandleModule AddMyHandleModule(string themeKeyName, string dataKeyName)
{
var key = (themeKeyName, dataKeyName);
if (!MyHandleModuleDict.TryGetValue(key, out var myHandleModule))
{
myHandleModule = new MyHandleModule(themeKeyName, dataKeyName);
myHandleModule = new WebSocketHandleModule(themeKeyName, dataKeyName);
MyHandleModuleDict[key] = myHandleModule;
}
return myHandleModule;
}
public void RemoteModule(ISocketControlBase socketControlBase)
public void RemoteModule(ISocketHandleModule socketControlBase)
{
var type = socketControlBase.GetType();
var moduleAttribute = type.GetCustomAttribute<AutoSocketModuleAttribute>();
@@ -53,8 +53,8 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
{
return;
}
var themeKeyName = moduleAttribute.JsonThemeField;
var dataKeyName = moduleAttribute.JsonDataField;
var themeKeyName = moduleAttribute.ThemeKey;
var dataKeyName = moduleAttribute.DataKey;
var key = (themeKeyName, dataKeyName);
if (MyHandleModuleDict.TryGetValue(key, out var myHandleModules))
{
@@ -63,7 +63,14 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
}
}
public void AddModule(ISocketControlBase socketControlBase, Action<Exception, Action<object>> onExceptionTracking)
/// <summary>
/// 添加
/// </summary>
/// <param name="socketControlBase"></param>
/// <param name="onExceptionTracking"></param>
public void AddModule(ISocketHandleModule socketControlBase, Action<Exception, Action<object>> onExceptionTracking)
{
var type = socketControlBase.GetType();
var moduleAttribute = type.GetCustomAttribute<AutoSocketModuleAttribute>();
@@ -72,8 +79,8 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
return;
}
var themeKey = moduleAttribute.JsonThemeField;
var dataKey = moduleAttribute.JsonDataField;
var themeKey = moduleAttribute.ThemeKey;
var dataKey = moduleAttribute.DataKey;
var handlemodule = AddMyHandleModule(themeKey, dataKey);
var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)

View File

@@ -8,7 +8,7 @@ using System.Threading.Tasks;
namespace Serein.Library.Network.WebSocketCommunication
{
public interface ISocketControlBase
public interface ISocketHandleModule
{
Guid HandleGuid { get; }
}

View File

@@ -14,11 +14,12 @@ using System.Threading.Tasks;
namespace Serein.Library.Network.WebSocketCommunication
{
[AutoRegister]
public class WebSocketServer
{
public SocketMsgHandleHelper MsgHandleHelper { get; } = new SocketMsgHandleHelper();
public WebSocketMsgHandleHelper MsgHandleHelper { get; } = new WebSocketMsgHandleHelper();
HttpListener listener;
private HttpListener listener;
public async Task StartAsync(string url)
{
listener = new HttpListener();

View File

@@ -4,7 +4,11 @@ using System;
namespace Serein.Library.Attributes
{
/// <summary>
/// 表示该属性为自动注入依赖项
/// <para>表示该属性为自动注入依赖项。</para>
/// <para>使用场景:构造函数中存在互相依赖的情况</para>
/// <para>例如ServiceA类构造函数中需要传入ServiceBServiceB类构造函数中也需要传入ServiceA</para>
/// <para>这种情况会导致流程启动时IOC容器无法注入构造函数并创建类型导致启动失败。</para>
/// <para>解决方法从ServiceA类的构造函数中移除ServiceB类型的入参将该类型更改为公开可见的可写属性成员ServiceB serviceB{get;set;},并在该属性上标记[AutoInjection]特性</para>
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public sealed class AutoInjectionAttribute : Attribute
@@ -25,8 +29,17 @@ namespace Serein.Library.Attributes
/// </summary>
FlowLoading,
}
/// <summary>
/// 表示该类自动注册(单例模式)
/// <para>启动流程时会将标记了该特性的类自动注册到IOC容器中从而无需手动进行注册绑定。</para>
/// <para>流程启动后IOC容器会进行5次注册绑定。</para>
/// <para>第1次注册绑定初始化所有节点所属的类[DynamicFlow]标记的类)。</para>
/// <para>第2次注册绑定※初始化所有[AutoRegister(Class=FlowInit)]的类。</para>
/// <para>第3次注册绑定调用所有Init节点后进行注册绑定。</para>
/// <para>第4次注册绑定※初始化所有[AutoRegister(Class=FlowLoading)]的类</para>
/// <para>第5次注册绑定调用所有Load节点后进行注册绑定。</para>
/// <para>需要注意的是在第1次进行注册绑定的过程中如果类的构造函数存在入参那么也会将入参自动创建实例并托管到IOC容器中。</para>
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public sealed class AutoRegisterAttribute : Attribute
@@ -39,7 +52,7 @@ namespace Serein.Library.Attributes
}
/// <summary>
/// 用来判断一个类是否需要注册并构建节点
/// <para>表示该类中存在节点信息</para>
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class DynamicFlowAttribute : Attribute
@@ -49,14 +62,21 @@ namespace Serein.Library.Attributes
Name = name;
Scan = scan;
}
/// <summary>
/// 补充名称,不影响运行流程
/// </summary>
public string Name { get; set; }
/// <summary>
/// 如果设置为false将忽略该类
/// </summary>
public bool Scan { get; set; } = true;
}
/// <summary>
/// 生成节点类型
/// <para>表示该方法将会生成节点,或是加入到流程运行中</para>
/// <para>如果是Task类型的返回值将会自动进行等待</para>
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class NodeActionAttribute : Attribute
@@ -71,10 +91,25 @@ namespace Serein.Library.Attributes
MethodTips = methodTips;
LockName = lockName;
}
/// <summary>
/// 如果设置为false时将不会生成节点信息
/// </summary>
public bool Scan;
/// <summary>
/// 类似于注释的效果
/// </summary>
public string MethodTips;
/// <summary>
/// 标记节点行为
/// </summary>
public NodeType MethodDynamicType;
/// <summary>
/// 显示标注方法返回类型,不影响运行逻辑(用于表示触发器触发后返回的数据)
/// </summary>
public Type ReturnType;
/// <summary>
/// 暂无意义
/// </summary>
public string LockName;
}