在WinForms应用程序的Unity容器中加载依赖项

在WinForms应用程序的Unity容器中加载依赖项,winforms,dependency-injection,domain-driven-design,unity-container,onion-architecture,Winforms,Dependency Injection,Domain Driven Design,Unity Container,Onion Architecture,这就是解决方案的部分外观 因为我在Winforms环境中使用Onion架构,所以我有UI、基础结构和核心层。所有层都使用依赖项注入进行松散耦合。我想要实现的是,无论何时从Accounts Forms(类库)加载表单,该表单的所有依赖项都应该加载到UnityContainer中,即注册类型。这些依赖关系是核心和基础设施项目中存在的接口和实现 我的困惑是,我应该在哪里编写代码来注册依赖项?此应用程序的合成根是什么?请注意,来自帐户表单、人力资源表单等的表单都是使用主Windows应用程序中的反射加载

这就是解决方案的部分外观

因为我在Winforms环境中使用Onion架构,所以我有UI、基础结构和核心层。所有层都使用依赖项注入进行松散耦合。我想要实现的是,无论何时从Accounts Forms(类库)加载表单,该表单的所有依赖项都应该加载到UnityContainer中,即注册类型。这些依赖关系是核心和基础设施项目中存在的接口和实现

我的困惑是,我应该在哪里编写代码来注册依赖项?此应用程序的合成根是什么?请注意,来自帐户表单、人力资源表单等的表单都是使用主Windows应用程序中的反射加载的,该应用程序仅引用基本表单项目

在Eben Roux的建议之后

以下是加载程序集时执行wireup代码的方式:

 Dim assemb As System.Reflection.Assembly    
              ...
              ... 
 If assemb IsNot Nothing Then
     Dim type As Type = GetType(IDependencyWiring)
     Dim modules As List(Of Type) = assemb.GetTypes().Where(Function(p) type.IsAssignableFrom(p) AndAlso p.IsClass).ToList()

     For Each [module] As Type In modules
         Dim argTypes As Type() = New Type() {}
         Dim cInfo As ConstructorInfo = [module].GetConstructor(argTypes)
         Dim dependencyWiringModule As IDependencyWiring = DirectCast(cInfo.Invoke(Nothing), IDependencyWiring)
         dependencyWiringModule.WireUp()
     Next
 End If
以下是具有WireUp方法的模块:

Public Class AccountModule : Implements IDependencyWiring

    Private Shared Container As IUnityContainer

    Public Sub New()
        Container = New UnityContainer()
    End Sub

    Public Sub WireUp() Implements IDependencyWiring.WireUp
        Container.RegisterType(Of IInterface1, Class1)()
        Container.RegisterType(Of IInterface2, Class2)()
        Container.RegisterType(Of IInterface3, Class3)()
        Container.RegisterType(Of IInterface4, Class4)()
    End Sub

    Public Shared Function Resolve(typeToResolve As Type) As Object
        Return Container.Resolve(typeToResolve.GetType())()
    End Function
End Class
因此,我现在的问题是:

  • 将容器存储为共享并使用它通过resolve方法解析依赖项是否是正确的方法
  • 我封装容器解析行为的方式存在问题。正确的语法是什么?我不想在每个表单上引用Unity来调用Resolve方法,所以我封装了我自己的Resolve方法。通过这种方式,如果我想更改IOC容器,而无需更改容器引用,我可以轻松地用另一个替换AccountModule

  • 使用这种类型的插件体系结构,您实际上会得到多个组合根(各种类型)。很可能会有一些依赖关系,只有您的插件知道并且可以连接起来

    因此,架构的一部分应该是加载插件。这可能发生在主应用程序中的某个位置,即连接位(组合根),这将为每个插件提供执行其连接的机会

    由于并非所有插件都需要布线,因此可以使用单独的接口来明确这一点:

    public interface IDependencyWiring
    {
        public void WireUp(IDependencyContainer container); // <-- changed to conform to update
    }
    
    我希望这是有道理的

    更新

    首先,我会使用安全石膏<在VB.NET世界中的code>TryCast。通过使用自己的接口,您可以使用dependency-iversion从实际插件中去除Unity。大概是这样的:

    public interface IDependencyContainer
    {
        void Register(Type type);
        void Register<T>();
        void Resolve(Type type);
        void Resolve<T>();
    }
    
    公共接口IDependencyContainer
    {
    无效登记簿(类型);
    无效寄存器();
    无效解析(类型);
    无效解析();
    }
    
    好吧,你可以添加你需要的。然后在wire up中传入对容器的引用,就像我在top
    public void wire up(IContainer容器)中所做的那样


    Resolve
    行为有些问题,因为您似乎正朝着服务定位器的方向前进。通过使用构造函数(或其他)注入,尝试让容器完成尽可能多的解析。当然,这适用于单例组件。对于暂时的我宁愿使用一个单例工厂,它接收
    IDependencyContainer
    (这样它也会被注册),并为您进行解析(创建,真的)。

    插件是指帐户表单(类库)中加载的单个WinForm?那么我需要用这个接口继承表单吗?还可以在页面中实现wireup方法?插件通常在略高于表单的级别上工作。它代表一个组件(帐户、人力资源等),在插件中,您通常会拥有一些在主shell中可用的特定功能。因此,您不希望在表单上实现该接口。而是在类似于
    AccountModule
    的东西上,让它来完成布线以及与在shell中提供模块功能相关的任何其他功能。感谢您的回答,Eben。我已经更新了我的问题,以显示我现在采取的方法。然而,我仍然有一些困惑。请看一看
    public interface IDependencyContainer
    {
        void Register(Type type);
        void Register<T>();
        void Resolve(Type type);
        void Resolve<T>();
    }