修改了无法保存项目文件的bug

This commit is contained in:
fengjiayi
2024-09-17 14:20:27 +08:00
parent e20855a076
commit afadbc5a95
36 changed files with 3023 additions and 772 deletions

View File

@@ -14,7 +14,17 @@ using Type = System.Type;
namespace Serein.Library.Core.Http namespace Serein.Library.Core.Http
{ {
/*
Router类负责解析请求的urlurl参数boby参数
根据url
web server 监听类,监听外部的请求
router 选择对应的控制器
agent 负责传入对应的参数,注入依赖
*/
/// <summary> /// <summary>
@@ -22,7 +32,6 @@ namespace Serein.Library.Core.Http
/// </summary> /// </summary>
public class Router public class Router
{ {
private readonly ConcurrentDictionary<string, bool> _controllerAutoHosting; // 存储是否实例化 private readonly ConcurrentDictionary<string, bool> _controllerAutoHosting; // 存储是否实例化
private readonly ConcurrentDictionary<string, Type> _controllerTypes; // 存储控制器类型 private readonly ConcurrentDictionary<string, Type> _controllerTypes; // 存储控制器类型
private readonly ConcurrentDictionary<string, object> _controllerInstances; // 存储控制器实例对象 private readonly ConcurrentDictionary<string, object> _controllerInstances; // 存储控制器实例对象

View File

@@ -22,7 +22,7 @@ namespace Serein.Library.Core.NodeFlow
public Task CreateTimingTask(Action action, int time = 100, int count = -1) public Task CreateTimingTask(Action action, int time = 100, int count = -1)
{ {
NodeRunCts ??= SereinIoc.GetOrInstantiate<NodeRunCts>(); NodeRunCts ??= SereinIoc.GetOrRegisterInstantiate<NodeRunCts>();
return Task.Factory.StartNew(async () => return Task.Factory.StartNew(async () =>
{ {
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)

View File

@@ -25,7 +25,7 @@ namespace Serein.Library.Framework.NodeFlow
if(NodeRunCts == null) if(NodeRunCts == null)
{ {
NodeRunCts = SereinIoc.GetOrInstantiate<NodeRunCts>(); NodeRunCts = SereinIoc.GetOrRegisterInstantiate<NodeRunCts>();
} }
return Task.Factory.StartNew(async () => return Task.Factory.StartNew(async () =>
{ {

View File

@@ -18,7 +18,7 @@
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType> <DebugType>full</DebugType>
<Optimize>false</Optimize> <Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath> <OutputPath>..\.Output\Debug\net8.0-windows7.0\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants> <DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>

View File

@@ -15,9 +15,9 @@ namespace Serein.Library.Api
public delegate void FlowRunCompleteHandler(FlowEventArgs eventArgs); public delegate void FlowRunCompleteHandler(FlowEventArgs eventArgs);
/// <summary> /// <summary>
/// 加载项目文件时成功加载了节点 /// 项目加载完成
/// </summary> /// </summary>
public delegate void LoadNodeHandler(LoadNodeEventArgs eventArgs); public delegate void ProjectLoadedHandler(ProjectLoadedEventArgs eventArgs);
/// <summary> /// <summary>
/// 加载项目文件时成功加载了DLL文件 /// 加载项目文件时成功加载了DLL文件
@@ -63,23 +63,30 @@ namespace Serein.Library.Api
/// </summary> /// </summary>
public string ErrorTips { get; protected set; } = string.Empty; public string ErrorTips { get; protected set; } = string.Empty;
} }
public class LoadNodeEventArgs : FlowEventArgs
{
public LoadNodeEventArgs(NodeInfo NodeInfo, MethodDetails MethodDetailss)
{
this.NodeInfo = NodeInfo;
this.MethodDetailss = MethodDetailss;
}
/// <summary>
/// 项目文件节点信息参数
/// </summary>
public NodeInfo NodeInfo { get; protected set; }
/// <summary>
/// 已加载在环境中的方法描述
/// </summary>
public MethodDetails MethodDetailss { get; protected set; }
}
//public class LoadNodeEventArgs : FlowEventArgs
//{
// public LoadNodeEventArgs(NodeInfo NodeInfo, MethodDetails MethodDetailss)
// {
// this.NodeInfo = NodeInfo;
// this.MethodDetailss = MethodDetailss;
// }
// /// <summary>
// /// 项目文件节点信息参数
// /// </summary>
// public NodeInfo NodeInfo { get; protected set; }
// /// <summary>
// /// 已加载在环境中的方法描述
// /// </summary>
// public MethodDetails MethodDetailss { get; protected set; }
//}
public class ProjectLoadedEventArgs : FlowEventArgs
{
public ProjectLoadedEventArgs()
{
}
}
public class LoadDLLEventArgs : FlowEventArgs public class LoadDLLEventArgs : FlowEventArgs
{ {
@@ -143,14 +150,25 @@ namespace Serein.Library.Api
public class NodeCreateEventArgs : FlowEventArgs public class NodeCreateEventArgs : FlowEventArgs
{ {
public NodeCreateEventArgs(object nodeModel) public NodeCreateEventArgs(object nodeModel, Position position)
{ {
this.NodeModel = nodeModel; this.NodeModel = nodeModel;
this.Position = position;
} }
public NodeCreateEventArgs(object nodeModel, bool isAddInRegion, string regeionGuid)
{
this.NodeModel = nodeModel;
this.RegeionGuid = regeionGuid;
this.IsAddInRegion = isAddInRegion;
}
/// <summary> /// <summary>
/// 节点Model对象目前需要手动转换对应的类型 /// 节点Model对象目前需要手动转换对应的类型
/// </summary> /// </summary>
public object NodeModel { get; private set; } public object NodeModel { get; private set; }
public Position Position { get; private set; }
public bool IsAddInRegion { get; private set; }
public string RegeionGuid { get; private set; }
} }
/// <summary> /// <summary>
@@ -189,13 +207,14 @@ namespace Serein.Library.Api
/// 新的起始节点Guid /// 新的起始节点Guid
/// </summary> /// </summary>
public string NewNodeGuid { get; private set; } public string NewNodeGuid { get; private set; }
} }
#endregion #endregion
public interface IFlowEnvironment public interface IFlowEnvironment
{ {
event FlowRunCompleteHandler OnFlowRunComplete; event FlowRunCompleteHandler OnFlowRunComplete;
event LoadNodeHandler OnLoadNode; event ProjectLoadedHandler OnProjectLoaded;
event LoadDLLHandler OnDllLoad; event LoadDLLHandler OnDllLoad;
event NodeConnectChangeHandler OnNodeConnectChange; event NodeConnectChangeHandler OnNodeConnectChange;
event NodeCreateHandler OnNodeCreate; event NodeCreateHandler OnNodeCreate;
@@ -206,13 +225,13 @@ namespace Serein.Library.Api
/// 保存当前项目 /// 保存当前项目
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
SereinOutputFileData SaveProject(); SereinProjectData SaveProject();
/// <summary> /// <summary>
/// 加载项目文件 /// 加载项目文件
/// </summary> /// </summary>
/// <param name="projectFile"></param> /// <param name="project"></param>
/// <param name="filePath"></param> /// <param name="filePath"></param>
void LoadProject(SereinOutputFileData projectFile, string filePath); void LoadProject(SereinProjectData project, string filePath);
/// <summary> /// <summary>
/// 从文件中加载Dll /// 从文件中加载Dll
/// </summary> /// </summary>
@@ -256,7 +275,7 @@ namespace Serein.Library.Api
/// </summary> /// </summary>
/// <param name="nodeBase">节点/区域/基础控件</param> /// <param name="nodeBase">节点/区域/基础控件</param>
/// <param name="methodDetails">节点绑定的方法说明(</param> /// <param name="methodDetails">节点绑定的方法说明(</param>
void CreateNode(NodeControlType nodeBase, MethodDetails methodDetails = null); void CreateNode(NodeControlType nodeBase, Position position, MethodDetails methodDetails = null);
/// <summary> /// <summary>
/// 移除两个节点之间的连接关系 /// 移除两个节点之间的连接关系
/// </summary> /// </summary>

View File

@@ -20,11 +20,11 @@ namespace Serein.Library.Api
/// <summary> /// <summary>
/// 获取或创建并注入目标类型 /// 获取或创建并注入目标类型
/// </summary> /// </summary>
T GetOrInstantiate<T>(); T GetOrRegisterInstantiate<T>();
/// <summary> /// <summary>
/// 获取或创建并注入目标类型 /// 获取或创建并注入目标类型
/// </summary> /// </summary>
object GetOrInstantiate(Type type); object GetOrRegisterInstantiate(Type type);
/// <summary> /// <summary>
/// 创建目标类型的对象, 并注入依赖项 /// 创建目标类型的对象, 并注入依赖项

View File

@@ -28,7 +28,7 @@ namespace Serein.Library.Entity
MethodName = MethodName, MethodName = MethodName,
MethodLockName = MethodLockName, MethodLockName = MethodLockName,
IsNetFramework = IsNetFramework, IsNetFramework = IsNetFramework,
ExplicitDatas = ExplicitDatas.Select(it => it.Clone()).ToArray(), ExplicitDatas = ExplicitDatas?.Select(it => it.Clone()).ToArray(),
}; };
} }

View File

@@ -1,7 +1,9 @@
using Serein.Library.Api; using Serein.Library.Api;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -9,9 +11,9 @@ namespace Serein.Library.Entity
{ {
/// <summary> /// <summary>
/// 输出文件 /// 项目输出文件
/// </summary> /// </summary>
public class SereinOutputFileData public class SereinProjectData
{ {
/// <summary> /// <summary>
/// 基础 /// 基础
@@ -54,13 +56,13 @@ namespace Serein.Library.Entity
/// 画布 /// 画布
/// </summary> /// </summary>
public FlowCanvas canvas { get; set; } public FlowCanvas Canvas { get; set; }
/// <summary> /// <summary>
/// 版本 /// 版本
/// </summary> /// </summary>
public string versions { get; set; } public string Versions { get; set; }
// 预览位置 // 预览位置
@@ -74,11 +76,11 @@ namespace Serein.Library.Entity
/// <summary> /// <summary>
/// 宽度 /// 宽度
/// </summary> /// </summary>
public float width { get; set; } public float Width { get; set; }
/// <summary> /// <summary>
/// 高度 /// 高度
/// </summary> /// </summary>
public float lenght { get; set; } public float Lenght { get; set; }
} }
/// <summary> /// <summary>
@@ -156,7 +158,7 @@ namespace Serein.Library.Entity
/// <summary> /// <summary>
/// 如果是区域控件,则会存在子项。 /// 如果是区域控件,则会存在子项。
/// </summary> /// </summary>
public NodeInfo[] ChildNodes { get; set; } public string[] ChildNodeGuids { get; set; }
/// <summary> /// <summary>
@@ -173,9 +175,9 @@ namespace Serein.Library.Entity
public class Parameterdata public class Parameterdata
{ {
public bool state { get; set; } public bool State { get; set; }
public string value { get; set; } public string Value { get; set; }
public string expression { get; set; } public string Expression { get; set; }
} }
@@ -185,8 +187,13 @@ namespace Serein.Library.Entity
/// </summary> /// </summary>
public class Position public class Position
{ {
public float X { get; set; } public Position(double x, double y)
public float Y { get; set; } {
this.X = x; this.Y = y;
}
public double X { get; set; } = 0;
public double Y { get; set; } = 0;
} }

116
Library/Http/Attribute.cs Normal file
View File

@@ -0,0 +1,116 @@
using System;
namespace Serein.Library.Http
{
/// <summary>
/// 表示参数为url中的数据Get请求中不需要显式标注
/// </summary>
[AttributeUsage(AttributeTargets.Parameter)]
public sealed class IsUrlDataAttribute : Attribute
{
}
/// <summary>
/// 表示入参参数为整个boby的数据
/// <para>
/// 例如User类型含有int id、string name字段</para>
/// <para>
/// ① Add(User user)</para>
/// <para>请求需要传入的json为
/// {"user":{
/// "id":2,
/// "name":"李志忠"}}</para>
/// <para>
/// ② Add([Boby]User user)</para>
/// <para>请求需要传入的json为
/// {"id":2,"name":"李志忠"}</para>
///
/// </summary>
[AttributeUsage(AttributeTargets.Parameter)]
public sealed class IsBobyDataAttribute : Attribute
{
}
/// <summary>
/// 表示该控制器会被自动注册与程序集同一命名空间暂时不支持运行时自动加载DLL需要手动注册
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public sealed class AutoHostingAttribute(string url = "") : Attribute
{
public string Url { get; } = url;
}
/// <summary>
/// 表示该属性为自动注入依赖项
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public sealed class AutoInjectionAttribute : Attribute
{
}
/// <summary>
/// 方法的接口类型与附加URL
/// </summary>
/// <remarks>
/// 假设UserController.Add()的WebAPI特性中
/// http是HTTP.POST
/// url被显示标明“temp”
/// 那么请求的接口是POST,URL是
/// [http://localhost:8080]/user/add/temp
/// </remarks>
/// <param name="http"></param>
/// <param name="url"></param>
[AttributeUsage(AttributeTargets.Method)]
public sealed class WebApiAttribute() : Attribute
{
public API Type ;
public string Url ;
/// <summary>
/// 方法名称不作为url的部分
/// </summary>
public bool IsUrl;
}
[AttributeUsage(AttributeTargets.Method)]
public sealed class ApiPostAttribute() : Attribute
{
public string Url;
/// <summary>
/// 方法名称不作为url的部分
/// </summary>
public bool IsUrl = true;
}
[AttributeUsage(AttributeTargets.Method)]
public sealed class ApiGetAttribute() : Attribute
{
public string Url;
/// <summary>
/// 方法名称不作为url的部分
/// </summary>
public bool IsUrl = true;
}
/*public sealed class WebApiAttribute(API http, bool isUrl = true, string url = "") : Attribute
{
public API Http { get; } = http;
public string Url { get; } = url;
/// <summary>
/// 方法名称不作为url的部分
/// </summary>
public bool IsUrl { get; } = isUrl;
}*/
public enum API
{
POST,
GET,
//PUT,
//DELETE
}
}

View File

@@ -0,0 +1,19 @@
using System;
namespace Serein.Library.Http
{
public class ControllerBase
{
public string Url { get; set; }
public string BobyData { get; set; }
public string GetLog(Exception ex)
{
return "Url : " + Url + Environment.NewLine +
"Ex : " + ex.Message + Environment.NewLine +
"Data : " + BobyData + Environment.NewLine;
}
}
}

768
Library/Http/Router.cs Normal file
View File

@@ -0,0 +1,768 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Serein.Library.Api;
using Serein.Library.Utils;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using Enum = System.Enum;
using Type = System.Type;
namespace Serein.Library.Http
{
/*
Router类负责解析请求的urlurl参数boby参数
根据url
web server 监听类,监听外部的请求
router 选择对应的控制器
agent 负责传入对应的参数,注入依赖
*/
/// <summary>
/// 路由注册与解析
/// </summary>
public class Router
{
private readonly ConcurrentDictionary<string, bool> _controllerAutoHosting; // 存储是否实例化
private readonly ConcurrentDictionary<string, Type> _controllerTypes; // 存储控制器类型
private readonly ConcurrentDictionary<string, object> _controllerInstances; // 存储控制器实例对象
private readonly ConcurrentDictionary<string, ConcurrentDictionary<string, MethodInfo>> _routes; // 用于存储路由信息
private readonly SereinIOC serviceRegistry; // 用于存储路由信息
//private Type PostRequest;
public Router(ISereinIOC serviceRegistry) // 构造函数,初始化 Router 类的新实例
{
this.serviceRegistry = serviceRegistry;
_routes = new ConcurrentDictionary<string, ConcurrentDictionary<string, MethodInfo>>(); // 初始化路由字典
_controllerAutoHosting = new ConcurrentDictionary<string, bool>(); // 初始化控制器实例对象字典
_controllerTypes = new ConcurrentDictionary<string, Type>(); // 初始化控制器实例对象字典
_controllerInstances = new ConcurrentDictionary<string, object>(); // 初始化控制器实例对象字典
foreach (API method in Enum.GetValues(typeof(API))) // 遍历 HTTP 枚举类型的所有值
{
_routes.TryAdd(method.ToString(), new ConcurrentDictionary<string, MethodInfo>()); // 初始化每种 HTTP 方法对应的路由字典
}
// 获取当前程序集
Assembly assembly = Assembly.GetExecutingAssembly();
// 获取包含“Controller”名称的类型
var controllerTypes = assembly.GetTypes()
.Where(t => t.Name.Contains("Controller"));
Type baseAttribute = typeof(AutoHostingAttribute);
Type baseController = typeof(ControllerBase);
foreach (var controllerType in controllerTypes)
{
if (controllerType.IsSubclassOf(baseController) && controllerType.IsDefined(baseAttribute))
{
// 如果属于控制器并标记了AutoHosting特性进行自动注册
AutoRegisterAutoController(controllerType);
}
else
{
continue;
}
}
}
/// <summary>
/// 自动注册 自动实例化控制器 类型
/// </summary>
/// <param name="controllerType"></param>
public void AutoRegisterAutoController(Type controllerType) // 方法声明,用于注册并实例化控制器类型
{
if (!controllerType.IsClass || controllerType.IsAbstract) return; // 如果不是类或者是抽象类,则直接返回
var autoHostingAttribute = controllerType.GetCustomAttribute<AutoHostingAttribute>();
if (autoHostingAttribute != null) {
foreach (var method in controllerType.GetMethods()) // 遍历控制器类型的所有方法
{
var apiGetAttribute = method.GetCustomAttribute<ApiGetAttribute>();
var apiPostAttribute = method.GetCustomAttribute<ApiPostAttribute>();
if( apiGetAttribute == null && apiPostAttribute == null )
{
continue;
}
WebApiAttribute webApiAttribute = new WebApiAttribute()
{
Type = apiGetAttribute != null ? API.GET : API.POST,
Url = apiGetAttribute != null ? apiGetAttribute.Url : apiPostAttribute.Url,
IsUrl = apiGetAttribute != null ? apiGetAttribute.IsUrl : apiPostAttribute.IsUrl,
};
if (apiPostAttribute != null) // 如果存在 WebAPIAttribute 属性
{
var url = AddRoutesUrl(autoHostingAttribute,
webApiAttribute,
controllerType, method);
Console.WriteLine(url);
if (url == null) continue;
_controllerAutoHosting[url] = true;
_controllerTypes[url] = controllerType;
_controllerInstances[url] = null;
}
/* var routeAttribute = method.GetCustomAttribute<WebApiAttribute>(); // 获取方法上的 WebAPIAttribute 自定义属性
if (routeAttribute != null) // 如果存在 WebAPIAttribute 属性
{
var url = AddRoutesUrl(autoHostingAttribute, routeAttribute, controllerType, method);
Console.WriteLine(url);
if (url == null) continue;
_controllerAutoHosting[url] = true;
_controllerTypes[url] = controllerType;
_controllerInstances[url] = null;
}*/
}
}
}
/// <summary>
/// 手动注册 自动实例化控制器实例
/// </summary>
public void RegisterAutoController<T>() // 方法声明,用于动态注册路由
{
Type controllerType = typeof(T); // 获取控制器实例的类型
foreach (var method in controllerType.GetMethods()) // 遍历控制器类型的所有方法
{
var apiGetAttribute = method.GetCustomAttribute<ApiGetAttribute>();
var apiPostAttribute = method.GetCustomAttribute<ApiPostAttribute>();
if (apiGetAttribute == null && apiPostAttribute == null)
{
continue;
}
WebApiAttribute webApiAttribute = new WebApiAttribute()
{
Type = apiGetAttribute != null ? API.GET : API.POST,
Url = apiGetAttribute != null ? apiGetAttribute.Url : apiPostAttribute.Url,
IsUrl = apiGetAttribute != null ? apiGetAttribute.IsUrl : apiPostAttribute.IsUrl,
};
var url = AddRoutesUrl(null, webApiAttribute, controllerType, method);
if (url == null) continue;
_controllerAutoHosting[url] = true;
_controllerTypes[url] = controllerType;
_controllerInstances[url] = null;
}
}
/// <summary>
/// 手动注册 实例持久控制器实例
/// </summary>
/// <param name="controllerInstance"></param>
public void RegisterController<TController>(TController controllerInstance) where TController : ControllerBase // 方法声明,用于动态注册路由
{
if(controllerInstance == null) return;
Type controllerType = controllerInstance.GetType(); // 获取控制器实例的类型
foreach (var method in controllerType.GetMethods()) // 遍历控制器类型的所有方法
{
var apiGetAttribute = method.GetCustomAttribute<ApiGetAttribute>();
var apiPostAttribute = method.GetCustomAttribute<ApiPostAttribute>();
if (apiGetAttribute == null && apiPostAttribute == null)
{
continue;
}
WebApiAttribute webApiAttribute = new WebApiAttribute()
{
Type = apiGetAttribute != null ? API.GET : API.POST,
Url = apiGetAttribute != null ? apiGetAttribute.Url : apiPostAttribute.Url,
IsUrl = apiGetAttribute != null ? apiGetAttribute.IsUrl : apiPostAttribute.IsUrl,
};
var url = AddRoutesUrl(null, webApiAttribute, controllerType, method);
if (url == null) continue;
_controllerInstances[url] = controllerInstance;
_controllerAutoHosting[url] = false;
}
}
/// <summary>
/// 从方法中收集路由信息
/// </summary>
/// <param name="controllerType"></param>
public string AddRoutesUrl(AutoHostingAttribute autoHostingAttribute, WebApiAttribute webAttribute, Type controllerType, MethodInfo method)
{
string controllerName;
if (autoHostingAttribute == null || string.IsNullOrWhiteSpace(autoHostingAttribute.Url))
{
controllerName = controllerType.Name.Replace("Controller", "").ToLower(); // 获取控制器名称并转换为小写
}
else
{
controllerName = autoHostingAttribute.Url;
}
var httpMethod = webAttribute.Type; // 获取 HTTP 方法
var customUrl = webAttribute.Url; // 获取自定义 URL
string url;
if (webAttribute.IsUrl)
{
if (string.IsNullOrEmpty(customUrl)) // 如果自定义 URL 为空
{
url = $"/{controllerName}/{method.Name}".ToLower(); // 构建默认 URL
}
else
{
customUrl = CleanUrl(customUrl);
url = $"/{controllerName}/{method.Name}/{customUrl}".ToLower();// 清理自定义 URL并构建新的 URL
}
_routes[httpMethod.ToString()].TryAdd(url, method); // 将 URL 和方法添加到对应的路由字典中
}
else
{
if (string.IsNullOrEmpty(customUrl)) // 如果自定义 URL 为空
{
url = $"/{controllerName}".ToLower(); // 构建默认 URL
}
else
{
customUrl = CleanUrl(customUrl);
url = $"/{controllerName}/{customUrl}".ToLower();// 清理自定义 URL并构建新的 URL
}
_routes[httpMethod.ToString()].TryAdd(url, method); // 将 URL 和方法添加到对应的路由字典中
}
return url;
}
/// <summary>
/// 收集路由信息
/// </summary>
/// <param name="controllerType"></param>
public void CollectRoutes(Type controllerType)
{
string controllerName = controllerType.Name.Replace("Controller", "").ToLower(); // 获取控制器名称并转换为小写
foreach (var method in controllerType.GetMethods()) // 遍历控制器类型的所有方法
{
var routeAttribute = method.GetCustomAttribute<WebApiAttribute>(); // 获取方法上的 WebAPIAttribute 自定义属性
if (routeAttribute != null) // 如果存在 WebAPIAttribute 属性
{
var customUrl = routeAttribute.Url; // 获取自定义 URL
string url;
if (string.IsNullOrEmpty(customUrl)) // 如果自定义 URL 为空
{
url = $"/api/{controllerName}/{method.Name}".ToLower(); // 构建默认 URL
}
else
{
customUrl = CleanUrl(customUrl);
url = $"/api/{controllerName}/{method.Name}/{customUrl}".ToLower();// 清理自定义 URL并构建新的 URL
}
var httpMethod = routeAttribute.Type; // 获取 HTTP 方法
_routes[httpMethod.ToString()].TryAdd(url, method); // 将 URL 和方法添加到对应的路由字典中
}
}
}
/// <summary>
/// 解析路由,调用对应的方法
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public async Task<bool> RouteAsync(HttpListenerContext context)
{
var request = context.Request; // 获取请求对象
var response = context.Response; // 获取响应对象
var url = request.Url; // 获取请求的 URL
var httpMethod = request.HttpMethod; // 获取请求的 HTTP 方法
var template = request.Url.AbsolutePath.ToLower();
if (!_routes[httpMethod].TryGetValue(template, out MethodInfo method))
{
return false;
}
var routeValues = GetUrlData(url); // 解析 URL 获取路由参数
ControllerBase controllerInstance;
if (!_controllerAutoHosting[template])
{
controllerInstance = (ControllerBase)_controllerInstances[template];
}
else
{
controllerInstance = (ControllerBase)serviceRegistry.Instantiate(_controllerTypes[template]);// 使用反射创建控制器实例
}
if (controllerInstance == null)
{
return false; // 未找到控制器实例
}
controllerInstance.Url = url.AbsolutePath;
object result;
switch (httpMethod) // 根据请求的 HTTP 方法执行不同的操作
{
case "GET": // 如果是 GET 请求传入方法、控制器、url参数
result = InvokeControllerMethodWithRouteValues(method, controllerInstance, routeValues);
break;
case "POST": // POST 请求传入方法、控制器、请求体内容url参数
var requestBody = await ReadRequestBodyAsync(request); // 读取请求体内容
controllerInstance.BobyData = requestBody;
var requestJObject = requestBody.FromJSON<object>();
result = InvokeControllerMethod(method, controllerInstance, requestJObject, routeValues);
break;
default:
result = null;
break;
}
Return(response, result); // 返回结果
return true;
}
public static string GetLog(string Url, string BobyData = "")
{
return Environment.NewLine +
"Url : " + Url + Environment.NewLine +
"Data : " + BobyData + Environment.NewLine;
}
/// <summary>
/// GET请求的控制器方法
/// </summary>
private object InvokeControllerMethodWithRouteValues(MethodInfo method, object controllerInstance, Dictionary<string, string> routeValues)
{
object[] parameters = GetMethodParameters(method, routeValues);
return InvokeMethod(method, controllerInstance, parameters);
}
private static readonly Dictionary<MethodInfo, ParameterInfo[]> methodParameterCache = new Dictionary<MethodInfo, ParameterInfo[]>();
/// <summary>
/// POST请求的调用控制器方法
/// </summary>
public object InvokeControllerMethod(MethodInfo method, object controllerInstance, dynamic requestData, Dictionary<string, string> routeValues)
{
object[] cachedMethodParameters;
if (!methodParameterCache.TryGetValue(method, out ParameterInfo[] parameters))
{
parameters = method.GetParameters();
}
cachedMethodParameters = new object[parameters.Length];
for (int i = 0; i < parameters.Length; i++)
{
string paramName = parameters[i].Name;
bool isUrlData = parameters[i].GetCustomAttribute(typeof(IsUrlDataAttribute)) != null;
bool isBobyData = parameters[i].GetCustomAttribute(typeof(IsBobyDataAttribute)) != null;
if (isUrlData)
{
if (!string.IsNullOrEmpty(paramName) && routeValues.TryGetValue(paramName, out string value))
{
cachedMethodParameters[i] = ConvertValue(value, parameters[i].ParameterType);
}
else
{
cachedMethodParameters[i] = null;
}
}
else if (isBobyData)
{
cachedMethodParameters[i] = ConvertValue(requestData.ToString(), parameters[i].ParameterType);
}
else
{
if (requestData.ContainsKey(paramName))
{
if (parameters[i].ParameterType == typeof(string))
{
cachedMethodParameters[i] = requestData[paramName].ToString();
}
else if (parameters[i].ParameterType == typeof(bool))
{
cachedMethodParameters[i] = requestData[paramName?.ToLower()].ToBool();
}
else if (parameters[i].ParameterType == typeof(int))
{
cachedMethodParameters[i] = requestData[paramName].ToInt();
}
else if (parameters[i].ParameterType == typeof(double))
{
cachedMethodParameters[i] = requestData[paramName].ToDouble();
}
else
{
cachedMethodParameters[i] = ConvertValue(requestData[paramName], parameters[i].ParameterType);
}
}
else
{
cachedMethodParameters[i] = null;
}
}
}
// 缓存方法和参数的映射
//methodParameterCache[method] = cachedMethodParameters;
// 调用方法
return method.Invoke(controllerInstance, cachedMethodParameters);
}
/// <summary>
/// 检查方法入参参数类型,返回对应的入参数组
/// </summary>
/// <param name="method"></param>
/// <param name="routeValues"></param>
/// <returns></returns>
private object[] GetMethodParameters(MethodInfo method, Dictionary<string, string> routeValues)
{
ParameterInfo[] methodParameters = method.GetParameters();
object[] parameters = new object[methodParameters.Length];
for (int i = 0; i < methodParameters.Length; i++)
{
string paramName = methodParameters[i].Name;
if (routeValues.TryGetValue(paramName, out string value))
{
parameters[i] = ConvertValue(value, methodParameters[i].ParameterType);
}
else
{
parameters[i] = null;
}
}
return parameters;
}
/*/// <summary>
/// 转为对应的类型
/// </summary>
/// <param name="value"></param>
/// <param name="targetType"></param>
/// <returns></returns>
private object ConvertValue(object value, Type targetType)
{
try
{
return JsonConvert.DeserializeObject(value.ToString(), targetType);
}
catch (JsonReaderException ex)
{
return value;
}
catch (JsonSerializationException ex)
{
// 如果无法转为对应的JSON对象
int startIndex = ex.Message.IndexOf("to type '") + "to type '".Length; // 查找类型信息开始的索引
int endIndex = ex.Message.IndexOf("'", startIndex); // 查找类型信息结束的索引
var typeInfo = ex.Message.Substring(startIndex, endIndex - startIndex); // 提取出错类型信息,该怎么传出去?
return null;
}
catch // (Exception ex)
{
return value;
}
}*/
/// <summary>
/// 转为对应的类型
/// </summary>
/// <param name="value"></param>
/// <param name="targetType"></param>
/// <returns></returns>
private object ConvertValue(string value, Type targetType)
{
if(targetType == typeof(string))
{
return value;
}
try
{
return JsonConvert.DeserializeObject(value.ToString(), targetType);
}
catch (JsonReaderException ex)
{
return value;
}
catch (JsonSerializationException ex)
{
// 如果无法转为对应的JSON对象
int startIndex = ex.Message.IndexOf("to type '") + "to type '".Length; // 查找类型信息开始的索引
int endIndex = ex.Message.IndexOf('\''); // 查找类型信息结束的索引
var typeInfo = ex.Message[startIndex..endIndex]; // 提取出错类型信息,该怎么传出去?
return null;
}
catch // (Exception ex)
{
return value;
}
}
/// <summary>
/// 调用控制器方法传入参数
/// </summary>
/// <param name="method">方法</param>
/// <param name="controllerInstance">控制器实例</param>
/// <param name="methodParameters">参数列表</param>
/// <returns></returns>
private static object InvokeMethod(MethodInfo method, object controllerInstance, object[] methodParameters)
{
object result = null;
try
{
result = method?.Invoke(controllerInstance, methodParameters);
}
catch (ArgumentException ex)
{
string targetType = ExtractTargetTypeFromExceptionMessage(ex.Message);
// 如果方法调用失败
result = new
{
error = $"函数签名类型[{targetType}]不符合",
};
}
catch (JsonSerializationException ex)
{
// 查找类型信息开始的索引
int startIndex = ex.Message.IndexOf("to type '") + "to type '".Length;
// 查找类型信息结束的索引
int endIndex = ex.Message.IndexOf('\'');
// 提取类型信息
string typeInfo = ex.Message[startIndex..endIndex];
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
return result; // 调用方法并返回结果
}
/// <summary>
/// 方法声明,用于解析 URL 获取路由参数
/// </summary>
/// <param name="uri"></param>
/// <returns></returns>
private static Dictionary<string, string> GetUrlData(Uri uri)
{
Dictionary<string, string> routeValues = [];
var pathParts = uri.ToString().Split('?'); // 拆分 URL获取路径部分
if (pathParts.Length > 1) // 如果包含查询字符串
{
var queryParams = HttpUtility.ParseQueryString(pathParts[1]); // 解析查询字符串
foreach (string key in queryParams) // 遍历查询字符串的键值对
{
if (key == null) continue;
routeValues[key] = queryParams[key]; // 将键值对添加到路由参数字典中
}
}
return routeValues; // 返回路由参数字典
}
/// <summary>
/// 读取Body中的消息
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
private static async Task<string> ReadRequestBodyAsync(HttpListenerRequest request)
{
using (Stream stream = request.InputStream)
using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
{
return await reader.ReadToEndAsync();
}
}
/// <summary>
/// 返回响应消息
/// </summary>
/// <param name="response"></param>
/// <param name="msg"></param>
private static void Return(HttpListenerResponse response, dynamic msg)
{
string resultData;
if (response != null)
{
try
{
if (msg is IEnumerable && msg is not string)
{
// If msg is a collection (e.g., array or list), serialize it as JArray
resultData = JArray.FromObject(msg).ToString();
}
else
{
// Otherwise, serialize it as JObject
resultData = JObject.FromObject(msg).ToString();
}
byte[] buffer = Encoding.UTF8.GetBytes(resultData);
response.ContentLength64 = buffer.Length;
response.OutputStream.Write(buffer, 0, buffer.Length);
}
catch
{
// If serialization fails, use the original message's string representation
resultData = msg.ToString();
}
}
}
/// <summary>
/// 解析JSON
/// </summary>
/// <param name="requestBody"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
private static dynamic ParseJson(string requestBody)
{
try
{
if (string.IsNullOrWhiteSpace(requestBody))
{
throw new Exception("Invalid JSON format");
}
return JObject.Parse(requestBody);
}
catch
{
throw new Exception("Invalid JSON format");
}
}
/// <summary>
/// 修正方法特性中的URL格式
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
private static string CleanUrl(string url)
{
while (url.Length > 0 && url[0] == '/') // 去除开头的斜杠
{
url = url[1..];
}
while (url.Length > 0 && url[^1] == '/') // 去除末尾的斜杠
{
url = url[..^1];
}
for (int i = 0; i < url.Length - 1; i++) // 去除连续的斜杠
{
if (url[i] == '/' && url[i + 1] == '/')
{
url = url.Remove(i, 1);
i--;
}
}
return url; // 返回清理后的 URL
}
/// <summary>
/// 从控制器调用方法的异常中获取出出错类型的信息
/// </summary>
/// <param name="errorMessage"></param>
/// <returns></returns>
public static string ExtractTargetTypeFromExceptionMessage(string errorMessage)
{
string targetText = "为类型“";
int startIndex = errorMessage.IndexOf(targetText);
if (startIndex != -1)
{
startIndex += targetText.Length;
int endIndex = errorMessage.IndexOf('\'');
if (endIndex != -1)
{
return errorMessage[startIndex..endIndex];
}
}
return null;
}
}
}

View File

@@ -0,0 +1,190 @@
using Serein.Library.Api;
using Serein.Library.Utils;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
namespace Serein.Library.Http
{
/// <summary>
/// HTTP接口监听类
/// </summary>
public class WebServer
{
private readonly HttpListener listener; // HTTP 监听器
private Router router; // 路由器
private readonly RequestLimiter requestLimiter; //接口防刷
public WebServer()
{
listener = new HttpListener();
requestLimiter = new RequestLimiter(5, 8);
}
// 启动服务器
public WebServer Start(string prefixe, ISereinIOC serviceContainer)
{
try
{
router = new Router(serviceContainer);
if (listener.IsListening)
{
return this;
}
if (!prefixe.Substring(prefixe.Length - 1, 1).Equals(@"/"))
{
prefixe += @"/";
}
listener.Prefixes.Add(prefixe); // 添加监听前缀
listener.Start(); // 开始监听
Console.WriteLine($"开始监听:{prefixe}");
Task.Run(async () =>
{
while (listener.IsListening)
{
var context = await listener.GetContextAsync(); // 获取请求上下文
_ = Task.Run(() => ProcessRequestAsync(context)); // 处理请求)
}
});
return this;
}
catch (HttpListenerException ex) when (ex.ErrorCode == 183)
{
return this;
}
}
/// <summary>
/// 处理请求
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
private async Task ProcessRequestAsync(HttpListenerContext context)
{
// 添加CORS头部
context.Response.Headers.Add("Access-Control-Allow-Origin", "*");
context.Response.Headers.Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
context.Response.Headers.Add("Access-Control-Allow-Headers", "Content-Type");
// 处理OPTIONS预检请求
if (context.Request.HttpMethod == "OPTIONS")
{
context.Response.StatusCode = (int)HttpStatusCode.OK;
context.Response.Close();
return;
}
var isPass = await router.RouteAsync(context); // 路由解析
if (isPass)
{
context.Response.StatusCode = (int)HttpStatusCode.OK;
context.Response.Close(); // 关闭响应
}
else
{
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
context.Response.Close(); // 关闭响应
}
//var isPass = requestLimiter.AllowRequest(context.Request);
//if (isPass)
//{
// // 如果路由没有匹配,返回 404
// router.RouteAsync(context); // 路由解析
//}
//else
//{
// context.Response.StatusCode = (int)HttpStatusCode.NotFound; // 返回 404 错误
// context.Response.Close(); // 关闭响应
//}
// var request = context.Request;
// 获取远程终结点信息
//var remoteEndPoint = context.Request.RemoteEndPoint;
//// 获取用户的IP地址和端口
//IPAddress ipAddress = remoteEndPoint.Address;
//int port = remoteEndPoint.Port;
//Console.WriteLine("外部连接:" + ipAddress.ToString() + ":" + port);
}
// 停止服务器
public void Stop()
{
if (listener.IsListening)
{
listener?.Stop(); // 停止监听
listener?.Close(); // 关闭监听器
}
}
public void RegisterAutoController<T>()
{
//var instance = Activator.CreateInstance(typeof(T));
router.RegisterAutoController<T>();
}
/*public void RegisterRoute<T>(T controllerInstance)
{
router.RegisterRoute(controllerInstance);
}*/
}
/// <summary>
/// 判断访问接口的频次是否正常
/// </summary>
public class RequestLimiter(int seconds, int maxRequests)
{
private readonly ConcurrentDictionary<string, Queue<DateTime>> requestHistory = new ();
private readonly TimeSpan interval = TimeSpan.FromSeconds(seconds);
private readonly int maxRequests = maxRequests;
/// <summary>
/// 判断访问接口的频次是否正常
/// </summary>
/// <returns></returns>
public bool AllowRequest(HttpListenerRequest request)
{
var clientIp = request.RemoteEndPoint.Address.ToString();
var clientPort = request.RemoteEndPoint.Port;
var clientKey = clientIp + ":" + clientPort;
var now = DateTime.Now;
// 尝试从字典中获取请求队列,不存在则创建新的队列
var requests = requestHistory.GetOrAdd(clientKey, new Queue<DateTime>());
lock (requests)
{
// 移除超出时间间隔的请求记录
while (requests.Count > 0 && now - requests.Peek() > interval)
{
requests.Dequeue();
}
// 如果请求数超过限制,拒绝请求
if (requests.Count >= maxRequests)
{
return false;
}
// 添加当前请求时间,并允许请求
requests.Enqueue(now);
}
return true;
}
}
}

View File

@@ -1,12 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;net461</TargetFrameworks> <TargetFrameworks>netstandard2.0</TargetFrameworks>
<BaseOutputPath>D:\Project\C#\DynamicControl\SereinFlow\.Output</BaseOutputPath>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Remove="Http\**" />
<Compile Remove="Utils\SerinExpression\**" /> <Compile Remove="Utils\SerinExpression\**" />
<EmbeddedResource Remove="Http\**" />
<EmbeddedResource Remove="Utils\SerinExpression\**" /> <EmbeddedResource Remove="Utils\SerinExpression\**" />
<None Remove="Http\**" />
<None Remove="Utils\SerinExpression\**" /> <None Remove="Utils\SerinExpression\**" />
</ItemGroup> </ItemGroup>

View File

@@ -1,5 +1,6 @@
using Serein.Library.Api; using Serein.Library.Api;
using Serein.Library.Attributes; using Serein.Library.Attributes;
using Serein.Library.Web;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
@@ -14,41 +15,46 @@ namespace Serein.Library.Utils
public class SereinIOC : ISereinIOC public class SereinIOC : ISereinIOC
{ {
/// <summary> /// <summary>
/// 实例集合 /// 类型集合,暂放待实例化的类型,完成实例化之后移除
/// </summary>
private readonly ConcurrentDictionary<string, Type> _typeMappings;
/// <summary>
/// 实例集合(包含已完成注入、未完成注入的对象实例,计划在未来的版本中区分:)
/// </summary> /// </summary>
private readonly ConcurrentDictionary<string, object> _dependencies; private readonly ConcurrentDictionary<string, object> _dependencies;
/// <summary> /// <summary>
/// 未完成注入的实例集合。 /// 未完成注入的实例集合。
/// 键:需要的类型名称 /// 键:需要的类型名称
/// 值:对象实例(存储对象 /// 值:元组(对象实例,对象的属性
/// </summary> /// </summary>
private readonly ConcurrentDictionary<string, List<(object,PropertyInfo)>> _unfinishedDependencies; private readonly ConcurrentDictionary<string, List<(object,PropertyInfo)>> _unfinishedDependencies;
/// <summary>
/// 类型集合
/// </summary>
private readonly ConcurrentDictionary<string, Type> _typeMappings;
/// <summary> /// <summary>
/// 待实例化的类型 /// 待实例化的类型
/// </summary> /// </summary>
private readonly List<Type> _waitingForInstantiation; // private readonly List<Type> _waitingForInstantiation;
public SereinIOC() public SereinIOC()
{ {
// 首先注册自己 // 首先注册自己
_dependencies = new ConcurrentDictionary<string, object> _dependencies = new ConcurrentDictionary<string, object>();
{ _typeMappings = new ConcurrentDictionary<string, Type>();
[typeof(ISereinIOC).FullName] = this
};
_typeMappings = new ConcurrentDictionary<string, Type>
{
[typeof(ISereinIOC).FullName] = typeof(ISereinIOC)
};
_unfinishedDependencies = new ConcurrentDictionary<string, List<(object, PropertyInfo)>>(); _unfinishedDependencies = new ConcurrentDictionary<string, List<(object, PropertyInfo)>>();
_waitingForInstantiation = new List<Type>();
} }
public void InitRegister()
{
_dependencies[typeof(ISereinIOC).FullName] = this;
Register<IRouter, Router>();
/*foreach (var type in _typeMappings.Values)
{
Register(type);
}
Build();*/
}
#region #region
@@ -59,7 +65,7 @@ namespace Serein.Library.Utils
/// <param name="parameters">参数</param> /// <param name="parameters">参数</param>
public ISereinIOC Register(Type type, params object[] parameters) public ISereinIOC Register(Type type, params object[] parameters)
{ {
RegisterType(type.FullName, type); RegisterType(type?.FullName, type);
return this; return this;
} }
/// <summary> /// <summary>
@@ -74,7 +80,11 @@ namespace Serein.Library.Utils
return this; return this;
} }
/// <summary>
/// 注册接口类型
/// </summary>
/// <param name="type">目标类型</param>
/// <param name="parameters">参数</param>
public ISereinIOC Register<TService, TImplementation>(params object[] parameters) public ISereinIOC Register<TService, TImplementation>(params object[] parameters)
where TImplementation : TService where TImplementation : TService
{ {
@@ -88,7 +98,7 @@ namespace Serein.Library.Utils
/// <summary> /// <summary>
/// 尝试从容器中获取对象,如果不存在目标类型的对象,则将类型信息登记到容器,并实例化注入依赖项。 /// 尝试从容器中获取对象,如果不存在目标类型的对象,则将类型信息登记到容器,并实例化注入依赖项。
/// </summary> /// </summary>
public object GetOrInstantiate(Type type) public object GetOrRegisterInstantiate(Type type)
{ {
// 尝试从容器中获取对象 // 尝试从容器中获取对象
if (!_dependencies.TryGetValue(type.FullName, out object value)) if (!_dependencies.TryGetValue(type.FullName, out object value))
@@ -103,7 +113,7 @@ namespace Serein.Library.Utils
/// <summary> /// <summary>
/// 尝试从容器中获取对象,如果不存在目标类型的对象,则将类型信息登记到容器,并实例化注入依赖项。 /// 尝试从容器中获取对象,如果不存在目标类型的对象,则将类型信息登记到容器,并实例化注入依赖项。
/// </summary> /// </summary>
public T GetOrInstantiate<T>() public T GetOrRegisterInstantiate<T>()
{ {
var value = Instantiate(typeof(T)); var value = Instantiate(typeof(T));
return (T)value; return (T)value;
@@ -144,7 +154,7 @@ namespace Serein.Library.Utils
_unfinishedDependencies?.Clear(); _unfinishedDependencies?.Clear();
_typeMappings?.Clear(); _typeMappings?.Clear();
_dependencies?.Clear(); _dependencies?.Clear();
_waitingForInstantiation?.Clear(); // _waitingForInstantiation?.Clear();
return this; return this;
} }
@@ -154,6 +164,7 @@ namespace Serein.Library.Utils
/// <returns></returns> /// <returns></returns>
public ISereinIOC Build() public ISereinIOC Build()
{ {
InitRegister();
// 遍历已注册类型 // 遍历已注册类型
foreach (var type in _typeMappings.Values.ToArray()) foreach (var type in _typeMappings.Values.ToArray())
{ {
@@ -262,29 +273,29 @@ namespace Serein.Library.Utils
/// <summary> /// <summary>
/// 再次尝试注入目标实例的依赖项 /// 再次尝试注入目标实例的依赖项
/// </summary> /// </summary>
private void TryInstantiateWaitingDependencies() //private void TryInstantiateWaitingDependencies()
{ //{
foreach (var waitingType in _waitingForInstantiation.ToList()) // foreach (var waitingType in _waitingForInstantiation.ToList())
{ // {
if (_typeMappings.TryGetValue(waitingType.FullName, out var implementationType)) // if (_typeMappings.TryGetValue(waitingType.FullName, out var implementationType))
{ // {
var instance = Instantiate(implementationType); // var instance = Instantiate(implementationType);
if (instance != null) // if (instance != null)
{ // {
_dependencies[waitingType.FullName] = instance; // _dependencies[waitingType.FullName] = instance;
_waitingForInstantiation.Remove(waitingType); // _waitingForInstantiation.Remove(waitingType);
} // }
} // }
} // }
} //}
#endregion #endregion
#region run() #region run()
public ISereinIOC Run<T>(Action<T> action) public ISereinIOC Run<T>(Action<T> action)
{ {
var service = GetOrInstantiate<T>(); var service = GetOrRegisterInstantiate<T>();
if (service != null) if (service != null)
{ {
action(service); action(service);
@@ -294,8 +305,8 @@ namespace Serein.Library.Utils
public ISereinIOC Run<T1, T2>(Action<T1, T2> action) public ISereinIOC Run<T1, T2>(Action<T1, T2> action)
{ {
var service1 = GetOrInstantiate<T1>(); var service1 = GetOrRegisterInstantiate<T1>();
var service2 = GetOrInstantiate<T2>(); var service2 = GetOrRegisterInstantiate<T2>();
action(service1, service2); action(service1, service2);
return this; return this;
@@ -303,69 +314,69 @@ namespace Serein.Library.Utils
public ISereinIOC Run<T1, T2, T3>(Action<T1, T2, T3> action) public ISereinIOC Run<T1, T2, T3>(Action<T1, T2, T3> action)
{ {
var service1 = GetOrInstantiate<T1>(); var service1 = GetOrRegisterInstantiate<T1>();
var service2 = GetOrInstantiate<T2>(); var service2 = GetOrRegisterInstantiate<T2>();
var service3 = GetOrInstantiate<T3>(); var service3 = GetOrRegisterInstantiate<T3>();
action(service1, service2, service3); action(service1, service2, service3);
return this; return this;
} }
public ISereinIOC Run<T1, T2, T3, T4>(Action<T1, T2, T3, T4> action) public ISereinIOC Run<T1, T2, T3, T4>(Action<T1, T2, T3, T4> action)
{ {
var service1 = GetOrInstantiate<T1>(); var service1 = GetOrRegisterInstantiate<T1>();
var service2 = GetOrInstantiate<T2>(); var service2 = GetOrRegisterInstantiate<T2>();
var service3 = GetOrInstantiate<T3>(); var service3 = GetOrRegisterInstantiate<T3>();
var service4 = GetOrInstantiate<T4>(); var service4 = GetOrRegisterInstantiate<T4>();
action(service1, service2, service3, service4); action(service1, service2, service3, service4);
return this; return this;
} }
public ISereinIOC Run<T1, T2, T3, T4, T5>(Action<T1, T2, T3, T4, T5> action) public ISereinIOC Run<T1, T2, T3, T4, T5>(Action<T1, T2, T3, T4, T5> action)
{ {
var service1 = GetOrInstantiate<T1>(); var service1 = GetOrRegisterInstantiate<T1>();
var service2 = GetOrInstantiate<T2>(); var service2 = GetOrRegisterInstantiate<T2>();
var service3 = GetOrInstantiate<T3>(); var service3 = GetOrRegisterInstantiate<T3>();
var service4 = GetOrInstantiate<T4>(); var service4 = GetOrRegisterInstantiate<T4>();
var service5 = GetOrInstantiate<T5>(); var service5 = GetOrRegisterInstantiate<T5>();
action(service1, service2, service3, service4, service5); action(service1, service2, service3, service4, service5);
return this; return this;
} }
public ISereinIOC Run<T1, T2, T3, T4, T5, T6>(Action<T1, T2, T3, T4, T5, T6> action) public ISereinIOC Run<T1, T2, T3, T4, T5, T6>(Action<T1, T2, T3, T4, T5, T6> action)
{ {
var service1 = GetOrInstantiate<T1>(); var service1 = GetOrRegisterInstantiate<T1>();
var service2 = GetOrInstantiate<T2>(); var service2 = GetOrRegisterInstantiate<T2>();
var service3 = GetOrInstantiate<T3>(); var service3 = GetOrRegisterInstantiate<T3>();
var service4 = GetOrInstantiate<T4>(); var service4 = GetOrRegisterInstantiate<T4>();
var service5 = GetOrInstantiate<T5>(); var service5 = GetOrRegisterInstantiate<T5>();
var service6 = GetOrInstantiate<T6>(); var service6 = GetOrRegisterInstantiate<T6>();
action(service1, service2, service3, service4, service5, service6); action(service1, service2, service3, service4, service5, service6);
return this; return this;
} }
public ISereinIOC Run<T1, T2, T3, T4, T5, T6, T7>(Action<T1, T2, T3, T4, T5, T6, T7> action) public ISereinIOC Run<T1, T2, T3, T4, T5, T6, T7>(Action<T1, T2, T3, T4, T5, T6, T7> action)
{ {
var service1 = GetOrInstantiate<T1>(); var service1 = GetOrRegisterInstantiate<T1>();
var service2 = GetOrInstantiate<T2>(); var service2 = GetOrRegisterInstantiate<T2>();
var service3 = GetOrInstantiate<T3>(); var service3 = GetOrRegisterInstantiate<T3>();
var service4 = GetOrInstantiate<T4>(); var service4 = GetOrRegisterInstantiate<T4>();
var service5 = GetOrInstantiate<T5>(); var service5 = GetOrRegisterInstantiate<T5>();
var service6 = GetOrInstantiate<T6>(); var service6 = GetOrRegisterInstantiate<T6>();
var service7 = GetOrInstantiate<T7>(); var service7 = GetOrRegisterInstantiate<T7>();
action(service1, service2, service3, service4, service5, service6, service7); action(service1, service2, service3, service4, service5, service6, service7);
return this; return this;
} }
public ISereinIOC Run<T1, T2, T3, T4, T5, T6, T7, T8>(Action<T1, T2, T3, T4, T5, T6, T7, T8> action) public ISereinIOC Run<T1, T2, T3, T4, T5, T6, T7, T8>(Action<T1, T2, T3, T4, T5, T6, T7, T8> action)
{ {
var service1 = GetOrInstantiate<T1>(); var service1 = GetOrRegisterInstantiate<T1>();
var service2 = GetOrInstantiate<T2>(); var service2 = GetOrRegisterInstantiate<T2>();
var service3 = GetOrInstantiate<T3>(); var service3 = GetOrRegisterInstantiate<T3>();
var service4 = GetOrInstantiate<T4>(); var service4 = GetOrRegisterInstantiate<T4>();
var service5 = GetOrInstantiate<T5>(); var service5 = GetOrRegisterInstantiate<T5>();
var service6 = GetOrInstantiate<T6>(); var service6 = GetOrRegisterInstantiate<T6>();
var service7 = GetOrInstantiate<T7>(); var service7 = GetOrRegisterInstantiate<T7>();
var service8 = GetOrInstantiate<T8>(); var service8 = GetOrRegisterInstantiate<T8>();
action(service1, service2, service3, service4, service5, service6, service7, service8); action(service1, service2, service3, service4, service5, service6, service7, service8);
return this; return this;
} }

86
Library/Web/Attribute.cs Normal file
View File

@@ -0,0 +1,86 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Library.Web
{
/// <summary>
/// 表示参数为url中的数据
/// </summary>
public class UrlAttribute : Attribute
{
}
/// <summary>
/// 表示入参参数为整个boby的数据
/// <para>
/// 例如User类型含有int id、string name字段</para>
/// <para>
/// ① Add(User user)</para>
/// <para>请求需要传入的json为
/// {"user":{
/// "id":2,
/// "name":"李志忠"}}</para>
/// <para>
/// ② Add([Boby]User user)</para>
/// <para>请求需要传入的json为
/// {"id":2,"name":"李志忠"}</para>
///
/// </summary>
public class BobyAttribute : Attribute
{
}
/// <summary>
/// 自动注册控制器
/// </summary>
public class AutoHostingAttribute : Attribute
{
public string Url { get; }
public AutoHostingAttribute(string url = "")
{
this.Url = url;
}
}
/// <summary>
/// 方法的接口类型与附加URL
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public class WebApiAttribute : Attribute
{
public API Http; // HTTP 请求类型
public string Url; // URL 路径
/// <summary>
/// 方法名称不作为url的部分
/// </summary>
public bool IsUrl;
/// <summary>
/// 假设UserController.Add()的WebAPI特性中
/// http是HTTP.POST
/// url被显示标明“temp”
/// 那么请求的接口是POST,URL是
/// [http://localhost:8080]/user/add/temp
/// </summary>
/// <param name="http"></param>
/// <param name="url"></param>
public WebApiAttribute(API http = API.POST, bool isUrl = false, string url = "")
{
Http = http;
Url = url;
IsUrl = isUrl;
}
}
public enum API
{
POST,
GET,
//PUT,
//DELETE
}
}

View File

@@ -0,0 +1,19 @@
using System;
namespace Serein.Library.Web
{
public class ControllerBase
{
// [AutoInjection]
// public ILoggerService loggerService { get; set; }
public string Url { get; set; }
public string BobyData { get; set; }
public string GetLog(Exception ex)
{
return "Url : " + Url + Environment.NewLine +
"Ex : " + ex + Environment.NewLine +
"Data : " + BobyData + Environment.NewLine;
}
}
}

650
Library/Web/Router.cs Normal file
View File

@@ -0,0 +1,650 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Serein.Library.Api;
using Serein.Library.Attributes;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using Enum = System.Enum;
using Type = System.Type;
namespace Serein.Library.Web
{
public interface IRouter
{
bool RegisterController(Type controllerType);
Task ProcessingAsync(HttpListenerContext context);
}
/// <summary>
/// 路由注册与解析
/// </summary>
public class Router : IRouter
{
/// <summary>
/// 控制器实例对象的类型,每次调用都会重新实例化,[Url - ControllerType]
/// </summary>
private readonly ConcurrentDictionary<string, Type> _controllerTypes; // 存储控制器类型
/// <summary>
/// 用于存储路由信息,[GET|POST - [Url - Method]]
/// </summary>
private readonly ConcurrentDictionary<string, ConcurrentDictionary<string, MethodInfo>> _routes;
[AutoInjection]
public ISereinIOC SereinIOC { get; set; } // 用于存储路由信息
// private readonly ILoggerService loggerService; // 用于存储路由信息
//private Type PostRequest;
public Router()
{
_routes = new ConcurrentDictionary<string, ConcurrentDictionary<string, MethodInfo>>(); // 初始化路由字典
_controllerTypes = new ConcurrentDictionary<string, Type>(); // 初始化控制器实例对象字典
foreach (API method in Enum.GetValues(typeof(API))) // 遍历 HTTP 枚举类型的所有值
{
_routes.TryAdd(method.ToString(), new ConcurrentDictionary<string, MethodInfo>()); // 初始化每种 HTTP 方法对应的路由字典
}
Type baseAttribute = typeof(AutoHostingAttribute);
Type baseController = typeof(ControllerBase);
// 获取当前程序集
Assembly assembly = Assembly.GetExecutingAssembly();
// 获取包含“Controller”名称的类型
var controllerTypes = assembly.GetTypes().Where(type => type.IsSubclassOf(baseController) // 控制器子类
&& type.IsDefined(baseAttribute) // 包含特性
&& type.Name.Contains("Controller"));
foreach (var controllerType in controllerTypes)
{
RegisterController(controllerType);
}
}
/// <summary>
/// 在外部调用API后解析路由调用对应的方法
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public async Task ProcessingAsync(HttpListenerContext context)
{
var request = context.Request; // 获取请求对象
var response = context.Response; // 获取响应对象
var url = request.Url; // 获取请求的 URL
var httpMethod = request.HttpMethod; // 获取请求的 HTTP 方法
var template = request.Url.AbsolutePath.ToLower();
if (!_routes[httpMethod].TryGetValue(template, out MethodInfo method))
{
return;
}
var routeValues = GetUrlData(url); // 解析 URL 获取路由参数
ControllerBase controllerInstance = (ControllerBase)SereinIOC.Instantiate(_controllerTypes[template]);// 使用反射创建控制器实例
if (controllerInstance == null)
{
return; // 未找到控制器实例
}
controllerInstance.Url = url.AbsolutePath;
try
{
object result;
switch (httpMethod) // 根据请求的 HTTP 方法执行不同的操作
{
case "GET": // 如果是 GET 请求传入方法、控制器、url参数
// loggerService.Information(GetLog(template));
result = InvokeControllerMethodWithRouteValues(method, controllerInstance, routeValues);
break;
case "POST": // POST 请求传入方法、控制器、请求体内容url参数
var requestBody = await ReadRequestBodyAsync(request); // 读取请求体内容
controllerInstance.BobyData = requestBody;
var requestJObject = JObject.Parse(requestBody); //requestBody.FromJSON<JObject>();
// loggerService.Information(GetLog(template, requestBody));
result = InvokeControllerMethod(method, controllerInstance, requestJObject, routeValues);
break;
default:
result = null;
break;
}
Return(response, result); // 返回结果
}
catch (Exception ex)
{
response.StatusCode = (int)HttpStatusCode.NotFound; // 返回 404 错误
Return(response, ex.Message); // 返回结果
}
}
/// <summary>
/// 自动注册并实例化控制器类型
/// </summary>
/// <param name="controllerType"></param>
public bool RegisterController(Type controllerType) // 方法声明,用于注册并实例化控制器类型
{
if (!controllerType.IsClass || controllerType.IsAbstract) return false; // 如果不是类或者是抽象类,则直接返回
var autoHostingAttribute = controllerType.GetCustomAttribute<AutoHostingAttribute>();
foreach (var method in controllerType.GetMethods()) // 遍历控制器类型的所有方法
{
var routeAttribute = method.GetCustomAttribute<WebApiAttribute>(); // 获取方法上的 WebAPIAttribute 自定义属性
if (routeAttribute != null) // 如果存在 WebAPIAttribute 属性
{
var url = AddRoutesUrl(autoHostingAttribute, routeAttribute, controllerType, method);
Console.WriteLine(url);
if (url == null) continue;
_controllerTypes[url] = controllerType;
}
}
return true;
}
#region Get Post对应的方法
/// <summary>
/// GET请求的控制器方法
/// </summary>
private object InvokeControllerMethodWithRouteValues(MethodInfo method, object controllerInstance, Dictionary<string, string> routeValues)
{
object[] parameters = GetMethodParameters(method, routeValues);
return InvokeMethod(method, controllerInstance, parameters);
}
/// <summary>
/// GET请求调用控制器方法传入参数
/// </summary>
/// <param name="method">方法</param>
/// <param name="controllerInstance">控制器实例</param>
/// <param name="methodParameters">参数列表</param>
/// <returns></returns>
private object InvokeMethod(MethodInfo method, object controllerInstance, object[] methodParameters)
{
object result = null;
try
{
result = method?.Invoke(controllerInstance, methodParameters);
}
catch (ArgumentException ex)
{
string targetType = ExtractTargetTypeFromExceptionMessage(ex.Message);
// 如果方法调用失败
result = new
{
error = $"函数签名类型[{targetType}]不符合",
};
}
catch (JsonSerializationException ex)
{
// 查找类型信息开始的索引
int startIndex = ex.Message.IndexOf("to type '") + "to type '".Length;
// 查找类型信息结束的索引
int endIndex = ex.Message.IndexOf("'", startIndex);
// 提取类型信息
string typeInfo = ex.Message.Substring(startIndex, endIndex - startIndex);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
return result; // 调用方法并返回结果
}
private readonly Dictionary<MethodInfo, ParameterInfo[]> methodParameterCache = new Dictionary<MethodInfo, ParameterInfo[]>();
/// <summary>
/// POST请求调用控制器方法传入参数
/// </summary>
public object InvokeControllerMethod(MethodInfo method, object controllerInstance, JObject requestData, Dictionary<string, string> routeValues)
{
if (requestData == null) return null;
ParameterInfo[] parameters;
object[] cachedMethodParameters;
if (!methodParameterCache.TryGetValue(method, out parameters))
{
parameters = method.GetParameters();
}
cachedMethodParameters = new object[parameters.Length];
for (int i = 0; i < parameters.Length; i++)
{
string paramName = parameters[i].Name;
bool isUrlData = parameters[i].GetCustomAttribute(typeof(UrlAttribute)) != null;
bool isBobyData = parameters[i].GetCustomAttribute(typeof(BobyAttribute)) != null;
if (isUrlData)
{
if (routeValues.ContainsKey(paramName))
{
cachedMethodParameters[i] = ConvertValue(routeValues[paramName], parameters[i].ParameterType);
}
else
{
cachedMethodParameters[i] = null;
}
}
else if (isBobyData)
{
cachedMethodParameters[i] = ConvertValue(requestData.ToString(), parameters[i].ParameterType);
}
else
{
if (requestData.ContainsKey(paramName))
{
var rd = requestData[paramName];
if (parameters[i].ParameterType == typeof(string))
{
cachedMethodParameters[i] = rd.ToString();
}
else if (parameters[i].ParameterType == typeof(bool))
{
cachedMethodParameters[i] = rd.ToBool();
}
else if (parameters[i].ParameterType == typeof(int))
{
cachedMethodParameters[i] = rd.ToInt();
}
else if (parameters[i].ParameterType == typeof(double))
{
cachedMethodParameters[i] = rd.ToDouble();
}
else
{
cachedMethodParameters[i] = ConvertValue(rd, parameters[i].ParameterType);
}
}
else
{
cachedMethodParameters[i] = null;
}
}
}
// 缓存方法和参数的映射
//methodParameterCache[method] = cachedMethodParameters;
// 调用方法
return method.Invoke(controllerInstance, cachedMethodParameters);
}
#endregion
#region
/// <summary>
/// 读取Body中的消息
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
private async Task<string> ReadRequestBodyAsync(HttpListenerRequest request)
{
using (Stream stream = request.InputStream)
{
using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
{
return await reader.ReadToEndAsync();
}
}
}
/// <summary>
/// 检查方法入参参数类型,返回对应的入参数组
/// </summary>
/// <param name="method"></param>
/// <param name="routeValues"></param>
/// <returns></returns>
private object[] GetMethodParameters(MethodInfo method, Dictionary<string, string> routeValues)
{
ParameterInfo[] methodParameters = method.GetParameters();
object[] parameters = new object[methodParameters.Length];
for (int i = 0; i < methodParameters.Length; i++)
{
string paramName = methodParameters[i].Name;
if (routeValues.ContainsKey(paramName))
{
parameters[i] = ConvertValue(routeValues[paramName], methodParameters[i].ParameterType);
}
else
{
parameters[i] = null;
}
}
return parameters;
}
/// <summary>
/// 转为对应的类型
/// </summary>
/// <param name="value"></param>
/// <param name="targetType"></param>
/// <returns></returns>
private object ConvertValue(object value, Type targetType)
{
try
{
return JsonConvert.DeserializeObject(value.ToString(), targetType);
}
catch (JsonReaderException ex)
{
return value;
}
catch (JsonSerializationException ex)
{
// 如果无法转为对应的JSON对象
int startIndex = ex.Message.IndexOf("to type '") + "to type '".Length; // 查找类型信息开始的索引
int endIndex = ex.Message.IndexOf("'", startIndex); // 查找类型信息结束的索引
var typeInfo = ex.Message.Substring(startIndex, endIndex - startIndex); // 提取出错类型信息,该怎么传出去?
return null;
}
catch // (Exception ex)
{
return value;
}
}
/// <summary>
/// 返回响应消息
/// </summary>
/// <param name="response"></param>
/// <param name="msg"></param>
private static void Return(HttpListenerResponse response, object msg)
{
string resultData;
if (response != null)
{
try
{
if (msg is JArray jArray)
{
resultData = jArray.ToString();
}
else if (msg is JObject jObject)
{
resultData = jObject.ToString();
}
else if (msg is IEnumerable ienumerable)
{
resultData = JArray.FromObject(ienumerable).ToString();
}
else if (msg is string tmpmsg)
{
resultData = tmpmsg;
}
else
{
// 否则将其序列化为JObject
resultData = JObject.FromObject(msg).ToString();
}
//
byte[] buffer = Encoding.UTF8.GetBytes(resultData);
response.ContentLength64 = buffer.Length;
response.OutputStream.Write(buffer, 0, buffer.Length);
}
catch (Exception ex)
{
// If serialization fails, use the original message's string representation
resultData = ex.ToString();
byte[] buffer = Encoding.UTF8.GetBytes(resultData);
response.ContentLength64 = buffer.Length;
response.OutputStream.Write(buffer, 0, buffer.Length);
}
}
}
/// <summary>
/// 从方法中收集路由信息返回方法对应的url
/// </summary>
/// <param name="autoHostingAttribute">类的特性</param>
/// <param name="webAttribute">方法的特性</param>
/// <param name="controllerType">控制器类型</param>
/// <param name="method">方法信息</param>
/// <returns>方法对应的urk</returns>
private string AddRoutesUrl(AutoHostingAttribute autoHostingAttribute, WebApiAttribute webAttribute, Type controllerType, MethodInfo method)
{
string controllerName;
if (string.IsNullOrWhiteSpace(autoHostingAttribute.Url))
{
controllerName = controllerType.Name.Replace("Controller", "").ToLower(); // 获取控制器名称并转换为小写
}
else
{
controllerName = autoHostingAttribute.Url;
}
var httpMethod = webAttribute.Http; // 获取 HTTP 方法
var customUrl = webAttribute.Url; // 获取自定义 URL
string url;
if (webAttribute.IsUrl)
{
if (string.IsNullOrEmpty(customUrl)) // 如果自定义 URL 为空
{
url = $"/{controllerName}/{method.Name}".ToLower(); // 构建默认 URL
}
else
{
customUrl = CleanUrl(customUrl);
url = $"/{controllerName}/{method.Name}/{customUrl}".ToLower();// 清理自定义 URL并构建新的 URL
}
_routes[httpMethod.ToString()].TryAdd(url, method); // 将 URL 和方法添加到对应的路由字典中
}
else
{
if (string.IsNullOrEmpty(customUrl)) // 如果自定义 URL 为空
{
url = $"/{controllerName}".ToLower(); // 构建默认 URL
}
else
{
customUrl = CleanUrl(customUrl);
url = $"/{controllerName}/{customUrl}".ToLower();// 清理自定义 URL并构建新的 URL
}
_routes[httpMethod.ToString()].TryAdd(url, method); // 将 URL 和方法添加到对应的路由字典中
}
return url;
}
/// <summary>
/// 修正方法特性中的URL格式
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
private static string CleanUrl(string url)
{
while (url.Length > 0 && url[0] == '/') // 去除开头的斜杠
{
url = url.Substring(1);
}
while (url.Length > 0 && url[url.Length - 1] == '/') // 去除末尾的斜杠
{
url = url.Substring(0, url.Length - 1);
}
for (int i = 0; i < url.Length - 1; i++) // 去除连续的斜杠
{
if (url[i] == '/' && url[i + 1] == '/')
{
url = url.Remove(i, 1);
i--;
}
}
return url; // 返回清理后的 URL
}
/// <summary>
/// 方法声明,用于解析 URL 获取路由参数
/// </summary>
/// <param name="uri"></param>
/// <returns></returns>
private Dictionary<string, string> GetUrlData(Uri uri)
{
Dictionary<string, string> routeValues = new Dictionary<string, string>();
var pathParts = uri.ToString().Split('?'); // 拆分 URL获取路径部分
if (pathParts.Length > 1) // 如果包含查询字符串
{
var queryParams = HttpUtility.ParseQueryString(pathParts[1]); // 解析查询字符串
foreach (string key in queryParams) // 遍历查询字符串的键值对
{
if (key == null) continue;
routeValues[key] = queryParams[key]; // 将键值对添加到路由参数字典中
}
}
return routeValues; // 返回路由参数字典
}
/// <summary>
/// 从控制器调用方法的异常中获取出出错类型的信息
/// </summary>
/// <param name="errorMessage"></param>
/// <returns></returns>
public static string ExtractTargetTypeFromExceptionMessage(string errorMessage)
{
string targetText = "为类型“";
int startIndex = errorMessage.IndexOf(targetText);
if (startIndex != -1)
{
startIndex += targetText.Length;
int endIndex = errorMessage.IndexOf("”", startIndex);
if (endIndex != -1)
{
return errorMessage.Substring(startIndex, endIndex - startIndex);
}
}
return null;
}
#endregion
}
internal static class WebFunc
{
public static bool ToBool(this JToken token,bool defult = false)
{
var value = token?.ToString();
if (string.IsNullOrWhiteSpace(value))
{
return defult;
}
if (!bool.TryParse(value, out bool result))
{
return defult;
}
else
{
return result;
}
}
public static int ToInt(this JToken token, int defult = 0)
{
var value = token?.ToString();
if (string.IsNullOrWhiteSpace(value))
{
return defult;
}
if (!int.TryParse(value, out int result))
{
return defult;
}
else
{
return result;
}
}
public static double ToDouble(this JToken token, double defult = 0)
{
var value = token?.ToString();
if (string.IsNullOrWhiteSpace(value))
{
return defult;
}
if (!int.TryParse(value, out int result))
{
return defult;
}
else
{
return result;
}
}
}
#region
// private readonly ConcurrentDictionary<string, bool> _controllerAutoHosting; // 存储是否实例化
// private readonly ConcurrentDictionary<string, object> _controllerInstances;
//public void CollectRoutes(Type controllerType)
//{
// string controllerName = controllerType.Name.Replace("Controller", "").ToLower(); // 获取控制器名称并转换为小写
// foreach (var method in controllerType.GetMethods()) // 遍历控制器类型的所有方法
// {
// var routeAttribute = method.GetCustomAttribute<WebApiAttribute>(); // 获取方法上的 WebAPIAttribute 自定义属性
// if (routeAttribute != null) // 如果存在 WebAPIAttribute 属性
// {
// var customUrl = routeAttribute.Url; // 获取自定义 URL
// string url;
// if (string.IsNullOrEmpty(customUrl)) // 如果自定义 URL 为空
// {
// url = $"/api/{controllerName}/{method.Name}".ToLower(); // 构建默认 URL
// }
// else
// {
// customUrl = CleanUrl(customUrl);
// url = $"/api/{controllerName}/{method.Name}/{customUrl}".ToLower();// 清理自定义 URL并构建新的 URL
// }
// var httpMethod = routeAttribute.Http; // 获取 HTTP 方法
// _routes[httpMethod.ToString()].TryAdd(url, method); // 将 URL 和方法添加到对应的路由字典中
// }
// }
//}
//public void RegisterRoute<T>(T controllerInstance) // 方法声明,用于动态注册路由
//{
// Type controllerType = controllerInstance.GetType(); // 获取控制器实例的类型
// var autoHostingAttribute = controllerType.GetCustomAttribute<AutoHostingAttribute>();
// foreach (var method in controllerType.GetMethods()) // 遍历控制器类型的所有方法
// {
// var webAttribute = method.GetCustomAttribute<WebApiAttribute>(); // 获取方法上的 WebAPIAttribute 自定义属性
// if (webAttribute != null) // 如果存在 WebAPIAttribute 属性
// {
// var url = AddRoutesUrl(autoHostingAttribute, webAttribute, controllerType, method);
// if (url == null) continue;
// _controllerInstances[url] = controllerInstance;
// _controllerAutoHosting[url] = false;
// }
// }
//}
#endregion
}

View File

@@ -0,0 +1,154 @@
using Serein.Library.Api;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
namespace Serein.Library.Web
{
/// <summary>
/// HTTP接口监听类
/// </summary>
public class WebServer
{
[AutoHosting]
public IRouter Router { get; set; } // 路由器
private HttpListener listener; // HTTP 监听器
private RequestLimiter requestLimiter; //接口防刷
public WebServer()
{
listener = new HttpListener();
requestLimiter = new RequestLimiter(5, 8);
}
// 启动服务器
public WebServer Start(string prefixe)
{
if (!prefixe.Substring(prefixe.Length - 1, 1).Equals(@"/"))
{
prefixe += @"/";
}
listener.Prefixes.Add(prefixe); // 添加监听前缀
listener.Start(); // 开始监听
Task.Run(async () =>
{
while (listener.IsListening)
{
var context = await listener.GetContextAsync(); // 获取请求上下文
ProcessRequestAsync(context); // 处理请求
}
});
return this;
}
/// <summary>
/// 处理请求
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
private async void ProcessRequestAsync(HttpListenerContext context)
{
// 添加CORS头部
context.Response.Headers.Add("Access-Control-Allow-Origin", "*");
context.Response.Headers.Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
context.Response.Headers.Add("Access-Control-Allow-Headers", "Content-Type");
// 处理OPTIONS预检请求
if (context.Request.HttpMethod == "OPTIONS")
{
context.Response.StatusCode = (int)HttpStatusCode.OK;
context.Response.Close();
return;
}
var isPass = requestLimiter.AllowRequest(context.Request);
if (isPass)
{
// 如果路由没有匹配,会返回 404
await Router.ProcessingAsync(context); // 路由解析
}
else
{
context.Response.StatusCode = (int)HttpStatusCode.NotFound; // 返回 404 错误
context.Response.Close(); // 关闭响应
}
// var request = context.Request;
// 获取远程终结点信息
var remoteEndPoint = context.Request.RemoteEndPoint;
// 获取用户的IP地址和端口
IPAddress ipAddress = remoteEndPoint.Address;
int port = remoteEndPoint.Port;
Console.WriteLine("外部连接:" + ipAddress.ToString() + ":" + port);
}
// 停止服务器
public void Stop()
{
listener.Stop(); // 停止监听
listener.Close(); // 关闭监听器
}
}
/// <summary>
/// 判断访问接口的频次是否正常
/// </summary>
public class RequestLimiter
{
private readonly ConcurrentDictionary<string, Queue<DateTime>> requestHistory = new ConcurrentDictionary<string, Queue<DateTime>>();
private readonly TimeSpan interval;
private readonly int maxRequests;
public RequestLimiter(int seconds, int maxRequests)
{
this.interval = TimeSpan.FromSeconds(seconds);
this.maxRequests = maxRequests;
}
/// <summary>
/// 判断访问接口的频次是否正常
/// </summary>
/// <returns></returns>
public bool AllowRequest(HttpListenerRequest request)
{
var clientIp = request.RemoteEndPoint.Address.ToString();
var clientPort = request.RemoteEndPoint.Port;
var clientKey = clientIp + ":" + clientPort;
var now = DateTime.Now;
// 尝试从字典中获取请求队列,不存在则创建新的队列
var requests = requestHistory.GetOrAdd(clientKey, new Queue<DateTime>());
lock (requests)
{
// 移除超出时间间隔的请求记录
while (requests.Count > 0 && now - requests.Peek() > interval)
{
requests.Dequeue();
}
// 如果请求数超过限制,拒绝请求
if (requests.Count >= maxRequests)
{
return false;
}
// 添加当前请求时间,并允许请求
requests.Enqueue(now);
}
return true;
}
}
}

View File

@@ -18,15 +18,15 @@ namespace Serein.NodeFlow.Base
/// </summary> /// </summary>
public abstract partial class NodeModelBase : IDynamicFlowNode public abstract partial class NodeModelBase : IDynamicFlowNode
{ {
public abstract Parameterdata[] GetParameterdatas(); internal abstract Parameterdata[] GetParameterdatas();
public virtual NodeInfo ToInfo() internal virtual NodeInfo ToInfo()
{ {
if (MethodDetails == null) return null; // if (MethodDetails == null) return null;
var trueNodes = SuccessorNodes[ConnectionType.IsSucceed].Select(item => item.Guid); // 真分支 var trueNodes = SuccessorNodes[ConnectionType.IsSucceed].Select(item => item.Guid); // 真分支
var falseNodes = SuccessorNodes[ConnectionType.IsFail].Select(item => item.Guid);// 假分支 var falseNodes = SuccessorNodes[ConnectionType.IsFail].Select(item => item.Guid);// 假分支
var upstreamNodes = SuccessorNodes[ConnectionType.IsError].Select(item => item.Guid);// 上游分支 var errorNodes = SuccessorNodes[ConnectionType.IsError].Select(item => item.Guid);// 异常分支
var errorNodes = SuccessorNodes[ConnectionType.Upstream].Select(item => item.Guid);// 异常分支 var upstreamNodes = SuccessorNodes[ConnectionType.Upstream].Select(item => item.Guid);// 上游分支
// 生成参数列表 // 生成参数列表
Parameterdata[] parameterData = GetParameterdatas(); Parameterdata[] parameterData = GetParameterdatas();
@@ -42,9 +42,41 @@ namespace Serein.NodeFlow.Base
UpstreamNodes = upstreamNodes.ToArray(), UpstreamNodes = upstreamNodes.ToArray(),
ParameterData = parameterData.ToArray(), ParameterData = parameterData.ToArray(),
ErrorNodes = errorNodes.ToArray(), ErrorNodes = errorNodes.ToArray(),
}; };
} }
internal virtual NodeModelBase LoadInfo(NodeInfo nodeInfo)
{
var node = this;
if (node != null)
{
node.Guid = nodeInfo.Guid;
for (int i = 0; i < nodeInfo.ParameterData.Length; i++)
{
Parameterdata? pd = nodeInfo.ParameterData[i];
node.MethodDetails.ExplicitDatas[i].IsExplicitData = pd.State;
node.MethodDetails.ExplicitDatas[i].DataValue = pd.Value;
}
}
//if (control is ConditionNodeControl conditionNodeControl)
//{
// conditionNodeControl.ViewModel.IsCustomData = pd.state;
// conditionNodeControl.ViewModel.CustomData = pd.value;
// conditionNodeControl.ViewModel.Expression = pd.expression;
//}
//else if (control is ExpOpNodeControl expOpNodeControl)
//{
// expOpNodeControl.ViewModel.Expression = pd.expression;
//}
//else
//{
// node.MethodDetails.ExplicitDatas[i].IsExplicitData = pd.state;
// node.MethodDetails.ExplicitDatas[i].DataValue = pd.value;
//}
return this;
}
@@ -55,7 +87,7 @@ namespace Serein.NodeFlow.Base
/// <returns></returns> /// <returns></returns>
public async Task StartExecution(IDynamicContext context) public async Task StartExecution(IDynamicContext context)
{ {
var cts = context.SereinIoc.GetOrInstantiate<CancellationTokenSource>(); var cts = context.SereinIoc.GetOrRegisterInstantiate<CancellationTokenSource>();
Stack<NodeModelBase> stack = []; Stack<NodeModelBase> stack = [];
stack.Push(this); stack.Push(this);
@@ -66,12 +98,12 @@ namespace Serein.NodeFlow.Base
var currentNode = stack.Pop(); var currentNode = stack.Pop();
// 设置方法执行的对象 // 设置方法执行的对象
if (currentNode.MethodDetails is not null) if (currentNode.MethodDetails is not null && currentNode.MethodDetails.ActingInstanceType is not null)
{ {
// currentNode.MethodDetails.ActingInstance ??= context.SereinIoc.GetOrInstantiate(MethodDetails.ActingInstanceType); // currentNode.MethodDetails.ActingInstance ??= context.SereinIoc.GetOrInstantiate(MethodDetails.ActingInstanceType);
// currentNode.MethodDetails.ActingInstance = context.SereinIoc.GetOrInstantiate(MethodDetails.ActingInstanceType); // currentNode.MethodDetails.ActingInstance = context.SereinIoc.GetOrInstantiate(MethodDetails.ActingInstanceType);
currentNode.MethodDetails.ActingInstance = context.SereinIoc.GetOrInstantiate(currentNode.MethodDetails.ActingInstanceType); currentNode.MethodDetails.ActingInstance = context.SereinIoc.GetOrRegisterInstantiate(currentNode.MethodDetails.ActingInstanceType);
} }
// 获取上游分支,首先执行一次 // 获取上游分支,首先执行一次

View File

@@ -1,4 +1,5 @@
using Serein.Library.Api;
using Serein.Library.Api;
using Serein.Library.Attributes; using Serein.Library.Attributes;
using Serein.Library.Entity; using Serein.Library.Entity;
using Serein.Library.Enums; using Serein.Library.Enums;
@@ -8,8 +9,10 @@ using Serein.NodeFlow.Model;
using Serein.NodeFlow.Tool; using Serein.NodeFlow.Tool;
using System.Diagnostics; using System.Diagnostics;
using System.Net.Mime; using System.Net.Mime;
using System.Numerics;
using System.Reflection; using System.Reflection;
using System.Reflection.Emit; using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Text; using System.Text;
using System.Xml.Linq; using System.Xml.Linq;
using static Serein.NodeFlow.FlowStarter; using static Serein.NodeFlow.FlowStarter;
@@ -44,20 +47,25 @@ namespace Serein.NodeFlow
/// </summary> /// </summary>
public class FlowEnvironment : IFlowEnvironment public class FlowEnvironment : IFlowEnvironment
{ {
/// <summary>
/// 节点的命名空间
/// </summary>
public const string NodeSpaceName = $"{nameof(Serein)}.{nameof(Serein.NodeFlow)}.{nameof(Serein.NodeFlow.Model)}";
/// <summary> /// <summary>
/// 加载Dll /// 加载Dll
/// </summary> /// </summary>
public event LoadDLLHandler OnDllLoad; public event LoadDLLHandler OnDllLoad;
/// <summary> /// <summary>
/// 加载节点事件 /// 项目加载完成
/// </summary> /// </summary>
public event LoadNodeHandler OnLoadNode; public event ProjectLoadedHandler OnProjectLoaded;
/// <summary> /// <summary>
/// 节点连接属性改变事件 /// 节点连接属性改变事件
/// </summary> /// </summary>
public event NodeConnectChangeHandler OnNodeConnectChange; public event NodeConnectChangeHandler OnNodeConnectChange;
/// <summary> /// <summary>
/// 节点创建时间 /// 节点创建事件
/// </summary> /// </summary>
public event NodeCreateHandler OnNodeCreate; public event NodeCreateHandler OnNodeCreate;
/// <summary> /// <summary>
@@ -75,11 +83,6 @@ namespace Serein.NodeFlow
private FlowStarter? nodeFlowStarter = null; private FlowStarter? nodeFlowStarter = null;
/// <summary>
/// 节点的命名空间
/// </summary>
public const string NodeSpaceName = $"{nameof(Serein)}.{nameof(Serein.NodeFlow)}.{nameof(Serein.NodeFlow.Model)}";
/// <summary> /// <summary>
/// 一种轻量的IOC容器 /// 一种轻量的IOC容器
/// </summary> /// </summary>
@@ -103,7 +106,7 @@ namespace Serein.NodeFlow
public Dictionary<string, NodeModelBase> Nodes { get; } = []; public Dictionary<string, NodeModelBase> Nodes { get; } = [];
// public List<NodeModelBase> Regions { get; } = []; public List<NodeModelBase> Regions { get; } = [];
/// <summary> /// <summary>
/// 存放触发器节点(运行时全部调用) /// 存放触发器节点(运行时全部调用)
@@ -149,6 +152,8 @@ namespace Serein.NodeFlow
var initMethods = MethodDetailss.Where(it => it.MethodDynamicType == NodeType.Init).ToList(); var initMethods = MethodDetailss.Where(it => it.MethodDynamicType == NodeType.Init).ToList();
var loadingMethods = MethodDetailss.Where(it => it.MethodDynamicType == NodeType.Loading).ToList(); var loadingMethods = MethodDetailss.Where(it => it.MethodDynamicType == NodeType.Loading).ToList();
var exitMethods = MethodDetailss.Where(it => it.MethodDynamicType == NodeType.Exit).ToList(); var exitMethods = MethodDetailss.Where(it => it.MethodDynamicType == NodeType.Exit).ToList();
await nodeFlowStarter.RunAsync(StartNode, await nodeFlowStarter.RunAsync(StartNode,
this, this,
runMethodDetailess, runMethodDetailess,
@@ -180,18 +185,42 @@ namespace Serein.NodeFlow
} }
/// <summary>
/// 运行环节加载了项目文件,需要创建节点控件
/// </summary>
/// <param name="nodeInfo"></param>
/// <param name="methodDetailss"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
private NodeControlType GetNodeControlType(NodeInfo nodeInfo)
{
// 创建控件实例
NodeControlType controlType = nodeInfo.Type switch
{
$"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleActionNode)}" => NodeControlType.Action,// 动作节点控件
$"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleFlipflopNode)}" => NodeControlType.Flipflop, // 触发器节点控件
$"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleConditionNode)}" => NodeControlType.ExpCondition,// 条件表达式控件
$"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleExpOpNode)}" => NodeControlType.ExpOp, // 操作表达式控件
$"{NodeStaticConfig.NodeSpaceName}.{nameof(CompositeConditionNode)}" => NodeControlType.ConditionRegion, // 条件区域控件
_ => NodeControlType.None,
};
return controlType;
}
#region #region
/// <summary> /// <summary>
/// 加载项目文件 /// 加载项目文件
/// </summary> /// </summary>
/// <param name="projectFile"></param> /// <param name="project"></param>
/// <param name="filePath"></param> /// <param name="filePath"></param>
public void LoadProject(SereinOutputFileData projectFile, string filePath) public void LoadProject(SereinProjectData project, string filePath)
{ {
// 加载项目配置文件 // 加载项目配置文件
var dllPaths = projectFile.Librarys.Select(it => it.Path).ToList(); var dllPaths = project.Librarys.Select(it => it.Path).ToList();
List<MethodDetails> methodDetailss = []; List<MethodDetails> methodDetailss = [];
// 遍历依赖项中的特性注解,生成方法详情 // 遍历依赖项中的特性注解,生成方法详情
@@ -201,66 +230,127 @@ namespace Serein.NodeFlow
(var assembly, var list) = LoadAssembly(dllFilePath); (var assembly, var list) = LoadAssembly(dllFilePath);
if (assembly is not null && list.Count > 0) if (assembly is not null && list.Count > 0)
{ {
methodDetailss.AddRange(methodDetailss); // 暂存方法描述 MethodDetailss.AddRange(list); // 暂存方法描述
OnDllLoad?.Invoke(new LoadDLLEventArgs(assembly, methodDetailss)); // 通知UI创建dll面板显示 OnDllLoad?.Invoke(new LoadDLLEventArgs(assembly, list)); // 通知UI创建dll面板显示
} }
} }
// 方法加载完成,缓存到运行环境中。 // 方法加载完成,缓存到运行环境中。
MethodDetailss.AddRange(methodDetailss); //MethodDetailss.AddRange(methodDetailss);
methodDetailss.Clear(); //methodDetailss.Clear();
List<(NodeModelBase, string[])> regionChildNodes = new List<(NodeModelBase, string[])>();
List<(NodeModelBase, Position)> ordinaryNodes = new List<(NodeModelBase, Position)>();
// 加载节点 // 加载节点
foreach (var nodeInfo in projectFile.Nodes) foreach (var nodeInfo in project.Nodes)
{ {
if (TryGetMethodDetails(nodeInfo.MethodName, out MethodDetails? methodDetails)) var controlType = GetNodeControlType(nodeInfo);
if(controlType == NodeControlType.None)
{ {
OnLoadNode?.Invoke(new LoadNodeEventArgs(nodeInfo, methodDetails)); continue;
}
else
{
TryGetMethodDetails(nodeInfo.MethodName, out MethodDetails? methodDetails); // 加载项目时尝试获取方法信息
methodDetails ??= new MethodDetails();
var nodeModel = CreateNode(controlType, methodDetails);
nodeModel.LoadInfo(nodeInfo); // 创建节点model
if (nodeModel is null)
{
continue;
}
TryAddNode(nodeModel);
if(nodeInfo.ChildNodeGuids?.Length > 0)
{
regionChildNodes.Add((nodeModel,nodeInfo.ChildNodeGuids));
OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, nodeInfo.Position));
}
else
{
ordinaryNodes.Add((nodeModel, nodeInfo.Position));
}
} }
} }
// 加载区域的子项
foreach((NodeModelBase region, string[] childNodeGuids) item in regionChildNodes)
{
foreach (var childNodeGuid in item.childNodeGuids)
{
Nodes.TryGetValue(childNodeGuid, out NodeModelBase? childNode);
if (childNode is null)
{
// 节点尚未加载
continue;
}
// 存在节点
OnNodeCreate?.Invoke(new NodeCreateEventArgs(childNode, true, item.region.Guid));
}
}
// 加载节点
foreach ((NodeModelBase nodeModel, Position position) item in ordinaryNodes)
{
bool IsContinue = false;
foreach ((NodeModelBase region, string[] childNodeGuids) item2 in regionChildNodes)
{
foreach (var childNodeGuid in item2.childNodeGuids)
{
if (item.nodeModel.Guid.Equals(childNodeGuid))
{
IsContinue = true;
}
}
}
if (IsContinue) continue;
OnNodeCreate?.Invoke(new NodeCreateEventArgs(item.nodeModel, item.position));
}
// 确定节点之间的连接关系 // 确定节点之间的连接关系
foreach (var nodeInfo in projectFile.Nodes) foreach (var nodeInfo in project.Nodes)
{ {
if (!Nodes.TryGetValue(nodeInfo.Guid, out NodeModelBase fromNode)) if (!Nodes.TryGetValue(nodeInfo.Guid, out NodeModelBase? fromNode))
{ {
// 不存在对应的起始节点 // 不存在对应的起始节点
continue; continue;
} }
List<(ConnectionType, string[])> nodeGuids = [(ConnectionType.IsSucceed,nodeInfo.TrueNodes), List<(ConnectionType connectionType, string[] guids)> allToNodes = [(ConnectionType.IsSucceed,nodeInfo.TrueNodes),
(ConnectionType.IsFail, nodeInfo.FalseNodes), (ConnectionType.IsFail, nodeInfo.FalseNodes),
(ConnectionType.IsError, nodeInfo.ErrorNodes), (ConnectionType.IsError, nodeInfo.ErrorNodes),
(ConnectionType.Upstream, nodeInfo.UpstreamNodes)]; (ConnectionType.Upstream, nodeInfo.UpstreamNodes)];
List<(ConnectionType, NodeModelBase[])> nodes = nodeGuids.Where(info => info.Item2.Length > 0) List<(ConnectionType, NodeModelBase[])> fromNodes = allToNodes.Where(info => info.guids.Length > 0)
.Select(info => (info.Item1, .Select(info => (info.connectionType,
info.Item2.Select(guid => Nodes[guid]) info.guids.Select(guid => Nodes[guid])
.ToArray())) .ToArray()))
.ToList(); .ToList();
// 遍历每种类型的节点分支(四种) // 遍历每种类型的节点分支(四种)
foreach ((ConnectionType connectionType, NodeModelBase[] nodeBases) item in nodes) foreach ((ConnectionType connectionType, NodeModelBase[] toNodes) item in fromNodes)
{ {
// 遍历当前类型分支的节点(确认连接关系) // 遍历当前类型分支的节点(确认连接关系)
foreach (var node in item.nodeBases) foreach (var toNode in item.toNodes)
{ {
ConnectNode(fromNode, node, item.connectionType); // 加载时确定节点间的连接关系 ConnectNode(fromNode, toNode, item.connectionType); // 加载时确定节点间的连接关系
} }
} }
} }
SetStartNode(project.StartNode);
OnProjectLoaded?.Invoke(new ProjectLoadedEventArgs());
} }
/// <summary> /// <summary>
/// 保存项目为项目文件 /// 保存项目为项目文件
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public SereinOutputFileData SaveProject() public SereinProjectData SaveProject()
{ {
var projectData = new SereinOutputFileData() var projectData = new SereinProjectData()
{ {
Librarys = LoadedAssemblies.Select(assemblies => assemblies.ToLibrary()).ToArray(), Librarys = LoadedAssemblies.Select(assemblies => assemblies.ToLibrary()).ToArray(),
Nodes = Nodes.Values.Select(node => node.ToInfo()).Where(info => info is not null).ToArray(), Nodes = Nodes.Values.Select(node => node.ToInfo()).Where(info => info is not null).ToArray(),
@@ -268,6 +358,7 @@ namespace Serein.NodeFlow
}; };
return projectData; return projectData;
} }
/// <summary> /// <summary>
/// 从文件路径中加载DLL /// 从文件路径中加载DLL
/// </summary> /// </summary>
@@ -281,66 +372,27 @@ namespace Serein.NodeFlow
MethodDetailss.AddRange(list); MethodDetailss.AddRange(list);
OnDllLoad?.Invoke(new LoadDLLEventArgs(assembly, list)); OnDllLoad?.Invoke(new LoadDLLEventArgs(assembly, list));
} }
} }
/// <summary> /// <summary>
/// 创建节点 /// 运行时创建节点
/// </summary> /// </summary>
/// <param name="nodeBase"></param> /// <param name="nodeBase"></param>
public void CreateNode(NodeControlType nodeControlType, MethodDetails? methodDetails = null) public void CreateNode(NodeControlType nodeControlType, Position position, MethodDetails? methodDetails = null)
{ {
// 确定创建的节点类型 var nodeModel = CreateNode(nodeControlType, methodDetails);
Type? nodeType = nodeControlType switch TryAddNode(nodeModel);
{
NodeControlType.Action => typeof(SingleActionNode),
NodeControlType.Flipflop => typeof(SingleFlipflopNode),
NodeControlType.ExpOp => typeof(SingleExpOpNode),
NodeControlType.ExpCondition => typeof(SingleConditionNode),
NodeControlType.ConditionRegion => typeof(CompositeConditionNode),
_ => null
};
if (nodeType == null)
{
return;
}
// 生成实例
var nodeObj = Activator.CreateInstance(nodeType);
if (nodeObj is not NodeModelBase nodeBase)
{
return;
}
// 配置基础的属性
nodeBase.ControlType = nodeControlType;
nodeBase.Guid = Guid.NewGuid().ToString();
if (methodDetails != null)
{
var md = methodDetails.Clone();
nodeBase.DisplayName = md.MethodTips;
nodeBase.MethodDetails = md;
}
Nodes[nodeBase.Guid] = nodeBase;
// 如果是触发器,则需要添加到专属集合中
if (nodeControlType == NodeControlType.Flipflop && nodeBase is SingleFlipflopNode flipflopNode)
{
var guid = flipflopNode.Guid;
if (!FlipflopNodes.Exists(it => it.Guid.Equals(guid)))
{
FlipflopNodes.Add(flipflopNode);
}
}
// 通知UI更改 // 通知UI更改
OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeBase)); OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, position));
// 因为需要UI先布置了元素才能通知UI变更特效 // 因为需要UI先布置了元素才能通知UI变更特效
// 如果不存在流程起始控件,默认设置为流程起始控件 // 如果不存在流程起始控件,默认设置为流程起始控件
if (StartNode is null) if (StartNode is null)
{ {
SetStartNode(nodeBase); SetStartNode(nodeModel);
} }
} }
/// <summary> /// <summary>
/// 移除节点 /// 移除节点
/// </summary> /// </summary>
@@ -452,12 +504,19 @@ namespace Serein.NodeFlow
/// </summary> /// </summary>
public bool TryGetMethodDetails(string name, out MethodDetails? md) public bool TryGetMethodDetails(string name, out MethodDetails? md)
{ {
md = MethodDetailss.FirstOrDefault(it => it.MethodName == name); var isPass = false;
if (md == null) if (!string.IsNullOrEmpty(name))
{ {
md = MethodDetailss.FirstOrDefault(it => it.MethodName == name);
return md != null;
}
else
{
md = null;
return false; return false;
} }
return true;
} }
/// <summary> /// <summary>
@@ -466,6 +525,10 @@ namespace Serein.NodeFlow
/// <param name="newNodeGuid"></param> /// <param name="newNodeGuid"></param>
public void SetStartNode(string newNodeGuid) public void SetStartNode(string newNodeGuid)
{ {
if (string.IsNullOrEmpty(newNodeGuid))
{
return;
}
if (Nodes.TryGetValue(newNodeGuid, out NodeModelBase? newStartNodeModel)) if (Nodes.TryGetValue(newNodeGuid, out NodeModelBase? newStartNodeModel))
{ {
if (newStartNodeModel != null) if (newStartNodeModel != null)
@@ -531,6 +594,65 @@ namespace Serein.NodeFlow
} }
} }
/// <summary>
/// 创建节点
/// </summary>
/// <param name="nodeBase"></param>
private NodeModelBase CreateNode(NodeControlType nodeControlType,MethodDetails? methodDetails = null)
{
// 确定创建的节点类型
Type? nodeType = nodeControlType switch
{
NodeControlType.Action => typeof(SingleActionNode),
NodeControlType.Flipflop => typeof(SingleFlipflopNode),
NodeControlType.ExpOp => typeof(SingleExpOpNode),
NodeControlType.ExpCondition => typeof(SingleConditionNode),
NodeControlType.ConditionRegion => typeof(CompositeConditionNode),
_ => null
};
if (nodeType == null)
{
throw new Exception($"节点类型错误[{nodeControlType}]");
}
// 生成实例
var nodeObj = Activator.CreateInstance(nodeType);
if (nodeObj is not NodeModelBase nodeBase)
{
throw new Exception($"无法创建目标节点类型的实例[{nodeControlType}]");
}
// 配置基础的属性
nodeBase.ControlType = nodeControlType;
if (methodDetails != null)
{
var md = methodDetails.Clone();
nodeBase.DisplayName = md.MethodTips;
nodeBase.MethodDetails = md;
}
// 如果是触发器,则需要添加到专属集合中
if (nodeControlType == NodeControlType.Flipflop && nodeBase is SingleFlipflopNode flipflopNode)
{
var guid = flipflopNode.Guid;
if (!FlipflopNodes.Exists(it => it.Guid.Equals(guid)))
{
FlipflopNodes.Add(flipflopNode);
}
}
return nodeBase;
}
private bool TryAddNode(NodeModelBase nodeModel)
{
nodeModel.Guid ??= Guid.NewGuid().ToString();
Nodes[nodeModel.Guid] = nodeModel;
return true;
}
/// <summary> /// <summary>
/// 连接节点 /// 连接节点
/// </summary> /// </summary>
@@ -583,7 +705,6 @@ namespace Serein.NodeFlow
} }
fromNode.SuccessorNodes[connectionType].Add(toNode); // 添加到起始节点的子分支 fromNode.SuccessorNodes[connectionType].Add(toNode); // 添加到起始节点的子分支
toNode.PreviousNodes[connectionType].Add(fromNode); // 添加到目标节点的父分支 toNode.PreviousNodes[connectionType].Add(fromNode); // 添加到目标节点的父分支
OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNode.Guid, OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNode.Guid,
@@ -603,6 +724,9 @@ namespace Serein.NodeFlow
StartNode = newStartNode; StartNode = newStartNode;
OnStartNodeChange?.Invoke(new StartNodeChangeEventArgs(oldNodeGuid, StartNode.Guid)); OnStartNodeChange?.Invoke(new StartNodeChangeEventArgs(oldNodeGuid, StartNode.Guid));
} }
#endregion #endregion
#region #region
@@ -633,6 +757,42 @@ namespace Serein.NodeFlow
}; };
} }
public static Type? ControlTypeToModel(this NodeControlType nodeControlType )
{
// 确定创建的节点类型
Type? nodeType = nodeControlType switch
{
NodeControlType.Action => typeof(SingleActionNode),
NodeControlType.Flipflop => typeof(SingleFlipflopNode),
NodeControlType.ExpOp => typeof(SingleExpOpNode),
NodeControlType.ExpCondition => typeof(SingleConditionNode),
NodeControlType.ConditionRegion => typeof(CompositeConditionNode),
_ => null
};
return nodeType;
}
public static NodeControlType ModelToControlType(this NodeControlType nodeControlType)
{
var type = nodeControlType.GetType();
NodeControlType controlType = type switch
{
Type when type == typeof(SingleActionNode) => NodeControlType.Action,
Type when type == typeof(SingleFlipflopNode) => NodeControlType.Flipflop,
Type when type == typeof(SingleExpOpNode) => NodeControlType.ExpOp,
Type when type == typeof(SingleConditionNode) => NodeControlType.ExpCondition,
Type when type == typeof(CompositeConditionNode) => NodeControlType.ConditionRegion,
_ => NodeControlType.None,
};
return controlType;
}
public static bool NotExitPreviousNode(this SingleFlipflopNode node) public static bool NotExitPreviousNode(this SingleFlipflopNode node)
{ {
ConnectionType[] ct = [ConnectionType.IsSucceed, ConnectionType[] ct = [ConnectionType.IsSucceed,

View File

@@ -70,12 +70,12 @@ namespace Serein.NodeFlow
/// </summary> /// </summary>
/// <param name="startNode">起始节点</param> /// <param name="startNode">起始节点</param>
/// <param name="env">运行环境</param> /// <param name="env">运行环境</param>
/// <param name="runMd">环境中已加载的所有节点方法</param> /// <param name="runNodeMd">环境中已加载的所有节点方法</param>
/// <param name="flipflopNodes">触发器节点</param> /// <param name="flipflopNodes">触发器节点</param>
/// <returns></returns> /// <returns></returns>
public async Task RunAsync(NodeModelBase startNode, public async Task RunAsync(NodeModelBase startNode,
IFlowEnvironment env, IFlowEnvironment env,
List<MethodDetails> runMd, List<MethodDetails> runNodeMd,
List<MethodDetails> initMethods, List<MethodDetails> initMethods,
List<MethodDetails> loadingMethods, List<MethodDetails> loadingMethods,
List<MethodDetails> exitMethods, List<MethodDetails> exitMethods,
@@ -89,6 +89,8 @@ namespace Serein.NodeFlow
return; return;
} }
#region
// 判断使用哪一种流程上下文 // 判断使用哪一种流程上下文
var isNetFramework = true; var isNetFramework = true;
if (isNetFramework) if (isNetFramework)
@@ -99,23 +101,38 @@ namespace Serein.NodeFlow
{ {
Context = new Serein.Library.Core.NodeFlow.DynamicContext(SereinIOC, env); Context = new Serein.Library.Core.NodeFlow.DynamicContext(SereinIOC, env);
} }
#endregion
#region Ioc容器 #region Ioc容器
// 清除节点使用的对象 // 清除节点使用的对象
foreach (var nodeMd in runMd) var thisRuningMds = new List<MethodDetails>();
thisRuningMds.AddRange(runNodeMd);
thisRuningMds.AddRange(initMethods);
thisRuningMds.AddRange(loadingMethods);
thisRuningMds.AddRange(exitMethods);
// .AddRange(initMethods).AddRange(loadingMethods).a
foreach (var nodeMd in thisRuningMds)
{ {
nodeMd.ActingInstance = null; nodeMd.ActingInstance = null;
} }
SereinIOC.Reset(); // 开始运行时清空ioc中注册的实例 SereinIOC.Reset(); // 开始运行时清空ioc中注册的实例
// 初始化ioc容器中的类型对象 // 初始化ioc容器中的类型对象
foreach (var md in runMd) foreach (var md in thisRuningMds)
{ {
SereinIOC.Register(md.ActingInstanceType); if(md.ActingInstanceType != null)
{
SereinIOC.Register(md.ActingInstanceType);
}
} }
SereinIOC.Build(); SereinIOC.Build(); // 流程启动前的初始化
foreach (var md in runMd) foreach (var md in thisRuningMds)
{ {
md.ActingInstance = SereinIOC.GetOrInstantiate(md.ActingInstanceType); if (md.ActingInstanceType != null)
{
md.ActingInstance = SereinIOC.GetOrRegisterInstantiate(md.ActingInstanceType);
}
} }
//foreach (var md in flipflopNodes.Select(it => it.MethodDetails).ToArray()) //foreach (var md in flipflopNodes.Select(it => it.MethodDetails).ToArray())
@@ -124,23 +141,36 @@ namespace Serein.NodeFlow
//} //}
#endregion #endregion
#region 退
#region Node中初始化退
foreach (var md in initMethods) // 初始化 foreach (var md in initMethods) // 初始化
{ {
md.ActingInstance ??= Context.SereinIoc.GetOrInstantiate(md.ActingInstanceType); md.ActingInstance ??= Context.SereinIoc.GetOrRegisterInstantiate(md.ActingInstanceType);
} }
foreach (var md in loadingMethods) // 加载 foreach (var md in loadingMethods) // 加载
{ {
md.ActingInstance ??= Context.SereinIoc.GetOrInstantiate(md.ActingInstanceType); md.ActingInstance ??= Context.SereinIoc.GetOrRegisterInstantiate(md.ActingInstanceType);
} }
foreach (var md in exitMethods) // 初始化 foreach (var md in exitMethods) // 初始化
{ {
md.ActingInstance ??= Context.SereinIoc.GetOrInstantiate(md.ActingInstanceType); md.ActingInstance ??= Context.SereinIoc.GetOrRegisterInstantiate(md.ActingInstanceType);
} }
#endregion
#region IOC容器退
object?[]? args = [Context]; object?[]? args = [Context];
foreach (var md in initMethods) // 初始化
{
object?[]? data = [md.ActingInstance, args];
md.MethodDelegate.DynamicInvoke(data);
}
Context.SereinIoc.Build(); // 绑定初始化时注册的类型
foreach (var md in loadingMethods) // 加载
{
object?[]? data = [md.ActingInstance, args];
md.MethodDelegate.DynamicInvoke(data);
}
Context.SereinIoc.Build(); // 预防有人在加载时才注册类型,再绑定一次
ExitAction = () => ExitAction = () =>
{ {
foreach (MethodDetails? md in exitMethods) foreach (MethodDetails? md in exitMethods)
@@ -159,34 +189,18 @@ namespace Serein.NodeFlow
FlowState = RunState.Completion; FlowState = RunState.Completion;
FlipFlopState = RunState.Completion; FlipFlopState = RunState.Completion;
}; };
Context.SereinIoc.Build();
#endregion #endregion
#region IOC容器 #region
foreach (var md in initMethods) // 初始化 - 调用方法
{
object?[]? data = [md.ActingInstance, args];
md.MethodDelegate.DynamicInvoke(data);
}
Context.SereinIoc.Build();
foreach (var md in loadingMethods) // 加载
{
object?[]? data = [md.ActingInstance, args];
md.MethodDelegate.DynamicInvoke(data);
}
#endregion
// 节点任务的启动
try try
{ {
if (flipflopNodes.Count > 0) if (flipflopNodes.Count > 0)
{ {
FlipFlopState = RunState.Running; FlipFlopState = RunState.Running;
// 如果存在需要启动的触发器,则开始启动 // 如果存在需要启动的触发器,则开始启动
FlipFlopCts = SereinIOC.GetOrInstantiate<NodeRunCts>(); FlipFlopCts = SereinIOC.GetOrRegisterInstantiate<NodeRunCts>();
// 使用 TaskCompletionSource 创建未启动的触发器任务 // 使用 TaskCompletionSource 创建未启动的触发器任务
var tasks = flipflopNodes.Select(async node => var tasks = flipflopNodes.Select(async node =>
{ {
@@ -196,7 +210,7 @@ namespace Serein.NodeFlow
} }
await startNode.StartExecution(Context); await startNode.StartExecution(Context);
// 等待结束 // 等待结束
if(FlipFlopCts != null) if (FlipFlopCts != null)
{ {
while (!FlipFlopCts.IsCancellationRequested) while (!FlipFlopCts.IsCancellationRequested)
{ {
@@ -207,7 +221,8 @@ namespace Serein.NodeFlow
catch (Exception ex) catch (Exception ex)
{ {
await Console.Out.WriteLineAsync(ex.ToString()); await Console.Out.WriteLineAsync(ex.ToString());
} }
#endregion
} }
/// <summary> /// <summary>
@@ -228,7 +243,7 @@ namespace Serein.NodeFlow
object?[]? parameters = singleFlipFlopNode.GetParameters(context, md); object?[]? parameters = singleFlipFlopNode.GetParameters(context, md);
// 调用委托并获取结果 // 调用委托并获取结果
md.ActingInstance = context.SereinIoc.GetOrInstantiate(md.ActingInstanceType); md.ActingInstance = context.SereinIoc.GetOrRegisterInstantiate(md.ActingInstanceType);
IFlipflopContext flipflopContext = await func.Invoke(md.ActingInstance, parameters); IFlipflopContext flipflopContext = await func.Invoke(md.ActingInstance, parameters);

View File

@@ -19,13 +19,13 @@ namespace Serein.NodeFlow.Model
ActionNodes = actionNodes; ActionNodes = actionNodes;
} }
public override Parameterdata[] GetParameterdatas()
internal override Parameterdata[] GetParameterdatas()
{ {
return []; return [];
} }
public override NodeInfo ToInfo() internal override NodeInfo ToInfo()
{ {
if (MethodDetails == null) return null; if (MethodDetails == null) return null;
@@ -35,8 +35,8 @@ namespace Serein.NodeFlow.Model
//var errorNodes = ErrorBranch.Select(item => item.Guid);// 异常分支 //var errorNodes = ErrorBranch.Select(item => item.Guid);// 异常分支
var trueNodes = SuccessorNodes[ConnectionType.IsSucceed].Select(item => item.Guid); // 真分支 var trueNodes = SuccessorNodes[ConnectionType.IsSucceed].Select(item => item.Guid); // 真分支
var falseNodes = SuccessorNodes[ConnectionType.IsFail].Select(item => item.Guid);// 假分支 var falseNodes = SuccessorNodes[ConnectionType.IsFail].Select(item => item.Guid);// 假分支
var upstreamNodes = SuccessorNodes[ConnectionType.IsError].Select(item => item.Guid);// 上游分支 var errorNodes = SuccessorNodes[ConnectionType.IsError].Select(item => item.Guid);// 异常分支
var errorNodes = SuccessorNodes[ConnectionType.Upstream].Select(item => item.Guid);// 异常分支 var upstreamNodes = SuccessorNodes[ConnectionType.Upstream].Select(item => item.Guid);// 上游分支
// 生成参数列表 // 生成参数列表
Parameterdata[] parameterData = GetParameterdatas(); Parameterdata[] parameterData = GetParameterdatas();
@@ -51,7 +51,7 @@ namespace Serein.NodeFlow.Model
UpstreamNodes = upstreamNodes.ToArray(), UpstreamNodes = upstreamNodes.ToArray(),
ParameterData = parameterData.ToArray(), ParameterData = parameterData.ToArray(),
ErrorNodes = errorNodes.ToArray(), ErrorNodes = errorNodes.ToArray(),
ChildNodes = ActionNodes.Select(node => node.ToInfo()).ToArray(), ChildNodeGuids = ActionNodes.Select(node => node.Guid).ToArray(),
}; };
} }
} }

View File

@@ -61,14 +61,14 @@ namespace Serein.NodeFlow.Model
} }
} }
public override Parameterdata[] GetParameterdatas() internal override Parameterdata[] GetParameterdatas()
{ {
return []; return [];
} }
public override NodeInfo ToInfo() internal override NodeInfo ToInfo()
{ {
if (MethodDetails == null) return null; //if (MethodDetails == null) return null;
//var trueNodes = SucceedBranch.Select(item => item.Guid); // 真分支 //var trueNodes = SucceedBranch.Select(item => item.Guid); // 真分支
//var falseNodes = FailBranch.Select(item => item.Guid);// 假分支 //var falseNodes = FailBranch.Select(item => item.Guid);// 假分支
@@ -76,8 +76,8 @@ namespace Serein.NodeFlow.Model
//var errorNodes = ErrorBranch.Select(item => item.Guid);// 异常分支 //var errorNodes = ErrorBranch.Select(item => item.Guid);// 异常分支
var trueNodes = SuccessorNodes[ConnectionType.IsSucceed].Select(item => item.Guid); // 真分支 var trueNodes = SuccessorNodes[ConnectionType.IsSucceed].Select(item => item.Guid); // 真分支
var falseNodes = SuccessorNodes[ConnectionType.IsFail].Select(item => item.Guid);// 假分支 var falseNodes = SuccessorNodes[ConnectionType.IsFail].Select(item => item.Guid);// 假分支
var upstreamNodes = SuccessorNodes[ConnectionType.IsError].Select(item => item.Guid);// 上游分支 var errorNodes = SuccessorNodes[ConnectionType.IsError].Select(item => item.Guid);// 异常分支
var errorNodes = SuccessorNodes[ConnectionType.Upstream].Select(item => item.Guid);// 异常分支 var upstreamNodes = SuccessorNodes[ConnectionType.Upstream].Select(item => item.Guid);// 上游分支
// 生成参数列表 // 生成参数列表
Parameterdata[] parameterData = GetParameterdatas(); Parameterdata[] parameterData = GetParameterdatas();
@@ -93,7 +93,7 @@ namespace Serein.NodeFlow.Model
UpstreamNodes = upstreamNodes.ToArray(), UpstreamNodes = upstreamNodes.ToArray(),
ParameterData = parameterData.ToArray(), ParameterData = parameterData.ToArray(),
ErrorNodes = errorNodes.ToArray(), ErrorNodes = errorNodes.ToArray(),
ChildNodes = ConditionNodes.Select(node => node.ToInfo()).ToArray(), ChildNodeGuids = ConditionNodes.Select(node => node.Guid).ToArray(),
}; };
} }

View File

@@ -64,15 +64,15 @@ namespace Serein.NodeFlow.Model
// context.SetFlowData(result); // context.SetFlowData(result);
// } // }
//} //}
public override Parameterdata[] GetParameterdatas() internal override Parameterdata[] GetParameterdatas()
{ {
if (base.MethodDetails.ExplicitDatas.Length > 0) if (base.MethodDetails.ExplicitDatas.Length > 0)
{ {
return MethodDetails.ExplicitDatas return MethodDetails.ExplicitDatas
.Select(it => new Parameterdata .Select(it => new Parameterdata
{ {
state = it.IsExplicitData, State = it.IsExplicitData,
value = it.DataValue, Value = it.DataValue,
}) })
.ToArray(); .ToArray();
} }

View File

@@ -55,31 +55,43 @@ namespace Serein.NodeFlow.Model
return result; return result;
} }
public override Parameterdata[] GetParameterdatas() internal override Parameterdata[] GetParameterdatas()
{ {
if (base.MethodDetails.ExplicitDatas.Length > 0) var value = CustomData switch
{ {
return MethodDetails.ExplicitDatas Type when CustomData.GetType() == typeof(int)
.Select(it => new Parameterdata && CustomData.GetType() == typeof(double)
{ && CustomData.GetType() == typeof(float)
state = IsCustomData, => ((double)CustomData).ToString(),
expression = Expression, Type when CustomData.GetType() == typeof(bool) => ((bool)CustomData).ToString(),
value = CustomData switch _ => CustomData?.ToString()!,
{ };
Type when CustomData.GetType() == typeof(int) return [new Parameterdata
&& CustomData.GetType() == typeof(double)
&& CustomData.GetType() == typeof(float)
=> ((double)CustomData).ToString(),
Type when CustomData.GetType() == typeof(bool) => ((bool)CustomData).ToString(),
_ => CustomData?.ToString()!,
}
})
.ToArray();
}
else
{ {
return []; State = IsCustomData,
Expression = Expression,
Value = value,
}];
}
internal override NodeModelBase LoadInfo(NodeInfo nodeInfo)
{
var node = this;
if (node != null)
{
node.Guid = nodeInfo.Guid;
for (int i = 0; i < nodeInfo.ParameterData.Length; i++)
{
Parameterdata? pd = nodeInfo.ParameterData[i];
node.IsCustomData = pd.State;
node.CustomData = pd.Value;
node.Expression = pd.Expression;
}
} }
return this;
} }
//public override void Execute(DynamicContext context) //public override void Execute(DynamicContext context)

View File

@@ -48,23 +48,25 @@ namespace Serein.NodeFlow.Model
} }
public override Parameterdata[] GetParameterdatas() internal override Parameterdata[] GetParameterdatas()
{ {
if (base.MethodDetails.ExplicitDatas.Length > 0) return [new Parameterdata{ Expression = Expression}];
}
internal override NodeModelBase LoadInfo(NodeInfo nodeInfo)
{
var node = this;
if (node != null)
{ {
return MethodDetails.ExplicitDatas node.Guid = nodeInfo.Guid;
.Select(it => new Parameterdata for (int i = 0; i < nodeInfo.ParameterData.Length; i++)
{ {
state = it.IsExplicitData, node.Expression = nodeInfo.ParameterData[i].Expression;
// value = it.DataValue, }
expression = Expression,
})
.ToArray();
}
else
{
return [];
} }
return this;
} }
} }
} }

View File

@@ -15,15 +15,15 @@ namespace Serein.NodeFlow.Model
return null; return null;
} }
public override Parameterdata[] GetParameterdatas() internal override Parameterdata[] GetParameterdatas()
{ {
if (base.MethodDetails.ExplicitDatas.Length > 0) if (base.MethodDetails.ExplicitDatas.Length > 0)
{ {
return MethodDetails.ExplicitDatas return MethodDetails.ExplicitDatas
.Select(it => new Parameterdata .Select(it => new Parameterdata
{ {
state = it.IsExplicitData, State = it.IsExplicitData,
value = it.DataValue Value = it.DataValue
}) })
.ToArray(); .ToArray();
} }

View File

@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.NodeFlow
{
public static class NodeStaticConfig
{
/// <summary>
/// 节点的命名空间
/// </summary>
public const string NodeSpaceName = $"{nameof(Serein)}.{nameof(Serein.NodeFlow)}.{nameof(Serein.NodeFlow.Model)}";
}
}

View File

@@ -18,6 +18,8 @@ namespace Serein.NodeFlow.Tool.SereinExpression.Resolver
public override bool Evaluate(object? obj) public override bool Evaluate(object? obj)
{ {
//object? memberValue = GetMemberValue(obj, MemberPath); //object? memberValue = GetMemberValue(obj, MemberPath);
if (TargetObj is T typedObj) if (TargetObj is T typedObj)
{ {
return new ValueTypeConditionResolver<T> return new ValueTypeConditionResolver<T>

View File

@@ -18,7 +18,16 @@ namespace Serein.NodeFlow.Tool.SereinExpression.Resolver
public override bool Evaluate(object obj) public override bool Evaluate(object obj)
{ {
object memberValue = GetMemberValue(obj, MemberPath); object memberValue;
if (!string.IsNullOrWhiteSpace(MemberPath))
{
memberValue = GetMemberValue(obj, MemberPath);
}
else
{
memberValue = obj;
}
if (memberValue is string strObj) if (memberValue is string strObj)
{ {
return new StringConditionResolver return new StringConditionResolver

View File

@@ -12,7 +12,9 @@ namespace Serein.NodeFlow.Tool.SereinExpression
try try
{ {
return ConditionParse(data, expression).Evaluate(data); var parse = ConditionParse(data, expression);
var result = parse.Evaluate(data);
return result;
} }
catch (Exception ex) catch (Exception ex)
@@ -24,7 +26,7 @@ namespace Serein.NodeFlow.Tool.SereinExpression
public static SereinConditionResolver ConditionParse(object data, string expression) public static SereinConditionResolver ConditionParse(object data, string expression)
{ {
if (expression.StartsWith('.')) // 表达式前缀属于从上一个节点数据对象获取成员值 if (expression.StartsWith('.') || expression.StartsWith('<')) // 表达式前缀属于从上一个节点数据对象获取成员值
{ {
return ParseObjectExpression(data, expression); return ParseObjectExpression(data, expression);
} }
@@ -128,9 +130,6 @@ namespace Serein.NodeFlow.Tool.SereinExpression
operatorStr = parts[0].ToLower(); // 操作类型 operatorStr = parts[0].ToLower(); // 操作类型
valueStr = string.Join(' ', parts.Skip(1)); // 表达式值 valueStr = string.Join(' ', parts.Skip(1)); // 表达式值
} }
targetObj = GetMemberValue(data, memberPath);// 获取对象成员,作为表达式的目标对象
Type? tempType = typeStr switch Type? tempType = typeStr switch
{ {
"int" => typeof(int), "int" => typeof(int),
@@ -140,6 +139,15 @@ namespace Serein.NodeFlow.Tool.SereinExpression
_ => Type.GetType(typeStr) _ => Type.GetType(typeStr)
}; };
type = tempType ?? throw new ArgumentException("对象表达式无效的类型声明"); type = tempType ?? throw new ArgumentException("对象表达式无效的类型声明");
if (string.IsNullOrWhiteSpace(memberPath))
{
targetObj = Convert.ChangeType(data, type);
}
else
{
targetObj = GetMemberValue(data, memberPath);// 获取对象成员,作为表达式的目标对象
}
} }
#region int #region int

View File

@@ -120,7 +120,7 @@ namespace Serein.WorkBench
/// <summary> /// <summary>
/// 成功加载的工程文件 /// 成功加载的工程文件
/// </summary> /// </summary>
public static SereinOutputFileData? FData { get; set; } public static SereinProjectData? FData { get; set; }
public static string FileDataPath = ""; public static string FileDataPath = "";
private void Application_Startup(object sender, StartupEventArgs e) private void Application_Startup(object sender, StartupEventArgs e)
{ {
@@ -141,7 +141,7 @@ namespace Serein.WorkBench
{ {
// 读取文件内容 // 读取文件内容
string content = System.IO.File.ReadAllText(filePath); // 读取整个文件内容 string content = System.IO.File.ReadAllText(filePath); // 读取整个文件内容
FData = JsonConvert.DeserializeObject<SereinOutputFileData>(content); FData = JsonConvert.DeserializeObject<SereinProjectData>(content);
FileDataPath = System.IO.Path.GetDirectoryName(filePath) ?? ""; FileDataPath = System.IO.Path.GetDirectoryName(filePath) ?? "";
} }
catch (Exception ex) catch (Exception ex)
@@ -150,13 +150,13 @@ namespace Serein.WorkBench
Shutdown(); // 关闭应用程序 Shutdown(); // 关闭应用程序
} }
} }
else if (1 == 11) else if (1 == 1)
{ {
string filePath = @"F:\临时\project\U9 project.dnf"; string filePath = @"F:\临时\project\new project.dnf";
//string filePath = @"D:\Project\C#\DynamicControl\SereinFlow\.Output\Debug\net8.0-windows7.0\U9 project.dnf"; //string filePath = @"D:\Project\C#\DynamicControl\SereinFlow\.Output\Debug\net8.0-windows7.0\U9 project.dnf";
string content = System.IO.File.ReadAllText(filePath); // 读取整个文件内容 string content = System.IO.File.ReadAllText(filePath); // 读取整个文件内容
FData = JsonConvert.DeserializeObject<SereinOutputFileData>(content); App.FData = JsonConvert.DeserializeObject<SereinProjectData>(content);
App.FileDataPath = System.IO.Path.GetDirectoryName(filePath); App.FileDataPath = filePath;//System.IO.Path.GetDirectoryName(filePath)!;
} }
} }

View File

@@ -5,6 +5,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Serein.WorkBench" xmlns:local="clr-namespace:Serein.WorkBench"
mc:Ignorable="d" mc:Ignorable="d"
Topmost="False"
Title="LogWindow" Height="600" Width="400" Title="LogWindow" Height="600" Width="400"
Closing="Window_Closing"> Closing="Window_Closing">
<Grid> <Grid>

View File

@@ -6,6 +6,7 @@
Title="Dynamic Node Flow" Height="700" Width="1200" Title="Dynamic Node Flow" Height="700" Width="1200"
AllowDrop="True" Drop="Window_Drop" DragOver="Window_DragOver" AllowDrop="True" Drop="Window_Drop" DragOver="Window_DragOver"
Loaded="Window_Loaded" Loaded="Window_Loaded"
ContentRendered="Window_ContentRendered"
Closing="Window_Closing"> Closing="Window_Closing">
<Window.Resources> <Window.Resources>
@@ -82,7 +83,7 @@
</Grid.RowDefinitions> </Grid.RowDefinitions>
<StackPanel Grid.Row="0" Background="#F5F5F5" Orientation="Horizontal" > <StackPanel Grid.Row="0" Background="#F5F5F5" Orientation="Horizontal" >
<Button x:Name="ButtonDebugRun" Content="运行" Width="100" Margin="10" Click="ButtonDebugRun_Click"></Button> <Button x:Name="ButtonDebugRun" Content="运行" Width="100" Margin="10" Click="ButtonDebugRun_Click"></Button>
<Button x:Name="ButtonDebugFlipflopNode" Content="停止" Width="100" Margin="10" Click="ButtonDebugFlipflopNode_Click"></Button> <Button x:Name="ButtonDebugFlipflopNode" Content="结束" Width="100" Margin="10" Click="ButtonDebugFlipflopNode_Click"></Button>
</StackPanel> </StackPanel>
<StackPanel Grid.Row="1" <StackPanel Grid.Row="1"
@@ -136,7 +137,7 @@
<!-- Bottom-Right Thumb --> <!-- Bottom-Right Thumb -->
<Thumb x:Name="BottomRightThumb" <Thumb x:Name="BottomRightThumb"
Width="10" Height="10" Width="40" Height="40"
DragDelta="Thumb_DragDelta_BottomRight" DragDelta="Thumb_DragDelta_BottomRight"
Cursor="SizeNWSE" Cursor="SizeNWSE"
Canvas.Left="{Binding ActualWidth, ElementName=FlowChartCanvas, Mode=OneWay, Converter={StaticResource RightThumbPositionConverter}}" Canvas.Left="{Binding ActualWidth, ElementName=FlowChartCanvas, Mode=OneWay, Converter={StaticResource RightThumbPositionConverter}}"

File diff suppressed because it is too large Load Diff

View File

@@ -35,9 +35,9 @@ namespace Serein.WorkBench.Node.ViewModel
public ConditionNodeControlViewModel(SingleConditionNode node) : base(node) public ConditionNodeControlViewModel(SingleConditionNode node) : base(node)
{ {
this.singleConditionNode = node; this.singleConditionNode = node;
IsCustomData = false; //IsCustomData = false;
CustomData = ""; //CustomData = "";
Expression = "PASS"; //Expression = "PASS";
} }
} }