Assembly binding is a nightmare when you have different versions of this same NuGet. There is three approaches that I use when I have a similar problem.
- Consolidation of NuGet packages. Install the latest and missing if possible.
- Remove all "assemblyBinding" sections from the code and reinstall all NuGet packages to get a clean version of the current setup.
- Use a custom MSBuild task to fix the problem but you really don't want to use it. If you can migrate your project to .NET Core library or just .NET SDK, do it. I had to force remap it all because I had 100+ projects with bindings with the old .NET Framework which I couldn't easily migrate. Maintenance was a nightmare so I created a task based on MSBuild one.
namespace YourNamespace.Sdk
{
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml.Linq;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
public class GenerateConfigBindingRedirects : Task
{
public string ConfigFilePath { get; set; }
public ITaskItem[] SuggestedBindingRedirects { get; set; }
public override bool Execute()
{
if (this.SuggestedBindingRedirects == null || this.SuggestedBindingRedirects.Length == 0)
{
return true;
}
var doc = XDocument.Load(this.ConfigFilePath);
if (doc == null)
{
return false;
}
var runtimeNode = doc.Root.Nodes().OfType<XElement>().FirstOrDefault(e => e.Name.LocalName == "runtime");
if (runtimeNode == null)
{
runtimeNode = new XElement("runtime");
doc.Root.Add(runtimeNode);
}
else
{
return false;
}
var ns = XNamespace.Get("urn:schemas-microsoft-com:asm.v1");
var redirectNodes = from redirect in this.ParseSuggestedRedirects()
select new XElement(
ns + "dependentAssembly",
new XElement(
ns + "assemblyIdentity",
new XAttribute("name", redirect.Key.Name),
new XAttribute("publicKeyToken", GetPublicKeyToken(redirect.Key.GetPublicKeyToken())),
new XAttribute("culture", string.IsNullOrEmpty(redirect.Key.CultureName) ? "neutral" : redirect.Key.CultureName)),
new XElement(
ns + "bindingRedirect",
new XAttribute("oldVersion", "0.0.0.0-" + redirect.Value),
new XAttribute("newVersion", redirect.Value)));
var assemblyBinding = new XElement(ns + "assemblyBinding", redirectNodes);
runtimeNode.Add(assemblyBinding);
using (var stream = new StreamWriter(this.ConfigFilePath))
{
doc.Save(stream);
}
return true;
}
private static string GetPublicKeyToken(byte[] bytes)
{
var builder = new StringBuilder();
for (var i = 0; i < bytes.GetLength(0); i++)
{
builder.AppendFormat("{0:x2}", bytes[i]);
}
return builder.ToString();
}
private IDictionary<AssemblyName, string> ParseSuggestedRedirects()
{
var map = new Dictionary<AssemblyName, string>();
foreach (var redirect in this.SuggestedBindingRedirects)
{
try
{
var maxVerStr = redirect.GetMetadata("MaxVersion");
var assemblyIdentity = new AssemblyName(redirect.ItemSpec);
map.Add(assemblyIdentity, maxVerStr);
}
catch
{
}
}
return map;
}
}
}
namespace YourNamespace.Sdk
{
using System.IO;
using System.Linq;
using System.Xml.Linq;
using Microsoft.Build.Utilities;
public class RemoveRuntimeNode : Task
{
public string ConfigFilePath { get; set; }
public override bool Execute()
{
var doc = XDocument.Load(this.ConfigFilePath);
if (doc == null)
{
return false;
}
doc.Root.Nodes().OfType<XElement>().FirstOrDefault(e => e.Name.LocalName == "runtime")?.Remove();
using (var stream = new StreamWriter(this.ConfigFilePath))
{
doc.Save(stream);
}
return true;
}
}
}
From csproj:
<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PackageReference Include="Microsoft.Build.Tasks.Core" Version="16.4.0" />
</ItemGroup>
<UsingTask TaskName="RemoveRuntimeNode" AssemblyFile="$([MSBuild]::ValueOrDefault('$(YourLibPath)', '$(MSBuildThisFileDirectory)..\lib\netstandard2.0\YourNamespace.Sdk.dll'))" />
<UsingTask TaskName="GenerateConfigBindingRedirects" AssemblyFile="$([MSBuild]::ValueOrDefault('$(YourLibPath)', '$(MSBuildThisFileDirectory)..\lib\netstandard2.0\YourNamespace.Sdk.dll'))" />
<Target Name="RemoveConfigRuntimeNode" BeforeTargets="ResolveAssemblyReferences" Condition="Exists('$(MSBuildProjectDirectory)\app.config')">
<RemoveRuntimeNode ConfigFilePath="$(MSBuildProjectDirectory)\app.config" />
</Target>
<Target Name="GenerateConfigBindingRedirects" AfterTargets="RemoveConfigRuntimeNode" Condition="Exists('$(MSBuildProjectDirectory)\app.config')">
<GenerateConfigBindingRedirects ConfigFilePath="$(MSBuildProjectDirectory)\app.config" SuggestedBindingRedirects="@(SuggestedBindingRedirects)"/>
</Target>
</Project>
Update-Package -reinstall
. See how to reinstall packages – Shoop