问题
我有一个带有插件/模块架构和多租户支持的 ASP.NET MVC 3 应用程序。MEF 用于解决依赖关系和加载可插拔部件。
每个模块由控制器、视图和其他对象组成(从物理上讲,它是一个程序集)。模块被加载到租户中。
简单的配置可能如下所示:
租户 1:
- 模块 A,版本 1.0 (ModuleA.dll)
- 模块 B,版本 1.0 (ModuleB.dll)
租户 2:
- 模块 B,版本 1.0 (ModuleB.dll)
不同模块和不同版本的DLL分别存储在不同的物理位置。并且应用程序在一个 AppDomain 上运行(默认一个)。
但是,如果我们想做不同租户使用不同模块版本的配置,我们会遇到在不同版本中加载相同程序集的问题。这意味着下面的场景不能完全正常工作,因为在从 ModuleB 解析类型期间,我们遇到了组合不匹配异常(版本 1.0 和 1.5 已加载到 MEF 中,但只有一个程序集已通过程序集加载器加载到 AppDomain 中)。
租户 1:
- 模块 A,版本 1.0 (ModuleA.dll)
- 模块 B,版本 1.0 (ModuleB.dll)
租户 2:
- 模块 A,版本 1.5 (ModuleB.dll)
解决方案?
所以我们想出了一种解决方案,即将不同的租户及其模块/程序集加载到单独的 AppDomains中。这意味着从我们的示例中,Tenant1 和 Tenant2 被加载到 AppDomain1 和 AppDomain2 中。在 ASP.NET MVC 管道中,我们连接到控制器工厂以选择合适的应用程序域,如下所示:
- 请求默认处理 AppDomain(Web 应用程序启动的那个)
- 控制器工厂
- 从请求中获取 Tenant_Id 并从适当的 AppDomain 解析适当的控制器(我们有 Tenant_Id->Tenant->AppDomain 关系)
- 返回 ControllerProxy (这是一个代理类,实现了 IController 并继承 MarshalByRefObject 以便能够在不同的 App Domians 之间传递控制器)
- 从请求中获取 Tenant_Id 并从适当的 AppDomain 解析适当的控制器(我们有 Tenant_Id->Tenant->AppDomain 关系)
- 动作调用者
- 在控制器代理对象上调用正确的操作,现在执行发生在底层应用程序域中
- 在这里我们遇到了问题,因为动作调用者无法将不可序列化的 RequestContext 传递给另一个应用程序域(换句话说,controllerProxy.Execute(RequestContext context) 正在引发关于序列化的异常)
问题):
- 如何以一种很好的方式在应用程序域之间传递 RequestContext(不可序列化对象)?
- 是否可以连接到管道中的另一个步骤以将执行重定向到底层应用程序域(在控制器工厂之前?)
- 或者关于这个问题的另一种解决方案的任何想法?