Signal 数据中心改为运行时加载 schema,解除 StandardScene 工程编译耦合。

加固 Config/Signal 路径解析,前端 DataCenter 表单类型与 API 对齐。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
黄兆尉
2026-08-26 17:47:15 +08:00
co-authored by Cursor
parent 3686abdc78
commit 4180140ae4
5 changed files with 103 additions and 62 deletions
+63 -49
View File
@@ -1,13 +1,12 @@
using System.ComponentModel;
using System.Reflection;
using System.Text.Json.Serialization;
using StandardScene.Signal.Model;
namespace MiGu.Server.Signal;
/// <summary>
/// 从 StandardScene.Signal 程序集反射数据中心表和列。
/// 优先使用 MiGu.Server 编译引用的程序集;否则再从 SimpleLite plugins 加载
/// 从 Simple3 <c>plugins/StandardScene.Signal.dll</c> 反射数据中心表和列。
/// 平台不编译引用该插件工程,避免仓外路径和 Windows TFM 耦合
/// </summary>
public static class SignalModelSchemaResolver
{
@@ -24,18 +23,19 @@ public static class SignalModelSchemaResolver
public static IReadOnlyList<SignalTableDef> ResolveTables(string? pluginsDir, string? workingDirectory = null)
{
var assembly = TryGetSignalAssembly(pluginsDir, workingDirectory);
if (assembly == null)
return Array.Empty<SignalTableDef>();
try
return WithSignalAssembly(pluginsDir, workingDirectory, assembly =>
{
return BuildTablesFromAssembly(assembly);
}
catch
{
return Array.Empty<SignalTableDef>();
}
if (assembly == null)
return Array.Empty<SignalTableDef>();
try
{
return BuildTablesFromAssembly(assembly);
}
catch
{
return Array.Empty<SignalTableDef>();
}
});
}
public static IReadOnlyList<SignalColumn> ResolveColumns(string? modelName, string? pluginsDir, string? workingDirectory = null)
@@ -43,61 +43,76 @@ public static class SignalModelSchemaResolver
if (string.IsNullOrWhiteSpace(modelName))
return Array.Empty<SignalColumn>();
var assembly = TryGetSignalAssembly(pluginsDir, workingDirectory);
if (assembly == null)
return Array.Empty<SignalColumn>();
try
return WithSignalAssembly(pluginsDir, workingDirectory, assembly =>
{
var type = assembly.GetType($"{ModelNamespace}.{modelName.Trim()}", throwOnError: false, ignoreCase: true);
if (type == null)
if (assembly == null)
return Array.Empty<SignalColumn>();
return DiscoverColumns(type);
}
catch
{
return Array.Empty<SignalColumn>();
}
try
{
var type = assembly.GetType($"{ModelNamespace}.{modelName.Trim()}", throwOnError: false, ignoreCase: true);
if (type == null)
return Array.Empty<SignalColumn>();
return DiscoverColumns(type);
}
catch
{
return Array.Empty<SignalColumn>();
}
});
}
private static Assembly? TryGetSignalAssembly(string? pluginsDir, string? workingDirectory)
private static T WithSignalAssembly<T>(string? pluginsDir, string? workingDirectory, Func<Assembly?, T> use)
{
try
{
var referenced = typeof(PlcStationModel).Assembly;
if (HasModelTypes(referenced))
return referenced;
}
catch
{
/* 未引用插件工程时继续走 LoadFrom */
}
var assemblyPath = FindAssemblyPath(pluginsDir);
if (assemblyPath == null)
return null;
return use(null);
var expected = Path.GetFullPath(assemblyPath);
var cached = FindLoadedFrom(expected);
if (cached != null)
return use(cached);
var probeDirs = BuildProbeDirs(pluginsDir, workingDirectory);
ResolveEventHandler? handler = null;
handler = (_, args) => ResolveAssembly(args.Name, probeDirs);
ResolveEventHandler handler = (_, args) => ResolveAssembly(args.Name, probeDirs);
AppDomain.CurrentDomain.AssemblyResolve += handler;
try
{
var loaded = Assembly.LoadFrom(assemblyPath);
return HasModelTypes(loaded) ? loaded : null;
var loaded = Assembly.LoadFrom(expected);
return use(HasModelTypes(loaded) ? loaded : null);
}
catch
{
return null;
return use(null);
}
finally
{
if (handler != null)
AppDomain.CurrentDomain.AssemblyResolve -= handler;
AppDomain.CurrentDomain.AssemblyResolve -= handler;
}
}
private static Assembly? FindLoadedFrom(string expectedFullPath)
{
try
{
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
if (!string.Equals(assembly.GetName().Name, "StandardScene.Signal", StringComparison.OrdinalIgnoreCase))
continue;
if (string.IsNullOrWhiteSpace(assembly.Location))
continue;
if (!string.Equals(Path.GetFullPath(assembly.Location), expectedFullPath, StringComparison.OrdinalIgnoreCase))
continue;
return HasModelTypes(assembly) ? assembly : null;
}
}
catch
{
/* 继续 LoadFrom */
}
return null;
}
private static bool HasModelTypes(Assembly assembly)
{
return SafeGetTypes(assembly).Any(t =>
@@ -119,7 +134,6 @@ public static class SignalModelSchemaResolver
Add(workingDirectory);
if (!string.IsNullOrWhiteSpace(pluginsDir))
Add(Path.GetDirectoryName(pluginsDir));
Add(AppContext.BaseDirectory);
return dirs;
}