C# アセンブリのバージョンの謎を調べる（１）

C# の Assembly 周りを職業柄深く理解しないといけないのだけど、自分の理解はとっても浅いと最近感じてきたので、勉強することにした。
特に、次の分野を勉強したい

* Assembly Versioning
* Resolve assembly loads
* Strong Name and Side by side  execution
* Load and unload Assembly

あたりは抑えておいた方がよさそう

# Assembly Version 
まずは基本的なバージョニングについてみておきたい。



## 自分のライブラリが参照するライブラリがあり、それが自分と同じライブラリを使っている場合、バージョンを参照先より高くする

自分が実験してみたのですが、下記のように、自分のライブラリがあり、それが、自分のライブラリの内部で使用している別のライブラリの一部として、`Newtonsoft.Json` が
あり、自分のライブラリが参照しているプロジェクトでも、`Newtonsoft.Json` が存在するとします。自分のライブラリの中で、`Newtonsoft.Json` を使いたい場合は、
必ずそれら２つよりもバージョンを高くします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3470/3769978f-b8fd-c948-6aa0-9cfbbcecde25.png)

例えば、内部参照しているライブラリのバージョンより低いバージョンを使った場合、内部参照をしているパッケージが、`11.0.2` を使っているのに、
自分のライブラリで、`9.0.1` を使うと、両方のライブラリが `9.0.1` になります。ただし、これは使えなさそうです。`Detected package downgrade: Newtonsoft.Json from 11.0.2 to 9.0.1` 
というエラーが出てしまいます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3470/c5e3f6f6-c16e-682f-0f41-ff5d64f09da9.png)

これは、外部参照先であっても同じです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3470/78dd92db-fadc-56e1-44aa-aec5bc654fd2.png)

エラーがダブルになりました。外部参照先は、`12.0.2` です。安全のためには、自分のライブラリで参照を持たないか、それらより高いバージョンを使えばよくなります。`13.0.1` を参照してみると、
自分のライブラリの参照が`13.0.1` になるのがわかるでしょう。もちろん依存先のライブラリにはバージョンの変化はありません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3470/25af8a53-c870-5c32-58bc-f29bb5de312b.png)

削除すると、高いバージョンに合わせる感じになります。内部参照のバージョンと比べると、外部参照の方が高いバージョンなので、内部参照の
ライブラリのバージョンが高くなりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3470/76b50a26-6c18-6f08-d3af-8a23964c1245.png)

## バージョンの数字の決定

今まで実験した通り、`cscproj`ファイルにバージョンを書いていても、同じライブラリのバージョン上のものがあれば、バージョンが上がったりしているのが
わかったと思います。バージョン指定をしているのに何でとおもうかもしれません。みんながよく使っている指定は、最小バージョンを指定しているので、上がるのはありなのです。

```
    <PackageReference Include="Newtonsoft.Json" Version="11.0.2"/>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3470/caea09fe-8282-4129-fcf0-ddd26dcf0f86.png)　

* [Version ranges](https://docs.microsoft.com/en-us/nuget/concepts/package-versioning)

試しに、最大で、`9.0.3` の指定をしてみました。

```
    <PackageReference Include="Newtonsoft.Json" Version="(9.0.3]"/>
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3470/c1b5a149-8ebd-cc23-de08-d3581eeb1701.png)

元々がルールに外れるのでさんざんな結果です。折角なので適切だけど最大値あるとかやってみます。

## バージョン指定を試してみる

バージョン指定を試してみます。`csproj` ファイルを、次のように指定してみます。最小バージョンが`9.0.1` `11.0.01`未満です。

```xml:Reference.csproj
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <Version>1.0.2</Version>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Newtonsoft.Json" version="[9.0.1, 11)" />
  </ItemGroup>
</Project>
```

このプロジェクトを `pack` して、LocalNuGet に配置した後、11以上のバージョンを指定してみます。

```xml:AssemblyVersion.csproj
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <AzureFunctionsVersion>v3</AzureFunctionsVersion>
    <Version>1.0.1</Version>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
    <PackageReference Include="Referenced" Version="1.0.2" />
  </ItemGroup>

  <ItemGroup>
    <None Update="host.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Update="local.settings.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <CopyToPublishDirectory>Never</CopyToPublishDirectory>
    </None>
  </ItemGroup>
</Project>

```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3470/edbf38b8-86fe-709d-77b2-9038f281e2c4.png)

`NU1608` のワーニングなので、サプレスは出来そうですが、しっかり必要以上のバージョンアップを止めてくれました。

```
Severity	Code	Description	Project	File	Line	Suppression State
Warning	NU1608	Detected package version outside of dependency constraint: Referenced 1.0.2 requires Newtonsoft.Json (>= 9.0.1 && < 11.0.0) but version Newtonsoft.Json 12.0.2 was resolved.	AssemblyVersioning	C:\Users\tsushi\source\repos\AssemblyVersioning\AssemblyVersioning\AssemblyVersioning.csproj	1	
```

# 次回

次回はより高度な内容にする予定です。DLLのマニフェストの見かた、LoadAssemblyでロードしたときのバージョンコンフリクトの発生、
そして、LoadAssemblyしたときにDIコンテナを共有するとどのような挙動になるのか、というところをサンプルプログラムを作って
試してみたいと思います。