2

Lately, I was working on a tool that verifies if a given binary could load the needed dependencies at runtime, for .NET Framework binaries the resolution of dependencies is pretty straightforward as mentioned here. For .NET Core i am still confused about the way CoreCLR locates and loads assemblies. As i know at the moment is that CoreCLR search in three different places :

  • Application base directory
  • Shared directory
  • Nuget cache

Resolution process relies on the presence of three Json files:

  • *.runtimeconfig.json file which specifies which version of .NET Core the application depends

{
  "runtimeOptions": {
    "tfm": "netcoreapp3.1",
    "framework": {
      "name": "Microsoft.NETCore.App",
      "version": "3.1.0"
    }
  }
}
  • *.runtimeconfig.dev.json that contains additional probing paths

{
  "runtimeOptions": {
    "additionalProbingPaths": [
      "C:\\Users\\yjirari\\.dotnet\\store\\|arch|\\|tfm|",
      "C:\\Users\\yjirari\\.nuget\\packages"
    ]
  }
}
  • *.deps.json that lists dependencies of the application and their relative path to the Nuget cache

{
  "runtimeTarget": {
    "name": ".NETCoreApp,Version=v3.1",
    "signature": ""
  },
  "compilationOptions": {},
  "targets": {
    ".NETCoreApp,Version=v3.1": {
      "ConsoleApp1/1.0.0": {
        "dependencies": {
          "Newtonsoft.Json": "12.0.1"
        },
        "runtime": {
          "ConsoleApp1.dll": {}
        }
      },
      "Newtonsoft.Json/12.0.1": {
        "runtime": {
          "lib/netstandard2.0/Newtonsoft.Json.dll": {
            "assemblyVersion": "12.0.0.0",
            "fileVersion": "12.0.1.25517"
          }
        }
      }
    }
  },
  "libraries": {
    "ConsoleApp1/1.0.0": {
      "type": "project",
      "serviceable": false,
      "sha512": ""
    },
    "Newtonsoft.Json/12.0.1": {
      "type": "package",
      "serviceable": true,
      "sha512": "sha512-ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==",
      "path": "newtonsoft.json/12.0.1",
      "hashPath": "newtonsoft.json.12.0.1.nupkg.sha512"
    }
  }
}

The *.runtimeconfig.json purpose is pretty obvious which is allowing the runtime to know which version of .NET Core the application was built against.

For *.runtimeconfig.dev.json is to add Nuget cache and other directories as additional probing directories But for .deps i am still confused about its goal.

To clarify the role of .deps, i did a little experiment using a console project that depends on Newtonsoft.Json v 12.0.1.

There are two main cases:

  • When .deps is present in bin folder:

If Newtonsoft.Json with same or superior version (e.g. v12, v13) exists also in bin, the application executes normally. Else the Newtonsoft.Json gets loaded from Nuget cache.

  • When the .deps is absent:

If Newtonsoft.Json with same or superior version exists also in bin, the application executes normally. Else a FileLoadException is raised

Conclusion :

I concluded from the result of my experiment that :

  • if the dependency is present in the bin folder with the same version or superior the dependency gets loaded.
  • The role of .deps appears only if the dependency doesn't exist in the bin folder, that helps loading dependency from the Nuget cache using the path of Nuget cache in *.runtimeconfig.dev.json concatenated to relative path specified in .deps.

My Questions are the following:

  • Why CoreClr loads dependencies with superior version of that referenced by the project ?
  • Is there any role of *.deps file while resolving dependencies other than locating assmeblies in Nuget cache ?
  • Is the CoreCLR parses the .deps before searching for dependencies?

I saw this two documents on resolution of CoreCLR but i didn't get too much about the role of .deps.

https://github.com/dotnet/cli/blob/v2.0.0/Documentation/specs/corehost.md https://learn.microsoft.com/en-us/dotnet/core/dependency-loading/default-probing

0 Answers0