dotnet / sdk

Core functionality needed to create .NET Core projects, that is shared between Visual Studio and CLI
https://dot.net/core
MIT License
2.66k stars 1.06k forks source link

Binding redirects are not generated automatically even with AutoGenerateBindingRedirects #1351

Open JoseFMP opened 7 years ago

JoseFMP commented 7 years ago

There are two issues here:

`<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net462</TargetFramework>
    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
    <PackageReference Include="WindowsAzure.Storage" Version="7.2.1" />
  </ItemGroup>
</Project>`
Produces a warning message saying: 

 `1>Consider app.config remapping of assembly "Newtonsoft.Json, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed" from Version "6.0.0.0" [] to Version "9.0.0.0" [C:\Users\me\.nuget\packages\newtonsoft.json\9.0.1\lib\net45\Newtonsoft.Json.dll] to solve conflict and get rid of warning.

1>C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin\Microsoft.Common.CurrentVersion.targets(1964,5): warning MSB3276: Found conflicts between different versions of the same dependent assembly. Please set the "AutoGenerateBindingRedirects" property to true in the project file. For more information, see http://go.microsoft.com/fwlink/?LinkId=294190.`

The information in the link says I should enable `AutoGenerateBindingRedirects`, but indeed `<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>`  is activated.

It looks like the <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> is somehow overlooked.

dsplaisted commented 7 years ago

Try adding an app.config file to your project. I think the issue is that without that, it won't generate binding redirects. Also, projects using Microsoft.NET.Sdk should have the auto generate property enabled by default, so you probably don't need that in your project file.

JoseFMP commented 7 years ago

@dsplaisted Thank you for your answer. I tried with the app.config file in the first place, in a project with the old csproj format. Again ignored.

I used binding redirects in the past and this always worked fine. No clue what is wrong now.

dsplaisted commented 7 years ago

Try adding app.config to a project in the new format.

Sent from my Windows 10 phone

From: Jose Sent: Monday, June 19, 2017 3:47 AM To: dotnet/sdk Cc: Daniel Plaisted; Mention Subject: Re: [dotnet/sdk] Binding redirects are not generated automaticallyeven with AutoGenerateBindingRedirects (#1351)

@dsplaisted Thank you for your answer. I tried with the app.config file in the first place, in a project with the old csproj format. Again ignored. I used binding redirects in the past and this always worked fine. No clue what is wrong now. — You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or mute the thread.

nguerrera commented 7 years ago

@dsplaisted The app.config is not required to exist in source, it will still be generated.

@Jose-CF I just tried with the exact project in the bug description and it created bin\Debug\net462*.exe.config with:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

Can you tell me the VS version you have as indicated in Help -> About Microsoft Visual Studio?

JoseFMP commented 7 years ago

@nguerrera Over here not working. Already when building in the build messages msbuild complains about the broken dependency and suggest to add the binding redirect. Ironically the binding redirect is already there!
Latest VS 2017 community, updated in the last 24 hours.

Notice however, the thing builds, and if I try to run it with mono, it will run flawlessly.

nguerrera commented 7 years ago

@Jose-CF on https://github.com/Microsoft/msbuild/issues/1310#issuecomment-309622789 you indicate that the workaround for getting binding redirects for libraries worked for you. Is that the same issue as this? Note that the project you put in the description of this bug has output type winexe so it works without a workaround. I am unable to repro the issue on any version of VS 2017 with that project.

JoseFMP commented 7 years ago

@nguerrera Yes, after my last message I was trying new solutions and the one proposed in here worked for me. So, none of the other solutions had any effect. The only thing that created the redirects was adding the target:

<Target Name="ForceGenerationOfBindingRedirects"
          AfterTargets="ResolveAssemblyReferences"
          BeforeTargets="GenerateBindingRedirects"
          Condition="'$(AutoGenerateBindingRedirects)' == 'true'">
    <PropertyGroup>
      <GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
    </PropertyGroup>
  </Target>

Without that the error will happen whenever I run the build in Windows. It happens both if the conflict is in a dependent dll or in the .exe (no difference at all).

nguerrera commented 7 years ago

That workaround is only be needed when the output type is not exe or winexe. GenerateBindingRedirectsOutputType would already be set to true for WinExe (as in your example above) here: https://github.com/Microsoft/msbuild/blob/dc1f683b20dc17266c4e4b47b4b03dd7a9acef81/src/Tasks/Microsoft.Common.CurrentVersion.targets#L324.

Were you actually seeing the issue with the project that you put here in the bug description or just another project that outputs a dll and not an exe?

JoseFMP commented 7 years ago

The redirects were not created either for libraries or for .exe. <OutputType>Exe</OutputType> Does not make any difference over here. I understand it should work without the target. But indeed it did not. Either library or exe. Using msbuild Microsoft (R) Build Engine version 15.2.0.0

nguerrera commented 7 years ago

So the exact project in this bug's description doesn't work without a target for you?

JoseFMP commented 7 years ago

Exactly. It only works if I set ` <Target Name="ForceGenerationOfBindingRedirects" AfterTargets="ResolveAssemblyReferences" BeforeTargets="GenerateBindingRedirects" Condition="'$(AutoGenerateBindingRedirects)' == 'true'">

true

`

dazinator commented 7 years ago

I have a similar issue, with binding redirect not being added for a PackageReference. not sure if you want me to detail it here or create a seperate issue - but here goes:

I have a web application <Project Sdk="Microsoft.NET.Sdk.Web"> with a project reference to a library application: <Project Sdk="Microsoft.NET.Sdk"> The library project has a PackageReference to a nuget package that has a dependency constraint Microsoft.Extensions.DependencyInject.Abstractions >= 1.0.0. I have then upgraded this dependency to version 1.0.2 (within the library project): image

I have fully cleaned (deleted bin folder) and rebuilt the web application. In the bin folder is: Microsoft.Extensions.DependencyInject.Abstractions version 1.0.2 as expected. When my application runs here is the fusion log from the exception:

=== Pre-bind state information ===
LOG: DisplayName = Microsoft.Extensions.DependencyInjection.Abstractions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
 (Fully-specified)
LOG: Appbase = file:///C:/Users/darrell.tunnell/Source/Repos/gluon/src/Gluon.WebApplication/bin/Debug/net46/
LOG: Initial PrivatePath = NULL
Calling assembly : Hangfire.AspNetCore, Version=1.6.12.0, Culture=neutral, PublicKeyToken=null.
===
LOG: This bind starts in default load context.
LOG: Using application configuration file: C:\Users\darrell.tunnell\Source\Repos\gluon\src\Gluon.WebApplication\bin\Debug\net46\Gluon.WebApplication.exe.Config
LOG: Using host configuration file: 
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework\v4.0.30319\config\machine.config.
LOG: Post-policy reference: Microsoft.Extensions.DependencyInjection.Abstractions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
LOG: Attempting download of new URL file:///C:/Users/darrell.tunnell/Source/Repos/gluon/src/Gluon.WebApplication/bin/Debug/net46/Microsoft.Extensions.DependencyInjection.Abstractions.DLL.
WRN: Comparing the assembly name resulted in the mismatch: Build Number
ERR: Failed to complete setup of assembly (hr = 0x80131040). Probing terminated.

Checking the binding redirects in the generated config xml file - there has been no assembly binding generated for Microsoft.Extensions.DependencyInjection.Abstractions.

Here is my VS version:

image

EvgenyMuryshkin commented 7 years ago

Hi, I understand that my solution for this problem is rather weird, and there is a better options, but I had nothing else to do :( I have net standard 1.6 library, and unit test that must be run on windows, so it is test project on .NET 4.7 I referenced my library from test project, it builds fine, but it did not run. It started complaining on missing assemblies, on version mismatch etc. Over the time I accumulated about 40 references and binding redirects on previous version (ns1.4 and .NET 4.6), so after upgrade I was horrified with amount of work to do in order to fix that. Auto generate bindidngs flag did not have any effect. So I ended up with next solution: I run my test, catch exception and append binding redirect to app.config, or report missing reference and manually add them from nuget (repeat until satisfied). That took about 15 minutes to complete. It is rather dodgy solution, but solved the issue. It also somehow produced multiple redirects for different versions, which is a bit weird, so check final app.config for duplicates

using System; using System.Reflection; using System.Linq; using System.Text.RegularExpressions; using System.Windows; using System.IO; using System.Collections.Generic; using System.Net.Http; using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Tests { [TestClass] public class BindingRedirect { [TestMethod] public void BindingRedirect_Manual() { var tmp = context;

        Func<string,string> formatRedirect = (message) =>
        {
            var match = Regex.Match(message, "'([a-zA-Z._0-9]*),.*=([a-zA-Z-0-9]*)'");

            var assembly = Assembly.Load(match.Groups[1].Value);
            var name = assembly.GetName();
            var publicKey = string.Join("", name.GetPublicKeyToken().Select(b => b.ToString("X2")));

            var parts = new[] {
                    "<dependentAssembly>",
                    $"\t<assemblyIdentity name=\"{name.Name}\" publicKeyToken=\"{publicKey}\" culture=\"neutral\" />",
                    $"\t<bindingRedirect oldVersion=\"0.0.0.0-{name.Version}\" newVersion=\"{name.Version}\"/>",
                    "</dependentAssembly>"
                };

            var redirect = string.Join(Environment.NewLine, parts);

            var configPath = @"<PATH_TO_APP_CONFIG>";
            var configLines = File.ReadAllLines(configPath);
            var newLines = configLines.Select(l =>
            {
                if (l.Contains("</assemblyBinding>"))
                {
                    return parts.Union(new[] { l });
                }

                return new List<string>() { l };
            }).SelectMany(l => l);

            File.WriteAllLines(configPath, newLines);

            return redirect;
        };

        Action<Exception> dispatchException = null;
        dispatchException = (ex) =>
        {
            switch(ex)
            {
                case FileNotFoundException fnfe:
                    {
                        var name = fnfe.FileName.Split(new[] { ',' }).First();
                        Clipboard.SetText(name);
                        Assert.Fail($"No reference: {name}");
                    }
                    break;
                case ReflectionTypeLoadException rtle:
                    {
                        var messages = rtle.LoaderExceptions.GroupBy(e => e.Message);

                        var redirects = messages.Select(group => formatRedirect(group.Key));

                        var allRedirects = string.Join(Environment.NewLine, redirects);

                        Clipboard.SetText(allRedirects + Environment.NewLine);

                        Assert.Inconclusive(allRedirects);
                    }
                    break;
                case FileLoadException fle:
                    {
                        var redirect = formatRedirect(fle.Message);
                        Clipboard.SetText(redirect + Environment.NewLine);
                        Assert.Inconclusive(redirect);
                    }
                    break;
                case AggregateException ae:
                    {
                        dispatchException(ae.InnerException);
                    }
                    break;
                default:
                    Assert.Fail(ex.ToString());
                    break;
            }
        };

        try
        {
            var assembly = Assembly.Load("<ASSEMBLY_NAME>");
            foreach(var t in assembly.GetTypes())
            {
                if( t.IsClass && !t.IsGenericTypeDefinition && !t.IsSealed )
                {
                    var ctor = t.GetConstructors().SingleOrDefault(c => !c.GetParameters().Any());
                    if( ctor != null)
                    {
                        var instance = Activator.CreateInstance(t);
                        if (instance == null)
                            Assert.Fail("");
                    }
                }
            }

            // DO SOMETHING ELSE WITH CLASSES
        }
        catch (Exception ex)
        {
            dispatchException(ex);
        }
    }
}

}

MichaelKetting commented 6 years ago

I have the same issue with library types. Since this issue looks like it only concerns itself with EXE projects, I've created a new issue here: https://github.com/dotnet/sdk/issues/1595

tompazourek commented 6 years ago

Same issue here with OutputType Exe. My workaround is to switch the project to Class Library, generate the redirects, and then switch it to Exe again.

Bananas-Are-Yellow commented 6 years ago

Everyone once in a while I find that my binding redirects are not appearing in the .exe.config file. The solution is to delete the 'obj' folder. Then it works again.

JackGrinningCat commented 5 years ago

That workaround is only be needed when the output type is not exe or winexe. GenerateBindingRedirectsOutputType would already be set to true for WinExe (as in your example above) here: https://github.com/Microsoft/msbuild/blob/dc1f683b20dc17266c4e4b47b4b03dd7a9acef81/src/Tasks/Microsoft.Common.CurrentVersion.targets#L324.

Were you actually seeing the issue with the project that you put here in the bug description or just another project that outputs a dll and not an exe?

I tried to use the new type with nunit/Specflow Test and it is not generated.

tomkludy commented 4 years ago

I had this problem and will add my own experience. First off, I ended up turning on <GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType for all projects, both libraries and executables. I don't know why this is not the default as this fixed dozens of projects by itself!

I still had a project that generated this mysterious error, even though it was definitely generating the *.dll.config file with all of the correct bindingRedirects. In my case, the problem was caused by a PackageReference that transitively pulls in Microsoft.Bcl.Build. This invisibly changed properties and added targets to the build. Once I turned up the build log verbosity to "Detailed" I found this:

1>Property reassignment: $(AutoUnifyAssemblyReferences)="false" (previous value: "true") at C:\Users\tomk\.nuget\packages\microsoft.bcl.build\1.0.21\build\Microsoft.Bcl.Build.targets (22,5)

Then later, while building target ResolveAssemblyReferences I confirmed:

1>    AutoUnify:
1>        False

.. right before the string of errors.

I have no idea what this property is for, because the bindingRedirects were still created, so whether it liked it or not, the assembly references were unified anyway. I was able to fix the error by adding to my project file:

  <Target Name="ForceAutoUnify"
          BeforeTargets="ResolveAssemblyReferences"
          Condition="'$(AutoGenerateBindingRedirects)' == 'true'">
    <PropertyGroup>
      <AutoUnifyAssemblyReferences>true</AutoUnifyAssemblyReferences>
    </PropertyGroup>
  </Target>

Hopefully this will save time for others, as I was struggling with this for quite a while.

dsplaisted commented 4 years ago

@tomkludy The Microsoft.Bcl.Build package causes problems. We created it way back when to help with things like generating binding redirects, but now that we've mostly fixed them in the product it interferes with things. If you can upgrade to a newer package that doesn't include it as a dependency that should help. If you can't do that, then add the following to your project:

<ItemGroup>
    <PackageReference Include="Microsoft.Bcl.Build" Version="1.0.21" ExcludeAssets="All" />
</ItemGroup>

That should prevent anything from the package from being used.

Also, I highly recommend checking out MSBuild binary logs and the viewer available at https://msbuildlog.com/ instead of trying to dig through verbose text log files.

TheXenocide commented 5 months ago

So I had a very similar symptom and went googling around for an answer when I came across this thread. While using command line builds to try to collect binlogs I noticed the problem went away after I deleted my obj folder and though that might have been a temporary issue and a good enough fix, but when I got back to Visual Studio and did a Clean (to ensure a fresh package restore on my next build) and eventually did a new build, my binding redirects went away in the output App.config again.

Eventually, after using the Project System Tools extension to dig through VS-specific build behaviors what I found was a different cause for the issue than what's reported here, but wanted to add my findings in case they wind up related or someone else bings into the same results.

In my case, what I noticed was that the GenerateBindingRedirects step was being skipped because the output file was newer than the input files. It turns out the App.config file was being transformed due to an App.Debug.config file existing and that this transformation was happening after the suggested redirects cache was being generated. As a result, the obj\Debug\<App>.exe.config was newer than all of the input files and the GenerateBindingRedirects target was skipped. As a workaround I've applied the following target to the impacted project:

  <Target Name="ForceReorderAppConfigTransforms" 
          DependsOnTargets="AfterCompile_TransformAppConfigFile" 
          BeforeTargets="_GenerateSuggestedBindingRedirectsCache" 
  />

I have a similar issue to look into for another project/solution, but unlike this one, that one is not generating binding redirects even from our CI builds (using the MSBuild command line). I'll report back what I find there.

TheXenocide commented 5 months ago

Just checking back in to mention that the other project that was having binding redirect issues just turned out to be a legacy ASP.NET Web API, which doesn't support compile-time auto-generation of binding redirects, so that is a non-issue as it pertains to this scope.

qlikTERror commented 1 month ago

I have a problem related to this too.

We have a Project A (exe type) that has a dependency on Project B (dll type). Project B has a assembly reference (Item type Reference) to an assembly C that depends on BouncyCastle 1.8.4.

We tried to force BouncyCastle to version 1.8.9, so we added a PackageReference to Project B to this version.

Unfortunately Project B never uses BouncyCastle, so it is not a direct dependency for it, but just a transitive one, brought inside from the reference to the assembly C.

When building Project A, instead of generating the correct binding redirect, the task ResolveAssemblyReference correctly notices that assembly C depends on BouncyCastle.Crypto 1.8.4, correctly finds there is in the output folder of Project B BouncyCastle.Crypto 1.8.9 but instead of choosing it, it reports this:

Considered "[..]BouncyCastle.Crypto.dll",
            but its name "BouncyCastle.Crypto, Version=1.8.9.0, Culture=neutral, PublicKeyToken=0e99375e54769942"
            didn't match the expected name "BouncyCastle.Crypto, Version=1.8.4.0, Culture=neutral, PublicKeyToken=0e99375e54769942".

Obviously both projects has AutoGenerateBindingRedirects at true and AutoUnifyAssemblyReferences at true.

The only way that I found to make the binding redirect generation works correctly is to create a reference to a type of the assembly BouncyCastle.Crypto 1.8.9 from our Project B. The easiest way is to do this with TypeForwardedToAttribute, or create an attribute that accept a Type as parameter.