61

Isn't this information necessary only in the executable's project?

How to disable this file creation?

NuGet 2.8

EDIT

Library projects were exceptions in NuGet 2.7, behavior changed in 2.8 by fixing this issue: http://nuget.codeplex.com/workitem/3827 with commit: https://github.com/NuGet/NuGet2/commit/448652d028e3f01ba4022e147baaf4e1fb3f969b

Tyler Lee
  • 2,736
  • 13
  • 24
lmagyar
  • 962
  • 1
  • 7
  • 12
  • What makes you think that assembly binding redirects are only necessary for executable projects? – khellang Mar 06 '14 at 14:52
  • duplicate of http://stackoverflow.com/questions/21795507/is-the-bindingredirect-config-file-needed-or-all-assemblies-in-an-application, but that wasn't answrered yet at the time this one was asked – stijn Aug 15 '14 at 07:01
  • See also https://github.com/JamesNK/Newtonsoft.Json/issues/636 – Erwin Mayer Sep 13 '17 at 11:13
  • 1
    ^^ Because assembly binding redirects are a runtime operation, so only a running program would need to configure them? Library projects don't run on their own. – Triynko Jan 16 '20 at 05:06
  • There is also a ticket on github https://github.com/NuGet/Home/issues/9706 – Chuck Lu Nov 12 '20 at 06:05

3 Answers3

23

The reason that the NuGet package manager adds assembly binding redirects to library projects is because there are types of projects where the output type is a library, but there are special mechanisms in place to assure that the library's app or web config file will be applied at runtime. This is opposed to more typical library usage that you're probably familiar with, wherein the library's config file is not used at all.

For example, Azure Web and Worker role projects in Azure SDK 1.8+ will produce libraries, but when they are wrapped in an exe by IIS, the library's config file will be set as the default for that exe. That way you get all your application configuration without having to explicitly publish a special config file with the same name as the wrapping executable, which is how it used to be done. Now the build process outputs the renamed config file (e.g. app.config -> myWebRoleLibrary.dll.config), and everything works as it should.

XUnit also does something similar; loading the app.config of the test assembly instead of the app config of the test runner process.

It's worth mentioning that you can also manually load a config file in any project, library or not. You would have to ensure that the config file ended up in the right place, but it is possible. This is less applicable for binding redirects though, since usually those are solely used by the assembly loader in the CLR. I suppose you could hook AssemblyLoad but now we're well on our way to poorly reinventing the wheel.

So, the answer to "is it necessary in my library project?" is maybe. If your library project is not a web or worker role or test project, and it doesn't manually load a configuration file, then the app.config is probably benign but unnecessary.

As for disabling it, you can only do that at the Visual Studio level. You can find the option in VS2019 at: Tools -> Options... -> NuGet Package Manager -> General -> Skip applying binding redirects.

What uses my configuration files?

The Compiler (csc.exe)

The compiler uses the config file of the assembly being built for one reason: to find and use supportPortability elements.

The link above lays out rare scenario where you would need that compiler option, but an overwhelming majority of users will not. Suffice it to say, if you don't know if you're using that feature, you are not.

It does not parse any of the other elements of the config file, including assembly binding redirects which are the elements that NuGet adds.

The Build Engine (msbuild)

MSBuild uses app configs in a few of its steps but importantly not to find the primary dependencies, which it will pass to csc.exe in the form of /reference: options.

In order to find the primary references MSBuild (and specifically the ResolveAssemblyReference task) will search a collection of paths enumerated in the common targets file. If it cannot find an explicit dependency in your csproj, it will issue a warning and likely an error further on if the dependency is required for compilation.

After that it will search for transitive dependencies. It does not pass these files to the compiler, but instead uses them to generate a list of files that need to be considered in subsequent build steps for things like generating license files, trust information, and suggested binding redirects. This step does consider the project's config file, and specifically uses its binding redirects to inform the list of transitive dependencies. It's important to note that building an exe will not consider its library's app.config files during the build, and a library's app.config will not change the way that library’s dll is produced.

The Runtime (CLR)

The CLR uses the config files to change the way it locates assemblies.

IIS

IIS will read elements of your web.config file to change the way your web app behaves. For example, caching characteristics.

Applications

Applications may manually retrieve configuration data from config files using the ConfigurationManager.

gitbox
  • 803
  • 7
  • 14
  • So at runtime does CLR consider assembly.dll.config files when it's a regular exe or an asp.net app? – axk Mar 03 '21 at 20:13
  • 1
    It does not consider the config file of a dll you *linked to* in either of those cases. It will consider the config file of the regular exe and the web.config file of the asp.net dll though. – gitbox Mar 04 '21 at 21:34
9

Assembly binding redirects are as valid in a class library as they are in executable projects.

Think about this; when building your application, how will the compiler know which version of referenced assemblies to use (for the class libraries)?

Often this will work just fine, without the redirects, but when you stumble over a machine that has a GAC'ed version of the assembly, you could get into trouble.

I suggest you read the assembly binding redirect documentation to better understand what it is and does.

NuGet adds the app.config with redirects to help you, and quite frankly, I don't get the fuzz about an extra app.config for everything to work as expected.

As of today, it will add redirects to all projects, except the following types:

  • WiX
  • JS
  • Nemerle
  • C++
  • Synergex
  • Visual Studio
  • Windows Store App

As far as I know, there's no way of turning this off. You could create an issue at Github if this is a problem.

The source code for adding assembly binding redirects can be found here.

Tyler Lee
  • 2,736
  • 13
  • 24
khellang
  • 17,550
  • 6
  • 64
  • 84
  • THX, I didn't know, that the compiler can use assembly binding redirect information from app.config! – lmagyar Mar 06 '14 at 16:27
  • 29
    The binding redirect documentation says `app.config` is used at runtime, not compile-time. So this doesn't explain why `app.config` would be needed for a library project. Eg, a web application that uses a library dll is not going to have any knowledge of that dll's app.config. – Justin M. Keyes Mar 08 '14 at 00:15
  • 10
    *how will the compiler know which version of referenced assemblies to use (for the class libraries)?* The `` element in the `.csproj` contains version information. – Justin M. Keyes Mar 08 '14 at 00:18
  • 6
    @JustinM.Keyes Which documentation did you read? The first paragraph states that "You can redirect compile-time binding references to .NET Framework assemblies", and further down it says that "If you manually add binding redirects to the source app.config file, at compile time, Visual Studio 2013 tries to unify the assemblies based on the binding redirects you added". – khellang Mar 10 '14 at 08:53
  • @khellang you're right, I must have read somewhere else on the page. – Justin M. Keyes Aug 06 '14 at 22:53
  • 5
    There needs to be (at least) a switch in the package manager console in the install/upgrade command to disable/prevent the bindings. They serve no real purpose other than as a bit of hand holding for the uninformed. – Tony Jan 22 '15 at 14:58
  • the issue comes when you add the nuget package into a dll, which ends up reading its internal app.config file instead of the entry one. – Kat Lim Ruiz Aug 05 '15 at 05:58
  • i tried in my class library project binding redirect to nonexistent version of referenced library and no compilation error, so it looks like compiler does not use app.config, i am little confused. Docs says about VS2013, i used VS2012. – sanjuro Mar 02 '16 at 09:56
  • 11
    @khellang *I don't get the fuzz about an extra app.config* - Imagine you have a big solution and you update a nuget package in one project. Now a lot of (seemingly unrelated) projects in that solution are suddenly getting an app.config. A lot of new files that you have to maintain. I tend to think a switch in the package manager could be a good idea. – bitbonk Mar 03 '16 at 08:48
  • @bitbonk What is there to maintain? You left out the last bit; *for everything to work as expected*. This is a small price to pay, IMO. There is a reason why it was added in the first place. Though I guess a switch could be warranted. Anyway, static binding redirects are hopefully a think of the past soon. – khellang Mar 03 '16 at 10:09
  • 1
    @khellang _Think about this; when building your application, how will the compiler know which version of referenced assemblies to use (for the class libraries)?_ Binding redirects found in .config files have nothing to do with this. They are unified at compile time but used only at runtime. You misunderstood the docs. – rciq Jan 12 '17 at 02:10
  • 12
    My testing indicates the app.config's in class libraries are of no consequence. What really matters is that the redirect appears in the config of the consuming application. Class library app.configs never actually get used as far as I've ever seen. – AaronLS Mar 27 '17 at 13:44
  • 8
    As others have said, I think this answer is incorrect and is misinterpreting the linked documentation. Although it says that there is automatic binding redirection at compile-time, it doesn't say that this uses class library app.configs to determine referenced versions of assemblies. As far as I can tell the only compile-time work done is to update the output app.config files of the projects in the solution. – Adam Goodwin Oct 03 '17 at 10:42
  • 1
    This blog post has some helpful information: https://www.samnoble.co.uk/2017/04/12/can-net-class-libraries-utilise-app-config-files/ – Adam Goodwin Oct 03 '17 at 10:47
  • @AdamGoodwin Want proof that MSBuild uses app.config at compile-time? Check the source; https://github.com/Microsoft/msbuild/blob/5df95761c73cd6b1b2c35a827ed168e32546388e/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs#L2088-L2100 – khellang Oct 03 '17 at 11:38
  • I don't have time to look through that in detail now, but you could be right if that is referring to the app.config file relating to the assembly reference and not just the one for the project doing the referencing. – Adam Goodwin Oct 03 '17 at 12:53
  • @khellang, as Adam mentioned app.configs in libraries are only used for assembly binding redirect unification for the libraries themselves. If you look through the code you linked you'll see the ResolveAssemblyReference task also outputs a collection of suggested redirects, and that the appconfigs are not used to resolve primary assembly references. Since unification only affects the output.dll.config, and that file will probably not be considered when an exe depending on the library is run, they most often have no affect. – gitbox Aug 04 '19 at 23:49
-1

I created a little console app that checks all the date of the app.config files and then auto deletes them from your .csproj and the file. Todo: delete from tfs. Perhaps this could help.

class Program
{
    private static string RootFolder;
    private static string AppConfigName;
    private static bool AskConfirmation = true;
    static void Main(string[] args)
    {
        try
        {
            AppConfigName = "app.config";
            RootFolder = @"<Your project path>";
            ScanDir(RootFolder);
            Console.WriteLine();
            Console.WriteLine("DONE!");
            Console.WriteLine("Press ENTER to finish...");
            Console.ReadLine();

        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

    private static void ScanDir(string directoryParent)
    {
        var dirs = Directory.GetDirectories(directoryParent);
        foreach (var dir in dirs)
        {
            var dirInfo = new DirectoryInfo(dir);
            if (dirInfo.Name == "bin" || dirInfo.Name == "obj" || dirInfo.Name == "debug" || dirInfo.Name == "tempPE" || dirInfo.Name == "java" || dirInfo.Name == "res") continue;
            var files = Directory.GetFiles(dir, "app.config");
            if (files.Length == 0)
            {
                ScanDir(dir);
                continue;
            }
            Process(dir);
            //ScanDir(dir);
        }
    }

    private static void Process(string dir)
    {
        var fi = new FileInfo(Path.Combine(dir, AppConfigName));
        if (fi.CreationTime.Date != DateTime.Now.Date) return;
        if (AskConfirmation)
        {
            Console.WriteLine("Scan " + dir.Replace(RootFolder, ""));
            Console.Write("Remove (y)es or (n)o ?");
            var key = Console.ReadKey();
            Console.WriteLine();
            if (key.Key.ToString() =="Y")
                // remove app.config
                RemoveAppConfig(dir, fi);
        }
        else
            RemoveAppConfig(dir, fi);
    }

    private static void RemoveAppConfig(string dir, FileInfo fi)
    {
        var csProjs = Directory.GetFiles(dir, "*.csproj");
        foreach (var csProj in csProjs)
        {
            var txt = File.ReadAllText(csProj);
            txt = Regex.Replace(txt,"<None Include=\"App.Config\" />", "",RegexOptions.IgnoreCase);
            File.Delete(csProj);
            File.WriteAllText(csProj, txt);
        }
        File.Delete(fi.FullName);
        // todo: undo in tfs
        Console.WriteLine("Deleted");
    }
}
Emmanuel
  • 7,574
  • 3
  • 24
  • 22
  • khellang:"NuGet adds the app.config with redirects to help you, and quite frankly, I don't get the fuzz about an extra app.config for everything to work as expected." You shouldn't write code to make your app more error prone. – Michael Freidgeim Jun 19 '17 at 06:09
  • I would have made a powershell module that would inspect the proejct type guid and the contents of the file. If library and the only contents are assembly binding redirects, then remove the file and undo any pending add for version control. – StingyJack Jan 19 '18 at 14:02
  • I think this answer is perfectly valid (Not sure if the code builds though). It might be better practice to keep the app.config, but if you want to get rid of them, this answer gives a hint of how to do it. – Sascha Berlin Sep 28 '22 at 18:29