Mef:"Cannot serialize" 尝试从另一个应用程序域加载 ApiController

Mef : "Cannot serialize" when trying to load an ApiController from another appdomain

我正在尝试通过添加新的 ApiController(由另一个下载服务)。

我尝试使用 Mef 并且能够在当前应用程序域中加载新的 ApiController,但是我在尝试更新现有插件时卡住了(程序集已经添加到应用程序域中,所以我无法添加新的)。 所以我决定在单独的应用程序域中加载包含 ApiController 的插件,并使用 MarshalByRefObject 从主应用程序域加载它,但事实证明 ApiConroller 无法序列化。

你知道我怎么序列化它吗? 你知道替代方案吗?

编辑:

如果程序集已签名,我设法加载了不同版本的程序集(在同一个应用程序域中),但它不符合我的要求。

我没有使用过 MEF(因为它很容易从头开始实现它的功能,与 MAF 相矛盾),但是这样我对裸 AppDomains 有了一些经验。

没有看到你的代码很难说出很多东西,但从你写的内容来看,在我看来你混淆了一些东西。

您可能知道并且您也已经指出,您实际上无法更新已加载的程序集。加载它的另一个版本(具有不同的签名)意味着您加载了两个不同的程序集。它们中的类型将具有不同的强名称。 如果需要,您实际上可以处理这个问题。 卸载程序集的唯一方法是卸载包含它的应用程序域。

我的问题是这句话:

... load the plugin containing the ApiController in a separate appdomain and use MarshalByRefObject to load it from the main appdomain

类型(class)定义+代码和实例数据是两个不同的东西。将程序集加载到应用程序域意味着您正在加载类型定义和代码。当您想要跨应用程序域边界传输实例数据时,序列化就会出现。你不能像你写的那样从其他应用程序域加载类型定义和代码(实际上你可以,但我怀疑你需要这样做)。为了能够传输实例数据,双方都需要了解正在传输的实例的类型定义。在这种情况下,序列化和传输由 .net 远程处理运行时管理。 您有两个选择:要么移动所有实例数据并一直对其进行序列化,要么像您所说的那样选择 MarshalByObjRef 方式。让我们继续这个。为了能够在其他应用程序域中使用实例,您需要使用激活器在其他应用程序域中实例化类型(在这种情况下您不能使用 new 运算符),并获取对它将是基于您知道的类型的代理(也可以是接口或基础 class,而不仅仅是确切的类型)。在这种情况下,反射有些受限,准备 asp.net 来找出远程对象的方法的准备就更少了——但您可以通过适当的接口来帮助它。

所以,假设您已经在另一个应用程序域中创建了一个控制器实例,并且您有一个远程引用可以分配给一个接口类型,该接口类型定义了您需要公开给 asp.net 的所有方法。现在,当试图访问控制器 class 的成员时,序列化就会出现。每个方法参数和方法 return 类型都需要是可序列化的。但不是 class 本身,因为它是 MashalByObjRef 的后代,不会作为实例混搭。 MashalByObjRef 与您将程序集加载到 appdomain 的方式无关。

但是等等! MarshalByObjRefApiController 都是抽象的 classes。你想如何从两者中推导出你的实际控制人class?你不能。因此,我认为您不能直接使用其他应用程序域中的 apicontroller。

我可以想象两件事:

1) 继续将新的签名版本加载到同一个程序集中,并自定义路由机制以将请求定向到最新版本(可能不再有效,但仍然是一个很好的起点:https://www.strathweb.com/2013/08/customizing-controller-discovery-in-asp-net-web-api/). 当然,重启时,如果你不需要同时拥有多个版本,你应该只加载最新的一个。

2) 做一个稍微复杂的基础设施:

  • 为控制器逻辑定义接口[=52​​=]
  • 创建无版本和无逻辑的 apicontroller,但能够创建和卸载应用程序域,将程序集加载到其中,保持对实现上面创建的接口的实例的引用,并将请求定向到那些
  • 请注意,您将无法将某些内容(如控制器上下文)传递给另一个应用程序域中的逻辑,您将不得不提取您需要的内容或在另一端重新创建
  • 通过这种方式,您可以在 "remote" 应用程序域中拥有逻辑 MarshalByObjRef 后代,而在主应用程序域中拥有您的控制器 ApiController 后代。
  • 我会创建一个临时摘要 class 扩展 ApiController 并能够自行处理上述分离。应用程序的其余部分不会意识到这一点。
  • 注意远程处理中涉及的生命周期服务,您可以通过使用赞助商或重写 MarshalByObjRef 的某些方法来处理。

这两种方法都不是简单的方法,您将面临一些进一步的挑战...