‘AutoMapper’ already has a dependency defined for ‘Microsoft.CSharp’


This is how the story begins. On our build server we are using a JetBrains Resharper CLT to generate a code analysis report. In one of the projects build we started getting the following exception log:

Executing the powershell script: C:\install\TFS \1.0.691\resharp.ps1
JetBrains Inspect Code 2016.1.2
Running in 64-bit mode, .NET runtime 4.0.30319.42000 under Microsoft Windows NT 6.2.9200.0
'AutoMapper' already has a dependency defined for 'Microsoft.CSharp'.
--- EXCEPTION #1/2 [InvalidOperationException]
Message = "'AutoMapper' already has a dependency defined for 'Microsoft.CSharp'."
ExceptionPath = Root.InnerException
ClassName = System.InvalidOperationException
HResult = COR_E_INVALIDOPERATION=80131509
Source = NuGet.Core
StackTraceString = "
  at NuGet.Manifest.ValidateDependencySets(IPackageMetadata metadata)
     at NuGet.Manifest.ReadFrom(Stream stream, IPropertyProvider propertyProvider, Boolean validateSchema)
     at NuGet.LocalPackage.ReadManifest(Stream manifestStream)
     at NuGet.OptimizedZipPackage.EnsureManifest()
     at NuGet.SharedPackageRepository.OpenPackage(String path)
     at NuGet.LocalPackageRepository.GetPackage(Func`2 openPackage, String path)
     at NuGet.LocalPackageRepository.<>c__DisplayClass13.<FindPackage>b__f(String path)
     at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
     at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
     at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 source)
     at NuGet.LocalPackageRepository.FindPackage(Func`2 openPackage, String packageId, SemanticVersion version)
     at NuGet.SharedPackageRepository.FindPackage(String packageId, SemanticVersion version)
     at JetBrains.ProjectModel.Packages.SharedPackageRepositoryInTemp.FindPackage(String packageId, SemanticVersion version)
     at NuGet.PackageRepositoryExtensions.FindPackage(IPackageRepository repository, String packageId, SemanticVersion version, IPackageConstraintProvider constraintProvider, Boolean allowPrereleaseVersions, Boolean allowUnlisted)
     at NuGet.PackageReferenceRepository.GetPackage(PackageReference reference)
     at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
     at System.Linq.Enumerable.WhereEnumerableIterator`1.MoveNext()
     at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
     at JetBrains.ProjectModel.Packages.NuGetSolutionManager.<>c__DisplayClass7.<GetInstalledPackages>b__6()
     at JetBrains.Util.ILoggerEx.Catch[TValue](ILogger th?s, Func`1 F, ExceptionOrigin origin, LoggingLevel loggingLevel)

Someone on StackOverflow already pointed that the error is related to the outdated Nuget. We do not directly call nuget.exe, but Resharper CLT does use Nuget.Core.dll library. I easily found a valid package on nuget.org and happily replaced the file in the Resharper CLT installation folder. And then Resharper CLT stopped working 🙂 I tried adding assembly forwarding directives in the inspectcode.exe.config file, but with no success.

There was nothing left, but start modifying the Nuget.Code.dll binary. But first let’s have a look at what the whole problem was about. Automapper version 5.0.0.0 has the following nuspec file (unimportant parts are stripped):

<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
  <metadata>
    <id>AutoMapper</id>
    <version>5.0.0</version>
    <authors>Jimmy Bogard</authors>
    ...
    <dependencies>
      <group targetFramework=".NETFramework4.5" />
      <group targetFramework=".NETStandard1.1">
        <dependency id="Microsoft.CSharp" version="[4.0.1, )" />
        <dependency id="System.Collections" version="[4.0.11, )" />
        ...
      </group>
      <group targetFramework=".NETStandard1.3">
        <dependency id="Microsoft.CSharp" version="[4.0.1, )" />
        <dependency id="System.Collections" version="[4.0.11, )" />
        ...
      </group>
    </dependencies>
  </metadata>
</package>

As you can see there are three dependencies sets defined for three different versions of the .NET framework: .NETFramework4.5, .NETStandard1.1 and .NETStandard1.3. Nuget version provided with Resharper CLT (2.10.1.766) does not recognize the last two versions of .NET, and their dependencies are put into the same set, defined as ‘Unknown .NET version’. When validating the package metadata, Nuget checks if there are no duplicate dependencies. If it finds any, an exception like the one in the title is thrown.

It is time to debug the inspectcode.exe. I started the process under the dnSpy debugger and found that the first chance exception is thrown from the ValidateDependencySets method. It didn’t take much time to find that the interesting code lies in the VersionUtility class. More specifically this class contains a static readonly dictionary with all the known .NET Framework versions:

private static readonly Dictionary<string, string> _knownIdentifiers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
	{
		"NET",
		".NETFramework"
	},
	{
		".NET",
		".NETFramework"
	},
	{
		"NETFramework",
		".NETFramework"
	},
	...
}

When I compared values on this list with the newest Nuget.Core.dll (2.12.0.18) I spotted the missing values for .NET Standard:

{
	".NETStandard",
	"netstandard"
},
{
	".NETStandardApp",
	"netstandardapp"
},

So I opened the static constructor for the VersionUtility class in the dnSpy editor and added 8 new instructions:

edit-method-body

new-instructions

Then I overwrite the assembly (File->Save Module…) (notice the public key stays the same!) and tried to run the inspectcode.exe. Unfortunately, I received a strange error:

The type initializer for 'NuGet.VersionUtility' threw an exception. Could not load type 'System.Collections.Generic.Dictionary`2' from assembly 'NuGet.Core, Version=2.10.1.766, Culture=neutral, PublicKeyToken=31bf3856ad364e35'.

--- EXCEPTION #1/3 [TypeLoadException]
The type initializer for 'NuGet.VersionUtility' threw an exception. Could not load type 'System.Collections.Generic.Dictionary`2' from assembly 'NuGet.Core, Version=2.10.1.766, Culture=neutral, PublicKeyToken=31bf3856ad364e35'.

--- EXCEPTION #1/3 [TypeLoadException]
Message = "Could not load type 'System.Collections.Generic.Dictionary`2' from assembly 'NuGet.Core, Version=2.10.1.766,
Culture=neutral, PublicKeyToken=31bf3856ad364e35'."
ExceptionPath = Root.InnerException.InnerException
ClassName = System.TypeLoadException
HResult = COR_E_TYPELOAD=80131522
Source = NuGet.Core
TypeLoadClassName = System.Collections.Generic.Dictionary`2
TypeLoadAssemblyName = "NuGet.Core, Version=2.10.1.766, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
TypeLoadResourceID = -2146233054
StackTraceString = "at NuGet.VersionUtility..cctor()"

For some reason CLR was looking for the Dictionary class in the NuGet.Core.dll. What’s going on here? Let’s have a look again on the method body in dnSpy. My newly added methods are shown as: [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, !1), while all other methods are decompiled as: [mscorlib]System.Collections.Generic.Dictionary`2<string, string>::Add(!0, !1). When I switched the language to IL I immediately spotted the difference in the generated op codes:

opcodes

dnSpy mistakenly generated an invalid code for this particular instructions. Fortunately we know the correct code and we have a binary editor 🙂 I found a place in the Nuget.Core.dll where there was a lot of 6FDF01000A and only two 6F5307000A among them:

opcodes-assembly

I manually updated my two methods opcodes. Finally, everything started working again and I’m waiting for the Jetbrains team to release a new version of the Resharper CLT, so I won’t need to add new .NET Framework versions to the Nuget.Core.dll 🙂

UPDATE 2016.08.26: Well, it wasn’t too long till I found a new version of Resharper CLT available. Jetbrains released it on 18th of August so it is quite fresh and has the updated Nuget version. But at least we could hack a little bit 🙂

‘AutoMapper’ already has a dependency defined for ‘Microsoft.CSharp’

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s