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

时间:2014-01-02 18:32:38

标签: winforms dependency-injection domain-driven-design unity-container onion-architecture

Solution structure (Partial)

这就是解决方案的部分效果。

由于我在Winforms环境中使用Onion Architecture,因此我拥有UI,Infrastructure和Core层。使用依赖注入将所有层松散耦合。我想要实现的是每当一个表格来自例如加载帐户表单(类库),其中所有依赖项应加载到UnityContainer中,即已注册的类型。这些依赖项是Core和Infrastructure项目中的接口和实现。

我的困惑是我应该在哪里编写代码来注册依赖项?这个应用程序的组合根是什么?请注意,例如,表格帐户表单,HR表单等都使用主Windows应用程序中的反射加载,该应用程序仅引用基本表单项目。

在Eben Roux的建议之后

以下是我在加载程序集时执行连线代码的方法:

 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

所以现在我的问题是:

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

1 个答案:

答案 0 :(得分:1)

使用这种类型的插件架构,您最终会有多个组合根(排序)。很可能只有你的插件知道并且可以连线的依赖关系。

因此,您的部分架构应该是加载插件。这可能发生在主要应用程序中的连接位(组合根)中,这将使每个插件有机会执行连接。

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

public interface IDependencyWiring
{
    public void WireUp(IDependencyContainer container); // <-- changed to conform to update
}

然后在主要作品根目录中:

foreach (var plugin in plugins)
{
    var wiring = plugin as IDependencyWiring;

    if (wiring != null)
    {
        wiring.WireUp(myContainer);
    }
}

我希望这是有道理的。

<强>更新

首先我会使用安全演员。 VB.NET世界中的TryCast。您可以使用依赖项iversion通过使用您自己的接口从实际插件中删除Unity。像这样:

public interface IDependencyContainer
{
    void Register(Type type);
    void Register<T>();
    void Resolve(Type type);
    void Resolve<T>();
}

好吧,你会添加你需要的东西。然后,当我在顶部public void WireUp(IContainer container);进行操作时,将参考文件传递给电子邮件中的容器。

Resolve行为 有点问题,因为您似乎是朝着 服务定位器 的方向前进。尝试通过使用构造函数(或其他)注入来获得容器完成的尽可能多的解析。当然,这适用于 Singleton 组件。对于 Transient ,我宁愿使用 Singleton 工厂来接收IDependencyContainer的实例(这样也会被注册),它会执行为你解决(创造,真的)。