Mef:"无法序列化"当试图从另一个appdomain加载ApiController时

时间:2018-04-28 22:11:07

标签: c# asp.net mef appdomain

我试图通过添加新的ApiController(由其他服务下载)找到一种在运行时更新Asp.net web api(.Net framework 4.5)的方法(不回收主appdomain)。

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

你知道我怎么能把它序列化吗? 你知道另一种选择吗?

修改

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

1 个答案:

答案 0 :(得分:2)

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

如果没有看到你的代码就很难说清楚了,但是根据你所写的内容,在我看来你有些困惑。

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

我的问题在于这句话:

  

...在单独的appdomain中加载包含ApiController的插件   并使用MarshalByRefObject从主appdomain

加载

类型(类)定义+代码和实例数据是两回事。将程序集加载到appdomain意味着您正在加载类型定义和代码。当您想要跨appdomain边界传输实例数据时,可以看到序列化。您无法在编写时从其他应用程序域加载类型定义和代码(实际上您可以,但我怀疑您需要)。为了能够传输实例数据,双方都需要了解正在传输的实例的类型定义。在这种情况下,序列化和传输由.net远程处理运行时管理。 您有两种选择:移动所有实例数据并将其一直序列化,或者按照您的说法选择MarshalByObjRef方式。让我们留在这里。为了能够使用其他appdomain中的实例,您需要使用激活器在其他appdomain中实例化该类型(在这种情况下您不能使用new运算符),并获取对它将是一个基于您所知类型的代理(也可以是接口或基类,而不仅仅是确切的类型)。在这种情况下反射有点受限,更少的是准备asp.net来计算远程对象的方法 - 但你可以通过适当的接口来帮助它。

所以,让我们假设你已经在另一个appdomain中创建了一个控制器实例,并且你有一个远程引用可以分配给一个接口类型,它定义了你需要向asp.net公开的所有方法。现在,当尝试访问控制器类的成员时,将会看到序列化。每个方法参数和方法返回类型都需要可序列化。但不是类本身,因为它是MashalByObjRef后代,不会被编组为实例。而MashalByObjRef与如何将程序集加载到appdomain中无关。

但是等等! MarshalByObjRefApiController都是抽象类。你想如何从两者中派生出你的实际控制器类?你不能。因此,我认为您不能直接从其他应用程序域直接使用apicontrollers。

我可以想象两件事:

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

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

  • 定义控制器逻辑的接口
  • 创建apicontroller无版本和无逻辑,但能够创建和卸载appdomains,将程序集加载到它们中,继续引用从上面创建的实现接口的实例,并将请求定向到那些
  • 请注意,您将无法将某些内容(如控制器上下文)传递给其他应用程序域中的逻辑,您必须提取所需内容或在另一侧重新创建
  • 这样您就可以在“远程”应用程序域中拥有逻辑MarshalByObjRef后代,并在主应用程序域中拥有控制器ApiController后代。
  • 我会创建一个扩展ApiController的临时抽象类,它能够自行处理上述分离。应用程序的其余部分不会意识到这一点。
  • 了解远程处理所涉及的终身服务,您可以使用赞助商或覆盖MarshalByObjRef的某些方法来处理。

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