使用Simple Injector

时间:2015-12-16 02:42:23

标签: c# .net wpf dependency-injection simple-injector

此问题目前与SimpleInjector有关,但可以适用于任何可以更好地处理此问题的DI容器......

我正在为我的公司做研究。我觉得DI容器可以帮助我们更方便,但是功能有一些问题所以我已经着手尝试解决其中的一些以证明DI容器的价值。我们有一个非常庞大的ERP系统,我们即将开始我们的下一代旗舰产品。该产品将包含几个大型组件,可能至少20-30个,所有这些组件都将很大。

用户的主要访问点将是一个shell,可以说,它由一个程序集组成,当用户在应用程序中导航时,将调用其他程序集。我们不希望停止启动时间,因此我们希望找到一种根据需要加载程序集的方法。只有当需要来自特定组件的东西时才应该加载程序集,直到那时它不应该在内存中。

有没有办法用Simple Injector或DI Container做这件事?我们正在WPF中编写应用程序。

1 个答案:

答案 0 :(得分:3)

您的要求有点棘手,因为DI容器的正常使用情况是预先加载所有内容。使用Pure DI这更简单,因为您只需通过为每个根类型(或根类型组)提供自己的方法来延迟程序集加载。装配加载发生在引用装配中的类型的方法是JITted时。

因此,使用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事件,但允许所有注册在最后一分钟在同一容器中注册。

最后注意到了。当然,我看不出你正在建造什么样的“旗舰”应用程序。对于我在载体中看到的大多数应用,这种延迟装配加载没有多大意义。它通常只适用于非常大的双层应用程序。两层应用程序是直接与数据库通信的胖客户端(Win Forms,WPF等)应用程序。这些双层应用程序现在非常罕见,因为在大多数情况下添加第三层(位于客户端和数据库之间的Web服务)要好得多。更好,因为您可以添加额外的安全层,并且更好,因为您可以在该级别进行额外控制(记录,调整,审计尾随)。在Web服务层,实现延迟程序集加载通常没什么好处,因为Web应用程序可以比预加载胖客户端应用程序更容易预加载。

这当然不适用于所有类型的应用程序。例如,如果您正在构建类似Visual Studio(使用Managed Extensibility Framework进行延迟装配加载btw)之类的东西,那么添加第三层通常是一种非常好的方法。当您围绕thesethese等模式构建应用程序时,第三层的开销非常小。