shimapapa.io

.NET,VB,C#,AzureなどMS関連中心の技術ブログ

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

前置き

この記事はQiita Visual Basic Advent Calendar 2019 の11日目の記事です。

qiita.com

.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の対応は前向きに進んでいるように見えるので、今後の展開に注目しています。

developercommunity.visualstudio.com

devblogs.microsoft.com

で(唐突)、Advent Calendar 用のネタが何か無いか探したところ、
「Adaptive Code」で読んだ Unitiy を利用した Dependency Injection を VB + WinForms で行えるか試してみることにしました。

rikupapa-shima.hatenablog.com

はじめに

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

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 を用いた依存性注入

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

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
Public Interface IRepository

    Function getAll() As List(Of String)

End Interface
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をインストールします。

f:id:rikupapa-shima:20191209233730p:plain

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

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をインストールします。

f:id:rikupapa-shima:20191209235045p:plain

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

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 には以下を追加します。

<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(依存性注入)」でした。

おわりに

VB.NETでWinForm、ASP.NET WebFormの保守開発をしているVBerの皆様、
いやいや、ていうかVB6.0の保守開発しているんですけど、というVBerの皆様、 良いお年を。来年も頑張りましょう。