Signal 数据中心改为运行时加载 schema,解除 StandardScene 工程编译耦合。
加固 Config/Signal 路径解析,前端 DataCenter 表单类型与 API 对齐。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user