4
votes

I have a solution with an azure cloud project and a worker role project (+ some auxiliary projects). When I debug the worker locally, I get this exception:

Could not load file or assembly 'System.Runtime.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)":"System.Runtime.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a

FusionLog:

=== Pre-bind state information ===
LOG: DisplayName = System.Runtime.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
 (Fully-specified)
LOG: Appbase = file:///C:/Azure/roles/[worker project]/approot
LOG: Initial PrivatePath = C:\Azure\roles\[worker project]\approot
Calling assembly : Microsoft.Extensions.Configuration, Version=1.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60.
===
LOG: This bind starts in default load context.
LOG: Using application configuration file: C:\Azure\roles\[worker project]\approot\[worker assembly].dll.config
LOG: Using host configuration file: 
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework64\v4.0.30319\config\machine.config.
LOG: Redirect found in application configuration file: 4.0.0.0 redirected to 4.1.1.0.
LOG: Post-policy reference: System.Runtime.Extensions, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
LOG: Attempting download of new URL file:///C:/Azure/roles/[worker project]/approot/System.Runtime.Extensions.DLL.
WRN: Comparing the assembly name resulted in the mismatch: Minor Version
ERR: Failed to complete setup of assembly (hr = 0x80131040). Probing terminated.

What I see in build output:

    2>  Task "Message"
    2>      CopyLocalDependencies=[solution]\packages\System.Runtime.Extensions.4.3.0\lib\net462\System.Runtime.Extensions.dll
    2>  Task "Message"
    2>      CopyLocalDependencies=C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.2\Facades\System.Runtime.Extensions.dll
    ...
    2>  Task "Message"
    2>      WorkerFiles=[solution]\packages\System.Runtime.Extensions.4.3.0\lib\net462\System.Runtime.Extensions.dll -> System.Runtime.Extensions.dll
    2>  Task "Message"
    2>      WorkerFiles=C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.2\Facades\System.Runtime.Extensions.dll -> System.Runtime.Extensions.dll
    ...
    2> Copying file from "[solution]\packages\System.Runtime.Extensions.4.3.0\lib\net462\System.Runtime.Extensions.dll" to "[solution]\[azure project]\obj\Debug\[worker project]\System.Runtime.Extensions.dll".
    2> Copying file from "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.2\Facades\System.Runtime.Extensions.dll" to "[solution]\[azure project]\obj\Debug\[worker project]\System.Runtime.Extensions.dll".

Why does MSBuild copy GAC's assembly to a local folder? How to change this behaviour? I do not have a direct reference to the GAC's System.Runtime.Extensions. Or how to change the order of copying assemblies?

What I tried:

  1. Add the property in the worker project DoNotCopyLocalIfInGac = true
  2. Add a direct reference to the nuget package System.Runtime.Extensions

UPD Found similar problems

1
Looks like you've been hacking it heavily before you gave up, requires guessing. I'd assume you better remove the BindingRedirect from the app.config file to force 4.1.1.0 to be loaded to normalize this a bit. Copy Local=true on the assembly reference needs to be reset as well. This is a retargetable assembly, it should load 4.0.0.0 from the GAC.Hans Passant
@HansPassant It helped but I am afraid that some day or other I will get the exception "System.MissingMethodException : Method not found…" because of old assembly. Some nuget packages depend on System.Runtime.Extensions package. Also removing bindingRedirects (which are added automatically by default) is laborious, because I have to remove redirects one by one -- some other redirects are necessary because nuget assemblies are properly copyed in approot folder.Evgeni Nabokov
I need a method to stop copying GAC's asemblies or set the order of copying (first from GAC, then from packages).Evgeni Nabokov
Avoiding a MissingMethodException is the exact point of these retargetable assemblies. You are trying to defeat the feature that was meant to keep you out of this trouble. You need to stop fighting it and explain what the real problem looks like. "I am afraid that some day..." is not a real problem that anybody here can solve.Hans Passant
@HansPassant I just pointed at a side effect of such solution of my problem. The real problem is that MSBuild poorly selects same assemblies from different sources, i.e. MSBuild makes wrong decisions which version to take. Actully, MSBuild takes all versions and copies them into output folder. The order of copying is significant in my case.Evgeni Nabokov

1 Answers

2
votes

It is a MSBuild 14.0 bug. I found the issue "ImplicitlyExpandDesignTimeFacades target should only add facades if a higher version isn't already referenced". The solution is to modify the target ImplicitlyExpandDesignTimeFacades.

What I did:

  1. Created a separate file with modified ImplicitlyExpandDesignTimeFacades target.
  2. Put it somewhere near the worker's csproj file.
  3. Imported this target in the worker's csproj file:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0"
DefaultTargets="Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
....  
<Import Project="..\..\ImplicitlyExpandDesignTimeFacades.targets" />
</Project>