VB.NET + Windows Forms + Unity.Container で Dependency Injection（依存性注入）

※ 個人blogに投稿した記事（投稿日：2019/12/11）をQiitaに移行しました

# 前置き

この記事は[Qiita Visual Basic Advent Calendar 2019](https://qiita.com/advent-calendar/2019) の11日目の記事です。

.NET Core 3.0 では WinForms が対応され、Visual Basic 16.0 (Visual Studio 2019) もリリースされました。
Visual Studio 2019 16.4 では、まだ .NET Core 3.1 + WinForms + Visual Basic のテンプレートは対応されていないようでしたが、
.NET Core 3系におけるVBの対応は前向きに進んでいるように見えるので、今後の展開に注目しています。

https://developercommunity.visualstudio.com/idea/750543/add-visual-basic-support-to-net-core-3-wpfwindows.html

https://devblogs.microsoft.com/dotnet/announcing-net-core-3-1/

で（唐突）、Advent Calendar 用のネタが何か無いか探したところ、
「[Adaptive Code](https://www.amazon.co.jp/dp/B07DJ2BL4Y)」で読んだ Unitiy を利用した Dependency Injection を VB + WinForms で行えるか試してみることにしました。

# はじめに

まず、以下のコードはForm1の中で`repository`のインスタンスをNewしているので、
Form1は`repository`に依存している関係にあります。

```vb
Public Class Form1

    Private ReadOnly repository As New Repository

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        MessageBox.Show(Join(repository.getAll.ToArray, ","))
    End Sub

End Class
```

# Unitiy.Container を用いた依存性注入

ここから依存性注入の形に変えていくには、`Repository`を`IRepository`インターフェースで抽象化し、
コンストラクタの引数でインスタンスを渡す形に変更します。

```vb
Public Class Form1

    Private ReadOnly repository As IRepository

    Sub New(repository As IRepository)

        ' この呼び出しはデザイナーで必要です。
        InitializeComponent()

        ' InitializeComponent() 呼び出しの後で初期化を追加します。
        Me.repository = repository
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        MessageBox.Show(Join(repository.getAll.ToArray, ","))
    End Sub
End Class
```

```vb
Public Interface IRepository

    Function getAll() As List(Of String)

End Interface
```

```vb
Public Class Repository
    Implements IRepository

    Public Function getAll() As List(Of String) Implements IRepository.getAll
        Return New List(Of String) From {"1", "2", "3"}
    End Function
End Class
```

NuGet で`Unity.Container`をインストールします。

![20191209233730.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23789/6bd366eb-4009-e505-86fc-625fdd1c0141.png)

メインエントリポイント`Module Program`を手動で追加して、
そこにコンテナの初期化とインターフェイスとインスタンス生成の紐付けを記述します。
名前空間`Unity`のImportを忘れずに。

```vb
Imports Unity

Public Module Program

    <STAThread()>
    Sub Main()

        Dim container As IUnityContainer = New UnityContainer

        container.RegisterInstance(Of IRepository)(New Repository())

        Application.EnableVisualStyles()
        Application.SetCompatibleTextRenderingDefault(False)
        Application.Run(container.Resolve(Of Form1))
    End Sub
End Module
```

ポイントは`container.Resolve(Of Form1)`により、
Form1のコンストラクタ`Sub New(repository As IRepository)`の引数`repository`に
Newされた`Repository`のインスタンスが渡されることです。 
依存性をクラスの外側から注入できたことになります。

# Unity.Configuration を使って、App.Config でインターフェイスとインスタンスを紐付け

ここからさらに、Unity.Configuration を使って、
App.Config でインターフェイスとインスタンスの紐付けを記述する形に変更します。
NuGetから`Unity.Configuration`をインストールします。

![20191209235045.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23789/303bad8c-321a-dcd1-f55d-831c804fdcc0.png)


インストール後、`Program`モジュールで名前空間`Microsoft.Practices.Unity.Configuration`をインポートします。
設定ファイルの値をContainerに読み込む拡張メソッド`LoadConfiguration`が`Microsoft.Practices.Unity.Configuration`に存在するためです。

```vb
Imports Unity
Imports Microsoft.Practices.Unity.Configuration

Public Module Program

    <STAThread()>
    Sub Main()

        Dim container As IUnityContainer = New UnityContainer
        container.LoadConfiguration()

        Application.EnableVisualStyles()
        Application.SetCompatibleTextRenderingDefault(False)
        Application.Run(container.Resolve(Of Form1))
    End Sub
End Module
```

App.Config には以下を追加します。

```config
<configSections>
    <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Unity.Configuration" />
</configSections>
<startup>
   <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
</startup>
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
 <assembly name="WindowsAppVb" />
 <namespace name="WindowsAppVb" />
<container>
  <register type="IRepository" mapTo="Repository" />
</container>
</unity>
```

※`assembly name`も記述しないと実行時に
「The type name or alias ”xxxxx“ could not be resolved. Please check your configuration file and verify this type name.”」 
が発生しました。

以上、「VB.NET + Windows Forms + Unity で Dependency Injection（依存性注入）」でした。