c# - 使用简单进样器根据需要加载装配体

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

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

我现在正在为我的公司做研究。我认为DI容器可以使我们更轻松地进行工作,但是这种能力存在一些问题,因此,我着手尝试解决其中的一些问题,以证明DI容器的价值。我们拥有非常庞大的ERP系统,并且即将开始旗舰产品的下一次迭代。该产品将包含几个大型组件,可能至少20-30个,所有这些组件又将很大。

可以说,我们对用户的主要访问点将是一个外壳,该外壳由一个程序集组成,当用户浏览应用程序时,将调用其他程序集。我们不想浪费启动时间,所​​以我们想找到一种方法来根据需要加载程序集。仅当需要来自特定程序集的某些内容时,才应加载该程序集,直到那时才应将其装入内存。

有什么办法可以通过简单注入器或DI容器来完成此操作吗?我们正在用WPF编写应用程序。

最佳答案

您的要求有些棘手,因为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进行延迟的程序集加载)的东西,那么添加第三层通常是一个很好的方法。当围绕thesethese之类的模式构建应用程序时,第三层的开销确实很小。

关于c# - 使用简单进样器根据需要加载装配体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34303091/

相关文章:

c# - 如何检查文件当前是否打开或正在写入 .NET?

c# - 如何将参数名称传递给方面方法

.net - GDI+实际上还是 "usable"技术吗?

wpf - 在 WPF MVVM 中绑定(bind)多绑定(bind)文本框

c# - 用户控制不透明度随可见性淡出

c# - 为什么这个 ListView CheckBox 绑定(bind)到整个 View 模型而不是行项目?

c# - 使用 OrderBy 的 EF 查询失败

c# - 在 C#/WPF 中确定控件类型的最有效方法

c# - TcpListener/TcpClient 在某些情况下停止处理数据

c# - Google Gmail API 定价