tl;dr: My .NET Core 3.1 console application crashes with a FileNotFoundException because a (referenced?) assembly is present in version A, but required in version B. What to do?
I am trying to get a console application to run that is now built for .NET Core 3.1, but that used to be a .NET Framework 4.8 project before it was converted.
The console application crashes with a System.IO.FileNotFoundException, saying that the assembly Microsoft.Extensions.FileProviders.Physical in version 3.1.0.0 cannot befound. Now, I can confirm it's not there - in the directory where the .exe file of my console application resides, there is a file named Microsoft.Extensions.FileProviders.Physical.dll, but its assembly version is 3.1.6.0.
The console application and its dependencies are a part of a bigger project in said folder, with a total of over 1,200 DLLs.
In .NET Framework, I'd have used a binding redirect to use the present version 3.1.6.0 of the indicated assembly. In .NET Core, though, I understand these binding redirects are not a thing anymore. Thus, I'm not sure how to proceed, or how to even find out why the runtime thinks it needs to load Microsoft.Extensions.FileProviders.Physical.dll.
I may have found a partial solution that loads the version-mismatched assembly nonetheless (see observation (6) below), but then, I'm still getting a FileNotFoundException, this time for Microsoft.AspNetCore.Mvc.Abstractions.
Some observations and attempts to solve this:
(1) None of the > 1,200 .csproj files contains the string "Physical".
(2) More than 400 of the .deps.json files mention "Microsoft.Extensions.FileProviders.Physical.dll", all of them referring to version 3.1.0.0.
(3) All of the respective DLLs are loaded in an ASP.NET Core application where the version mismatch appears to cause no issues.
(4) The .deps.json file of my console application itself does not mention "Microsoft.Extensions.FileProviders.Physical.dll".
(5) Putting the right version of the file (3.1.0.0) into the directory where the .exe file resides and from where the .exe file is also executed does not change anything. The FileNotFoundException still occurs, still complaining about an absence of "Microsoft.Extensions.FileProviders.Physical.dll", version 3.1.0.0.
(6) Based upon the information on assembly resolution in .NET Core provided in a CodeProject article, I have attempted to force loading of the assemblies from the same directory myself (preliminary code, relying on the working directory):
AssemblyLoadContext.Default.Resolving += (context, name) =>
{
var dllPath = System.IO.Path.Combine(Environment.CurrentDirectory, name.Name + ".dll");
if (File.Exists(dllPath))
{
return AssemblyLoadContext.Default.LoadFromAssemblyPath(dllPath);
}
return null;
};
This appears to help to some extent! Now, the "Microsoft.Extensions.FileProviders.Physical.dll" assembly, and plenty (more than 250) of others, can be loaded. But this fails once "Microsoft.AspNetCore.Mvc.Abstractions", 3.1.0.0, needs to be loaded, which is not actually anywhere around the .exe file. Apparently, it must be loaded from somewhere else (?)
(7) While the above appears to provide a partial solution concerning the version mismatch, our entire source code contains no other occurrence of "AssemblyLoadContext". Therefore, the ASP.NET Core application apparently avoids the version mismatch issue using some other mechanism.
(8) Building my console application with build output set to Diagnostic1 confirms the suspected behaviour for the "Microsoft.Extensions.FileProviders.Physical.dll" file (shortened excerpt of the output):
Dependency "Microsoft.Extensions.FileProviders.Physical, Version=3.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60".
Could not resolve this reference. Could not locate the assembly "Microsoft.Extensions.FileProviders.Physical, Version=3.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60". Check to make sure the assembly exists on disk. If this reference is required by your code, you may get compilation errors.
For SearchPath "C:\(...)".
Considered "C:\(...)\Microsoft.Extensions.FileProviders.Physical.winmd", but it didn't exist.
Considered "C:\(...)\Microsoft.Extensions.FileProviders.Physical.dll",
but its name "Microsoft.Extensions.FileProviders.Physical, Version=3.1.6.0, Culture=neutral, PublicKeyToken=adb9793829ddae60"
didn't match the expected name "Microsoft.Extensions.FileProviders.Physical, Version=3.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60".
Considered "C:\(...)\Microsoft.Extensions.FileProviders.Physical.exe", but it didn't exist.
Required by "(A)".
Required by "(B)".
Required by "(C)".
In there, (A), (B), and (C) are assemblies of our own project. But as far as I can see, neither of their .csproj files mentions the text "Physical", so I do not understand why the DLL is allegedly being required by them.
(9) For the "Microsoft.AspNetCore.Mvc.Abstractions" assembly, diagnostic output says:
Dependency "Microsoft.AspNetCore.Mvc.Abstractions, Version=3.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60".
Could not resolve this reference. Could not locate the assembly "Microsoft.AspNetCore.Mvc.Abstractions, Version=3.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60". Check to make sure the assembly exists on disk. If this reference is required by your code, you may get compilation errors.
For SearchPath "C:\(...)".
Considered "C:\(...)\Microsoft.AspNetCore.Mvc.Abstractions.winmd", but it didn't exist.
Considered "C:\(...)\Microsoft.AspNetCore.Mvc.Abstractions.dll", but it didn't exist.
Considered "C:\(...)\Microsoft.AspNetCore.Mvc.Abstractions.exe", but it didn't exist.
Considered "C:\(...)\Microsoft.AspNetCore.Mvc.Abstractions.winmd", but it didn't exist.
Considered "C:\(...)\Microsoft.AspNetCore.Mvc.Abstractions.dll", but it didn't exist.
Considered "C:\(...)\Microsoft.AspNetCore.Mvc.Abstractions.exe", but it didn't exist.
Required by "(B)".
Once again, (B) is an assembly (same as the (B) in (8)) of our own, but looking into the .csproj file does not reveal a single occurrence of "Mvc.Abstractions".
I have found a couple of questions that appeared to provide solutions, but none of them worked for me:
Assembly binding redirect in .NET Core - just points to another question (listed below).
Adding a bindingRedirect to a .Net Standard library - the answer points out that binding redirects do not exist in .NET Core, but that the .deps.json file can be used to resolve assemblies. It then goes on to describe .NET Framework binding redirects, without mentioning anything else on what to do with .deps.json in .NET Core.
Common practice to load the dependency(different version of dll) in program - the question is about .NET Core, but the answer applies to .NET Framework. For .NET Core, it links to one of the other questions listed here.
How can I add an assembly binding redirect to a .net core unit test project? - the answers to this question seem to suggest using binding redirects in app.config files, even though these are apparently not supported anymore in .NET Core according to another comment on that question. In any case, the suggested solution of adding
<PropertyGroup>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
</PropertyGroup>
to the .csproj file (uh, which one? I tried the one of my console application; is that the right one?) has no effect to my .deps.json files or the exception I keep getting, as far as I can tell.
Error System.IO.FileLoadException: 'Could not load file or assembly 'log4net, Version=2.0.8.0 in .NET Core - in this case, the correct DLL was available in the right version, it was just not copied to the appropriate output folder.
.NET Core 3.1 - Could not load file or assembly System.Runtime, Version=4.2.2.0 - the solution in this case seemed to be to use another library/library version that would fit with the assembly reference. I do not think that is a viable way for me, as replacing the Microsoft.Extensions.FileProviders.Physical assembly might just cause any kinds of conflicts or issues in any of the > 400 of our assemblies that apparently somehow use the file, according to the .deps.json mentions.
Why is my .NET framework app looking for the wrong version of the .NET core/standard platform extension assembly, and how do I fix it? - it seems this question's OP just accidentally stepped into the .NET Core topic, while they were actually working in a .NET Framework context.
FileNotFoundException when referencing DLL in .NET Core Application - this issue was centered around deficiencies in earlier .NET Core versions, which do not apply to .NET Core 3.1 anymore.
FileNotFoundException with indirectly (.net to .net standard to NuGet) referenced DLL - this appears to have been another case of the correct DLL file being available, just not in the right location.
Can I control .NET Core assembly redirects programmatically? - once again, comments in this question point out that binding redirects are not a solution in .NET Core. Moreover, the answer appears to apply to compile time. As none of our .csproj files mentions the files with which I am observing a version mismatch, I suspect it is referenced from within one of the 3rd party libraries we are using and thus compile-time solutions may not be applicable.
How can I make the runtime load version of 3.1.6.0 of the indicated assembly rather than the requested version 3.1.0.0? Alternatively, how do I find out how the runtime does it when running the ASP.NET Core application?
1: in VS2019: Tools -> Options -> Projects and Solutions -> Build And Run -> MSBuild project build output verbosity -> Diagnostic
I'm working on a project where plugins are deployed in DLL's.
When deploying a DLL, it gets locked by the application. To update it I have to change the name of the DLL (since I can't overwrite the original), and update the app database to use the new version of the library.
I'd like to make this as automated as possible.
In AssemblyInfo, I've set the [assembly: AssemblyVersion("1.0.*")] to automatically increment.
I'm hoping that I can pull this version into the project properties Assembly Name, so that it automatically appends the version number to the generated DLL.
Is this possible at all with VS/C#?
Following Hans advice that the file renaming can't be done during the build, I've decided to rename the file after build.
In order to find the version number quickly for renaming, I've removed the AssemblyFileVersion line from the AssemblyInfo file. Which will set the file version to the version number. Automatically Update Assembly File Version
After building the DLL I can check the file properties, where the version number will be listed as File version under the details tab.
I'll copy the version number from here and append it to the DLL name.
I made a change to an ancillary DLL that my project uses, built it of course, renamed the legacy DLL to *.dll_old, and copy and pasted in the new version of the DLL to that same folder.
However, when I then ran the app that uses the DLL, it errored out with:
"An expected error has occurred...bla bla bla...or select Details for more information.
I did select Details, and saw:
TypeLoadException
File or assembly name <name of the DLL, which I just replaced>
Version=<bla>
Culture=neutral
PublicKeyToken=null, or one of its dependencies, was not found.
UPDATE
Based on the comments, I guess there's more to replacing a DLL than one might expect. I don't know if this is significant or not, I thought replacing a DLL would be like replacing an EXE, but maybe not: the DLL project's AssemblyInfo.cs says,
[assembly: AssemblyVersion("1.3.*")] // used by .NET framework only
[assembly: AssemblyFileVersion("1.3.0.308")] // File Version - increment here
[assembly: AssemblyInformationalVersion("6.3.0")] // Product version - set to current IEQ system
...and the Version in the err msg is "1.3.3889.27539"
Do I need to update one of these lines (I would guess the middle one, if so) to that value (1.3.3889.27539)? Or...???
UPDATE
So since the .DLL is not strongly named, I tried simply removing the reference (to the old .DLL) in the project that uses the DLL and then adding it back again (same file name, different version). I see, though, that updating the .DLL does not change the version numbers shown above - IOW, the AssemblyInfo.cs does not get updated when building. Should it? Do I need to manually update these vals?
It seems that types inside your original DLL were referenced by your EXE file. WHen you replaced it with your own version the references were messed. The EXE file contains metadata table with a list of types, methods, properties, etc that it references and exact version of the assemble expected. Providing something else even if everything was the same but the version number is simply not the same thing. That is why you are getting the exception.
UPDATE: Yes it is possible. However it involves creating a manifest file. For more information check this MSDN web site on Redirecting Assembly Versions. Also, keep in mind that only strongly signed assemblies can be redirected. Non-signed assemblies will be ignored.
I had a project whose framework is 3.5 and i converted it to 4 and when i faced a problem of mixed mode. i changed the useLegacyV2RuntimeActivationPolicy from this answer
Now when i install my application to program files and try to run it, it crashes but when i place the config file beside my exe it runs.
Can anyone tell my the reason of that crash?
but when it was 3.5 i didn't need to place the app.config file beside the exe
That was required in 3.5 as well. It is the only way that the CLR can find the .config file. I'd have to guess that you somehow got away with it before and it just didn't matter. But now it is critical to let the CLR find the .config file since you really need that attribute to allow the mixed-mode assembly to load.
An obvious way ahead is to rebuild the C++/CLI assembly and have it target v4 as well. So you won't need the .config file anymore. If it is not yours then ask the vendor or author of the assembly for an update.
I have created a dll that will be used by multiple applications, and have created an installer package that installs it to the program files, as well as adds it to the Global Assembly Cache.
The dll itself uses log4net, and requires a xml file for the logging definitions.
Therefore when the installer is run, the following files get copied to the install directory within program files:
The main dll that I developed
- The Log4Net.dll
- the Log4Net.xml file
I am now experiencing a problem. I have created a test console application for experimentation. I have added my dll as a reference, and set the 'local copy' flag to false.
When I compile the test console exe however, I noticed that it has copied the log4net.dll and log4net.xml files to the bin directory. And when running the test console, it appears that it will only work if the log4net.dll is in the same directory as the exe. This is dispite the fact that the test console application does not use log4net, only the dll that was added as a reference does.
Is there some way to have it so that the log4net.dll & xml files used will be the ones that were installed to the program files, rather than any application needed to copy over local copies? The applications that will be using my dll will not be using log4net, only the dll that they are referencing uses it.
Many thanks
Don't install into the Global Assembly Cache! Even if your library dll is used by multiple applications each should have it's own local copy. Otherwise you get into a whole world of pain for saving a few KB of disk space.
Always copy the required dlls locally. If you are really sure that the application won't need it you can simply delete the unnessesary dlls later or don't include them in the installer. But if your application will call ANY reference there it will crash at runtime. So best option is to leave them there (after all they WERE referenced for a reason).
No, it's not possible (at least not without much efford) to have .Net load dlls from arbitrary locations on the disk. And it should be this way (look up DLL-hell if you want to know why).
I suspect your problem is the configuration. You must use fully qualified names if you want it to work from the GAC. As per the documentation at http://logging.apache.org/log4net/release/faq.html:
"When loading an assembly from the GAC the fully qualified assembly name, including the version, culture and public key must be specified. This is in the standard syntax supported by System.Type.GetType. See the next FAQ on how to get the version and public key for an assembly."
I managed to resolve this by adding Log4net.dll to the GAC as well. It will now run without needing a local copy the dll.
It does however require a local copy of the XML file, to correctly log.