I'm using VS2017 (15.9.25) to develop a VSIX VSPackage.
The package uses the MySqlConnector 1.0.0
NuGet package, which in turn depends on System.Memory 4.5.4
NuGet package which includes the System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
assembly.
When I run my VSPackage in debug mode using the Visual Studio Experimental Instance, my plugin loads, and I can see my custom options pages and the custom commands which I'm adding to the VS context menus. But when I actually run the custom command (which tries to retrieve some data using MySqlConnector
), the database connection fails because the System.Memory
assembly fails to load.
There is some more debugging information provided in the exception message which I don't fully understand:
My app.config
contains the following binding redirect:
<dependentAssembly>
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.1" newVersion="4.0.1.1" />
</dependentAssembly>
The bin/Debug
folder and the generated .vsix
file both contain MySqlConnector.dll 1.0.0
and System.Memory.dll 4.0.1.1
.
Looking at MySqlConnector.dll
using ReSharper's Assembly Explorer, I see it references System.Memory 4.0.1.0
, which is the assembly that's failing to load.
My questions are:
FileNotFoundException
mention 4.0.1.0? Shouldn't the binding redirect cause .NET to try and load 4.0.1.1, which is definitely present?=== Pre-bind state information ===
etc) actually mean?I started with the standard VS2017 VSIX package project template, which targets .NET 4.6, and later retargeted my project to .NET Framework 4.7.2.
I thought the issue might be caused by types which are included in .NET Fx 4.7.2 but shipped separately when targeting .NET Fx 4.6, so I tried reinstalling both the MySqlConnector
and System.Memory
NuGet packages after changing target to 4.7.2. It made no difference.
I am using traditional packages.config
-style NuGet configuration.
The VSIX package uses the NuGet package MySqlConnector 1.0.0
(project home page). The package doesn't list any dependencies explicitly for .NET 4.7.2:
But it appears the dependencies for .NETFramework,Version=4.7.1
also apply to 4.7.2
, since installing MySqlConnector
results in the System.Memory 4.5.4
NuGet package also being installed. This adds a reference to the System.Memory 4.0.1.1
assembly to my VSIX VSPackage project, with Copy Local == True
:
The checkbox in the project properties for "Auto-generate binding redirects" is checked:
But, oddly, there is no <AutoGenerateBindingRedirects>
element in the csproj
file. Perhaps that element doesn't apply to VSIX projects for some reason or it became implicit behaviour at some point?
When I build my project, the System.Memory.dll
file is being copied from the NuGet packages folder into the binary output folder, as I would expect:
Inspecting the file in the binary output folder with ILSpy proves that it's the latest version, and matches the assembly binding redirect in app.config
:
// C:\git\[redacted]\bin\Debug\System.Memory.dll
// System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
// Global type: <Module>
// Architecture: AnyCPU (64-bit preferred)
// Runtime: v4.0.30319
// This assembly is signed with a strong name key.
// Hash algorithm: SHA1
Opening the .vsix
file in the output folder using 7-zip shows that the VSIX also contains System.Memory.dll
:
It seems that the answer is that .NET was trying to load the older version because VSPackage/VSIX projects do not honor binding redirects configured in app.config
(which is maintained by NuGet package install/uninstall actions), because they are plugins loaded within the context of Visual Studio's devenv.exe
, which picks up its primary binding redirect configuration from its own devenv.exe.config
file that the VSIX can't edit.
Instead, VSIX infrastructure allows for binding redirects to be configured in .pkgdef
files, which VS pays attention to when loading the VSPackage. The .pkgdef
file is an output from the VSIX build process, so directly editing it would be rather fragile. The easiest way to maintain .pkgdef binding redirects is to add assembly-scoped instances of the Microsoft.VisualStudio.Shell.ProvideBindingRedirectionAttribute
to the VSPackage project's AssemblyInfo.cs
file. The VSIX build tools will pick up those attributes and generate binding redirects in the .pkgdef
file when the VSIX is built.
After adding various ProvideBindingRedirectionAttribute
instances based on the contents of my app.config
, I can now load the MySqlConnector
DLL.