What is the purpose of *deps.json file in .NET Core?
What is the reason to store references in such file and not in assembly manifest(as in standalone .NET Framework)?
Using Ildasm i checked that assembly manifest doesn't contain entries for these dependecies after dotnet build command.
But it has entries after dotnet publish command.
The .deps.json file contains metadata about the assemblies referenced by the built assembly and the locations to search for them as well as information about the compilation options used.
This information is read by the native component (corehost) that loads and configures the runtime. When a referenced assembly needs to be loaded, the host will use the information in this file (as well as any runtimeconfig.json/runtimeconfig.dev.json) to locate the correct assembly to load.
This information is used in other places as well. For example ASP.NET Core's Razor view compilation also uses it to pass the correct references and configuration to the generated code. And unit test hosts also need to use the information in this file when a unit test library is loaded into the test host. The managed API to read and write this file is available in the Microsoft.Extensions.DependencyModel NuGet package.
Related
When I build a C# application project named MyApplication with .NET 6.0 on Windows, I get the following files:
MyApplication.dll
MyApplication.exe
MyApplication.deps.json
MyApplication.dll.config
MyApplication.pdb
MyApplication.runtimeconfig.json
MyApplication.xml
Which of these files do I need to distribute?
MyApplication.dll contains the IL code of the application. I certainly need this one.
MyApplication.exe is the stub loader that starts the application. I do need this.
MyApplication.pdb contains the debug info. I don't need to distribute this.
But what about the others? They seem to contain some dependency information. But is that only required for the compiler, or is it required later at runtime? (My application consists of dozens of dll's).
From the spec of runtime configuration files
MyApp.dll - The managed assembly for MyApp, including an ECMA-compliant entry point token.
MyApp.exe - A copy of the corehost.exe executable.
MyApp.runtimeconfig.json - An optional configuration file containing runtime configuration settings.
MyApp.deps.json - A list of dependencies, as well as compilation context data and compilation dependencies. Not technically required, but required to use the servicing or package cache/shared package install features.
So while the json files are not strictly required, I would probably recommend including them.
The MyApplication.xml is an optional documentation file that contains comments for your dll. This is probably not needed if you are distributing an application and not a library, and there should be an option to turn of the generation of this file in your project properties.
You should also check the documentation for Deploying your application. I would especially consider the parts about self contained and single file publishing.
I have an odd behavior in one of our .Net Core 3.1 projects, namely the application throws a FileNotFoundException for one of the 3rd party NuGet packages even though the file the exception referring to be missing is physically in the same directory.
Basically the Asp.net Core 3.1 Web Api .exe knows nothing about concrete implementations, only interfaces. (.Net Core 3.1 Class) Libaries then implement those interfaces and some of them pull in NuGet packages. It all comes together at runtime when the libraries register themselves to the .exe's IServiceCollection on startup. There's no direct project/nuget reference between the Web Api and these libraries, rather the Web Api .exe scans on startup the assemblies and calls an (interface declared) method to allow the libraries to register themselves to the host's DI container / service collection. This all works fine. However, whenever a method is now called in one of these libraries' classes that has a using statement to one NuGet package reference, the .exe throws said FileNotFoundException stating that, i.e. "System.IO.FileNotFoundException: 'Could not load file or assembly 'Hangfire.Core, Version=1.7.22.0, Culture=neutral, PublicKeyToken=null'. The system cannot find the file specified.'". That file (and all its dependencies) are in the .exe's base directory though.
Now, comes the confusing part: when adding a project reference to that class library (that references Hangfire) and doing nothing else, it works - no FileNotFoundException anymore. The assemblies in the base directory are the same BUT the only main difference is, that the .exe.deps.json file of the Web API project is with this direct project reference vastly bigger and also lists Hangfire.Core and its sub-dependencies. I can also take the .exe.deps.json file and place it in the build output of the non-project-referenced compilation state (which throws the FileNotFoundException) and then it works.
So the only difference is the .exe.deps.json file with and without these (in)direct references and I am wondering, is there any way to get out of this mess?
I'd really like to keep things loosely coupled in our application for a good reason and I would not want to pull in package references if I didn't have to.
Problem is, I don't even know where to begin to solve / work on that deps.json generation and I don't know if I even want to go there. In the old days build output was enough to run an application, having a .json file make or break an application is somewhat.. odd to me.
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
Could you please explain what is an Assembly in C# or .NET?
Where does it begin and where does it end?
What important information should I know about Assemblies?
An assembly is the compiled output of your code, typically a DLL, but your EXE is also an assembly. It's the smallest unit of deployment for any .NET project.
The assembly typically contains .NET code in MSIL (Microsoft Intermediate language) that will be compiled to native code ("JITted" - compiled by the Just-In-Time compiler) the first time it is executed on a given machine. That compiled code will also be stored in the assembly and reused on subsequent calls.
The assembly can also contain resources like icons, bitmaps, string tables and so on. Furthermore, the assembly also contains metadata in the assembly manifest - information like version number, strong name, culture, referenced assemblies and so forth.
In 99% of your cases, one assembly equals a physical file on disk - the case of a multi-file assembly (one assembly, distributed across more than a single file) appears to be a rather odd-ball edge case which I've never encountered so far in my 5+ years of .NET development.
In a multifile assembly there would still be only one assembly manifest in a DLL or EXE and the MSIL code in multiple netmodule files.
The answer is in order for immediate-grasping.
Put simply, it is the compiled project involving your classes and additional files, if there are. That is, each project in a
solution is assembly.
Or more techinally,
An assembly is where a type is stored in the flesystem. Assemblies are
a mechanism for deploying code. For example, the System.Data.dll
assembly contains types for managing data. To use types in other
assemblies, they must be referenced. - Source
How do we know it? If you glance at properties of a project under the solution you can see the following images.
When you compile the project, it turns out to DLL or EXE.
.NET assembly
In the Microsoft .NET framework, an
assembly is a partially compiled code
library for use in deployment,
versioning and security.
http://www.codeguru.com/columns/csharp_learning/article.php/c5845
An assembly is a file that is automatically generated by the compiler upon successful compilation of every .NET application. It can be either a Dynamic Link Library or an executable file. It is generated only once for an application and upon each subsequent compilation the assembly gets updated.
Here's another explanation of the make up of .NET Assemblies, a mini-quote:
The .NET framework consists of the
concepts of modules, assemblies, which
both store metadata and manifest
information. An assembly can contain
multiple modules. Visual C# only ever
creates one module which is turned
into an assembly by the C# compiler
(csc.exe), but an assembly can link
many .NET modules together via the
assembly linker (al.exe) command-line
tool. For example each of your source
code .cs files could be compiled into
a module and linked together to form
an assembly - an assembly is just a
collection of modules and resources.
One of these modules, however; must
contain manifest metadata (see below)
information for the assembly to be
understood by the CLR.
....
Having created a new .exe or .dll
inside VS.NET you see your file appear
inside your bin folder. Opening it in
notepad will give out gibberish, or
even inside a hexadecimal editor
without knowing the structure of the
file, you need a tool like ildasm.exe
or CFF explorer to make meaning from
it. The structure of the assembly is
as follows:
PE header
CLR header
CLR metadata
CLR
IL code
Native data
When a source code is compiled by the language compiler it Generate a Managed Assembly and MSIL(MisroSoft Intermediate Language). That Assembly contains .dll or .exe file. An Assebmly can be of two types Private Assembly and Shared Assembly, shared Assembly is stored in GAC(Global Assembly Cache) so that any application can refer to it while private assembly is stored in application folder which can be used by only one Application.
An assembly is a DLL or an EXE which will be created when you publish it or compile your application.
After writing source code of your program(project) then a file is created which may be DLL or EXE depends on your project. It makes only once for a single project. It has two types
1:- single
2:- shared or multiprogram
single assembly used only in a single program while shared can be used for multiprogram
An Assembly is a collection of logical units. Logical units refer to the types and resources which are required to build an application and deploy them using the .Net framework. Basically, Assembly is a collection of Exe and DLLs. It is portable and executable.
create a .net standard dll , name it as Paas dll, and change the output path
..\Output\
add a class logger like below-
public class Logger
{
public void LogMessage()
{
Console.Write("test");
}
}
Now create another .net core console project in solution and add dll as a refrence ( not project refrence) for pass.dll and make copy local to false.
Also change output directory of console app to ..\Output\
add below code in console app -
var logger = new Logger();
logger.LogMessage();
Console.Read();
Build and run .net core app, but now application goes in break mode with below error
Could not load file or assembly 'Paas, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=null'. The system cannot find the file
specified.
But if you perform same steps with .net framework 4.6.1 projects it works, why this different behavior with .net core exists and how to fix it?
github link for project showcasing this issue -
https://github.com/ankgupta067/DependencyInjection.git
The issue is setting copy local to false. On .NET Core, in order to know which assemblies should get loaded, it uses what is called a .deps.json file that gets generated next to your output .dll. In order to generate this .deps.json file, the .NET Core SDK inspects information about the assemblies you reference. One of the pieces of information it uses is whether the Reference is CopyLocal or not. See the code here. If you inspect your console app's .deps.json file, you'll see there is no entry for Paas.dll in it.
So in order to make this work, the .deps.json file will need to be written correctly. Using the current tools, that means you will have to stop setting copy local to false. If you want to open an issue for this scenario please log it here. That way it can be fixed in a future release.
OLD ANSWER:
A difference between SDK-based (".NET Core style") .csproj projects and traditional .csproj projects is that by default SDK-based projects will append the target framework to the <OutputPath> property.
So when you say
Also change output directory of console app to ..\Output\
What is really happening is that the output of the SDK-based netstandard library is going to ..\Output\netstandard2.0\Paas.dll and the output of the SDK-based console project is going to ..\Output\netcoreapp2.0\. These are 2 different directories, so the runtime can't find the library.
To stop this behavior, edit both .csproj files and add:
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
inside a <PropertyGroup> element.
This will cause both projects to output directly to the specified ..\Output\ folder.