使用 Simple Injector 根据需要加载程序集
Load Assemblies as needed with Simple Injector
这个问题目前是关于 SimpleInjector 的,但可以适用于任何可以更好地处理这个问题的 DI 容器...
我现在正在为我的公司做研究。我觉得 DI 容器可以让我们的事情变得更容易,但是它的功能有一些问题,所以我已经着手尝试解决其中的一些问题,以证明 DI 容器的价值。我们有一个非常大的 ERP 系统,我们即将开始我们旗舰产品的下一次迭代。该产品将包含几个大型组件,可能至少有 20-30 个,所有这些组件都将是大型组件。
我们的用户主要访问点将是 shell,可以这么说,它由一个程序集组成,当用户在应用程序中导航时,将调用其他程序集。我们不想拖慢启动时间,所以我们想找到一种方法来根据需要加载程序集。只有当需要来自特定程序集的某些内容时,程序集才应该加载,直到那时它不应该在内存中。
有没有什么方法可以使用 Simple Injector 或 DI Container 来做到这一点?我们正在用 WPF 编写应用程序。
您的要求有点棘手,因为 DI 容器的正常用例是预先加载所有内容。使用 Pure DI 这更简单,因为您只需通过为每个根类型(或一组根类型)提供自己的方法来延迟程序集加载。程序集加载发生在 JIT 引用程序集类型的方法时。
因此,对于 Pure DI,我将合成根想象成这样:
public static object GetRootType(Type type) {
if (type == typeof(HomeController))
return GetHomeController();
if (type == typeof(ShipOrderController))
return GetShipOrderController();
if (type == typeof(CancelOrderController))
return GetCancelOrderController();
// etc
}
private static object GetCancelOrderController() {
return new CancelOrderController(
ApplyAop(new CancelOrderHandler(new UnitOfWork())));
}
private static object GetShipOrderController() { ... }
private static object GetHomeController() { ... }
private static ICommandHandler<T> ApplyAop<T>(ICommandHandler<T> handler) {
return new TransactionCommandHandlerDecorator<T>(
new ValidationCommandHandlerDecorator<T>(handler));
}
由于 CLR 通常只会在第一次调用 JIT 方法时调用它们,程序集加载将在此时发生,当然除非之前引用了此类类型。例如,如果 GetRootType
方法中引用的类型引用了需要延迟加载的程序集,这意味着 GetRootType
的第一次命中将预加载这些程序集。
虽然使用 Pure DI 可以更容易地延迟程序集加载,但是当应用程序很大并且包含许多 类 时,use of a container will typically out-gain Pure DI.
但 Simple Injector 实际上使这个场景比其他包含更难,因为它有一个 strict lock-down policy,这意味着你不能通过注册 API 添加注册。解决。另一方面,此锁定策略迫使您进入一个干净的模型,该模型实际上有助于防止许多难以检测的错误。
如何使用 Simple Injector 解决这个问题取决于应用程序。我想说有两种方法可以解决这个问题。您可以为每个程序集(或一组程序集)提供自己的容器。这允许在加载此类组件时构建此类容器。这允许您在一个地方定义您需要的每个特定注册,可能集中在一个地方。缺点是如果这些程序集不是隔离的,注册会变得更加复杂;这些程序集之间共享的类型越多,每个程序集的注册就越难正确。
另一种选择是利用 Simple Injector 公开的 ResolveUnregisteredType
事件。这允许您在第一次请求未注册的类型时进行最后一刻的注册。
然而,这两个选项仍然有点棘手,因为您必须确保并非所有程序集都在第一次调用容器时直接预加载。因此,运行以检查某个程序集是否已加载的方法本身不能引用该程序集中的任何类型,并且本身不能加载它正在寻找的程序集。否则你回到原点。
使用多个容器实例时,您的解决方案可能如下所示:
var asm1Container = new lazy<Container>(Asm1Bootstrapper.Bootstrap);
var asm2Container = new lazy<Container>(Asm2Bootstrapper.Bootstrap);
var asm3Container = new lazy<Container>(Asm3Bootstrapper.Bootstrap);
mainContainer.ResolveUnregisteredType += (s, e) =>
{
var asmContainer = GetAssemblyContainer(e.UnregisteredServiceType.Assembly);
e.Register(() => asmContainer.GetInstance(e.UnregisteredServiceType));
}
private Container GetAssemblyContainer(Assembly assembly) {
string assemblyName = assembly.FullName;
if (assemblyName.Contains("Assembly1"))
asm1Container.Value;
if (assemblyName.Contains("Assembly2"))
asm2Container.Value;
if (assemblyName.Contains("Assembly3"))
asm3Container.Value;
// etc
}
使用第二种方法,您将以相同的方式使用 ResolveUnregisteredType
事件,但允许在同一容器中最后一刻注册所有注册。
不过最后要注意的是。当然,我看不到您正在构建哪种 "flag ship" 应用程序。对于我在载体中看到的大多数应用程序,这种延迟的程序集加载没有多大意义。它通常只对非常大的两层应用程序有意义。两层应用程序是直接与数据库对话的胖客户端(Win Forms、WPF 等)应用程序。这些两层应用程序现在非常少见,因为在大多数情况下添加第三层(位于客户端和数据库之间的 Web 服务)要好得多。更好是因为您可以添加额外的安全层,更好是因为您可以在该级别拥有额外的控制(日志记录、调整、审计跟踪)。而在 web 服务层,实现延迟程序集加载通常没有什么好处,因为可以预加载 web 应用程序比预加载胖客户端应用程序容易得多。
这当然不适用于所有类型的应用程序。例如,如果您正在构建类似 Visual Studio 的东西(顺便说一句,它使用托管可扩展性框架来延迟程序集加载),添加第三层通常是一种非常好的方法。当您围绕 these and these.
等模式构建应用程序时,第三层的开销非常小
这个问题目前是关于 SimpleInjector 的,但可以适用于任何可以更好地处理这个问题的 DI 容器...
我现在正在为我的公司做研究。我觉得 DI 容器可以让我们的事情变得更容易,但是它的功能有一些问题,所以我已经着手尝试解决其中的一些问题,以证明 DI 容器的价值。我们有一个非常大的 ERP 系统,我们即将开始我们旗舰产品的下一次迭代。该产品将包含几个大型组件,可能至少有 20-30 个,所有这些组件都将是大型组件。
我们的用户主要访问点将是 shell,可以这么说,它由一个程序集组成,当用户在应用程序中导航时,将调用其他程序集。我们不想拖慢启动时间,所以我们想找到一种方法来根据需要加载程序集。只有当需要来自特定程序集的某些内容时,程序集才应该加载,直到那时它不应该在内存中。
有没有什么方法可以使用 Simple Injector 或 DI Container 来做到这一点?我们正在用 WPF 编写应用程序。
您的要求有点棘手,因为 DI 容器的正常用例是预先加载所有内容。使用 Pure DI 这更简单,因为您只需通过为每个根类型(或一组根类型)提供自己的方法来延迟程序集加载。程序集加载发生在 JIT 引用程序集类型的方法时。
因此,对于 Pure DI,我将合成根想象成这样:
public static object GetRootType(Type type) {
if (type == typeof(HomeController))
return GetHomeController();
if (type == typeof(ShipOrderController))
return GetShipOrderController();
if (type == typeof(CancelOrderController))
return GetCancelOrderController();
// etc
}
private static object GetCancelOrderController() {
return new CancelOrderController(
ApplyAop(new CancelOrderHandler(new UnitOfWork())));
}
private static object GetShipOrderController() { ... }
private static object GetHomeController() { ... }
private static ICommandHandler<T> ApplyAop<T>(ICommandHandler<T> handler) {
return new TransactionCommandHandlerDecorator<T>(
new ValidationCommandHandlerDecorator<T>(handler));
}
由于 CLR 通常只会在第一次调用 JIT 方法时调用它们,程序集加载将在此时发生,当然除非之前引用了此类类型。例如,如果 GetRootType
方法中引用的类型引用了需要延迟加载的程序集,这意味着 GetRootType
的第一次命中将预加载这些程序集。
虽然使用 Pure DI 可以更容易地延迟程序集加载,但是当应用程序很大并且包含许多 类 时,use of a container will typically out-gain Pure DI.
但 Simple Injector 实际上使这个场景比其他包含更难,因为它有一个 strict lock-down policy,这意味着你不能通过注册 API 添加注册。解决。另一方面,此锁定策略迫使您进入一个干净的模型,该模型实际上有助于防止许多难以检测的错误。
如何使用 Simple Injector 解决这个问题取决于应用程序。我想说有两种方法可以解决这个问题。您可以为每个程序集(或一组程序集)提供自己的容器。这允许在加载此类组件时构建此类容器。这允许您在一个地方定义您需要的每个特定注册,可能集中在一个地方。缺点是如果这些程序集不是隔离的,注册会变得更加复杂;这些程序集之间共享的类型越多,每个程序集的注册就越难正确。
另一种选择是利用 Simple Injector 公开的 ResolveUnregisteredType
事件。这允许您在第一次请求未注册的类型时进行最后一刻的注册。
然而,这两个选项仍然有点棘手,因为您必须确保并非所有程序集都在第一次调用容器时直接预加载。因此,运行以检查某个程序集是否已加载的方法本身不能引用该程序集中的任何类型,并且本身不能加载它正在寻找的程序集。否则你回到原点。
使用多个容器实例时,您的解决方案可能如下所示:
var asm1Container = new lazy<Container>(Asm1Bootstrapper.Bootstrap);
var asm2Container = new lazy<Container>(Asm2Bootstrapper.Bootstrap);
var asm3Container = new lazy<Container>(Asm3Bootstrapper.Bootstrap);
mainContainer.ResolveUnregisteredType += (s, e) =>
{
var asmContainer = GetAssemblyContainer(e.UnregisteredServiceType.Assembly);
e.Register(() => asmContainer.GetInstance(e.UnregisteredServiceType));
}
private Container GetAssemblyContainer(Assembly assembly) {
string assemblyName = assembly.FullName;
if (assemblyName.Contains("Assembly1"))
asm1Container.Value;
if (assemblyName.Contains("Assembly2"))
asm2Container.Value;
if (assemblyName.Contains("Assembly3"))
asm3Container.Value;
// etc
}
使用第二种方法,您将以相同的方式使用 ResolveUnregisteredType
事件,但允许在同一容器中最后一刻注册所有注册。
不过最后要注意的是。当然,我看不到您正在构建哪种 "flag ship" 应用程序。对于我在载体中看到的大多数应用程序,这种延迟的程序集加载没有多大意义。它通常只对非常大的两层应用程序有意义。两层应用程序是直接与数据库对话的胖客户端(Win Forms、WPF 等)应用程序。这些两层应用程序现在非常少见,因为在大多数情况下添加第三层(位于客户端和数据库之间的 Web 服务)要好得多。更好是因为您可以添加额外的安全层,更好是因为您可以在该级别拥有额外的控制(日志记录、调整、审计跟踪)。而在 web 服务层,实现延迟程序集加载通常没有什么好处,因为可以预加载 web 应用程序比预加载胖客户端应用程序容易得多。
这当然不适用于所有类型的应用程序。例如,如果您正在构建类似 Visual Studio 的东西(顺便说一句,它使用托管可扩展性框架来延迟程序集加载),添加第三层通常是一种非常好的方法。当您围绕 these and these.
等模式构建应用程序时,第三层的开销非常小