FlowSerializationBinder.cs 3.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Runtime.Serialization;
  4. namespace TeamAAS.FlowEngine.Execution
  5. {
  6. /// <summary>
  7. /// 流程文件(.aas)序列化绑定器 —— 算子程序集迁移的兼容层。
  8. ///
  9. /// 背景:流程文件用 BinaryFormatter 序列化,对象按「类型全名 + 程序集名」记录。
  10. /// 把已存在的插件类(如 HalconThresholdPlugin)迁移到新的分类程序集
  11. /// (Plugins.Halcon → Plugins.Halcon.ImageProcessing)后,旧流程文件里的旧程序集名将无法解析。
  12. /// 本绑定器按 <see cref="LegacyTypeMap"/> 把旧「类型全名→新 Type」重定向,旧流程零改动可加载。
  13. ///
  14. /// 规则:
  15. /// - 映射命中 → 迁移后的新 Type;
  16. /// - 未命中 → 返回 null(BinaryFormatter 走默认解析,行为与无绑定器完全一致);
  17. /// - 保存方向(BindToName)不介入:新流程直接记录迁移后的新程序集(回滚旧版本软件将无法识别,
  18. /// 迁移前请确认不再需要回滚)。
  19. /// </summary>
  20. public sealed class FlowSerializationBinder : SerializationBinder
  21. {
  22. private static readonly FlowSerializationBinder _instance = new FlowSerializationBinder();
  23. public static FlowSerializationBinder Instance => _instance;
  24. /// <summary>
  25. /// 旧类型全名 → 迁移后 Type。宿主/插件在启动时注册(如算子从主程序集迁入分类程序集时登记)。
  26. /// key 形如 "Plugins.Halcon.Models.HalconThresholdModel, Plugins.Halcon"(与旧文件里的程序集记录一致)。
  27. /// </summary>
  28. public static readonly ConcurrentDictionary<string, Type> LegacyTypeMap =
  29. new ConcurrentDictionary<string, Type>(StringComparer.OrdinalIgnoreCase);
  30. /// <summary>
  31. /// 注册一条迁移映射(旧类型全名+旧程序集 → 新 Type)。
  32. /// </summary>
  33. public static void MapLegacy(string legacyTypeFullName, string legacyAssemblyName, Type newType)
  34. {
  35. if (string.IsNullOrEmpty(legacyTypeFullName) || newType == null) return;
  36. LegacyTypeMap[legacyTypeFullName + ", " + legacyAssemblyName] = newType;
  37. }
  38. /// <summary>兜底解析缓存:typeName → 已解析 Type(避免每次反序列化全域扫描)。</summary>
  39. private static readonly ConcurrentDictionary<string, Type> _fallbackCache =
  40. new ConcurrentDictionary<string, Type>(StringComparer.Ordinal);
  41. /// <inheritdoc />
  42. public override Type BindToType(string assemblyName, string typeName)
  43. {
  44. // 1) 显式映射表命中(完整匹配 typeName + assemblyName)
  45. Type mapped;
  46. if (LegacyTypeMap.TryGetValue(typeName + ", " + assemblyName, out mapped))
  47. return mapped;
  48. // 2) 兜底:记录的程序集里没有该类型(典型场景=算子已迁入新的分类程序集),
  49. // 在当前已加载的全部程序集中按「类型全名」查找。类型未移动时查到的就是原类型,
  50. // 行为与默认解析一致;已移动时自动指向新程序集 → 旧流程零改动可加载。
  51. // 前提:插件程序集在流程加载前已全部装载(PluginLoader.LoadFrom 启动时扫描)。
  52. return _fallbackCache.GetOrAdd(typeName, tn =>
  53. {
  54. foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
  55. {
  56. try
  57. {
  58. if (asm.IsDynamic) continue;
  59. var t = asm.GetType(tn, throwOnError: false);
  60. if (t != null) return t;
  61. }
  62. catch { }
  63. }
  64. return null; // 全域找不到 → null(BinaryFormatter 走默认解析并按原样报错)
  65. });
  66. }
  67. }
  68. }