This feature provides support for component dependency resolution. The planned usage is going to be implementing a managed public API which will provide a building block for custom AssemblyLoadContext implementation or for any other solution which needs to be able to resolve dependencies for a given component. In .NET Core the SDK produces .deps.json
to describe component dependencies. The host already uses this during app startup to determine the list of static dependencies for the app itself. This new capability is to enable to use the same code/logic for resolving dependencies of dynamically accessed/loaded components.
The host components (mostly hostpolicy
) already contain code which does .deps.json
parsing and resolution for the app start, so to avoid code duplication and maintain consistent behavior the same code is used for component dependency resolution as well.
As of now the dependency resolution does NOT process frameworks. It only resolves dependencies directly described in the .deps.json
of the component being processed.
For typical plugin scenarios resolving framework assemblies is not desirable, as the plugin will very likely want to share the framework with the app, and thus the ALC based on this component dependency resolution should fall back to the app for framework assemblies. Since there's no good way to detect which assembly is framework, it's better to not return them from the resolution for this scenario.
In the future it's possible expand the functionality to also support full framework resolution, but it should be an explicit choice (opt-in). The scenario for this would be using this feature to resolve dependencies for the MetadataLoadContext
for inspection not loading. In which case it might make sense to load a full "app" using this functionality (including separate frameworks and so on).
Consequence of this is that we're not processing .runtimeconfig.json
at all. For most cases this doesn't matter as typical classlib
projects won't have .runtimeconfig.json
generated by the SDK. Whenever the full framework resolution functionality is implemented, the feature would have to start processing .runtimeconfig.json
as well.
This feature certainly provides a somewhat duplicate functionality to the existing DependencyModel. With several core differences though:
- DependencyModel is an OM-like API fully describing the
.deps.json
format. It's not simple to use it for dependency resolution (although it's possible). - Using DependencyModel in a similar fashion would require additional code handling things like servicing, store and RID fallbacks.
- DependencyModel exposes additional information like compile-only assets which don't play any role in dependency resolution.
- DependencyModel comes with its own dependencies (namely Newtonsoft.Json) and thus is harder to use in some scenarios (or might event prevent its usage in certain cases).
- It would not be practical to layer DependencyModel on top of this feature since it needs to expose the full picture of
.deps.json
. The dependency resolution only handles runtime assets and only limits its view to the current RID.
- The component dependency resolution uses the same servicing and shared store paths as the app. So it's up to the app to set these up either via config or environment variables.
- There are several settings which are inherited from the app for the purpose of dependency resolution of the components. Note that for simple components most of these are not actually used for anything
- probe_paths - reused
- tfm - reused
- host_mode - reused
- RID fallback graph from the root framework - reused
- Just like settings, there's a set of environment variables which are used in the same way as the app would use them
DOTNET_ROLL_FORWARD_ON_NO_CANDIDATE_FX
- used just like in the appProgramFiles
andProgramFiles(x86")
- used to find servicing root and the shared storeDOTNET_SHARED_STORE
- used to find the shared store - just like the appDOTNET_MULTILEVEL_LOOKUP
- used to enable multi-level lookup - used just like in the app
- Right now this feature doesn't process
.runtimeconfig.json
or.runtimeconfig.dev.json
. Most dynamically loaded components don't have these anyway, since SDK doesn't generate these for theclasslib
project type. The only meaningful piece of info from these which could be used is the set of probing paths to use. Currently the same set ofprobing paths as for the app is used. With the changes in .NET Core 3 wheredotnet build
will copy all static dependencies locally, the importance of additional probing paths should be very low anyway.
- Which probing paths (as per host-probing) should be used for the component. Currently it's the same set as for the app. Which means that scenarios like self-contained app consuming
dotnet build
produced component won't work since self-contained apps typically have no probing paths (specifically they don't setup NuGet paths in any way). - Review the list of settings which are reused from the app (see above)
- Review the list of environment variables - if we should use the same as the app or not
- Currently we don't consider frameworks for the app when computing probing paths for resolving assets from the component's
.deps.json
. This is a different behavior from the app startup where these are considered. Is it important - needed? - Add ability to corelate tracing with the runtime - probably some kind of activity ID
- Handling of native assets - currently returning just probing paths. Would be cleaner to return full resolved paths. But we would have to keep some probing paths. In the case of missing
.deps.json
the native library should be looked for in the component directory - thus requires probing - we can't figure out which of the files in the folder are native libraries in the hosts. - Handling of satellite assemblies (resource assets) - currently returning just probing paths which exclude the culture. So from a resolved asset
./foo/en-us/resource.dll
we only take./foo
as the probing path. Consider using full paths instead - probably would require more parsing as we would have to be able to figure out the culture ID somewhere to build the true map AssemblyName->path in the managed class. Just like for native assets, if there's no.deps.json
the only possible solution is to use probing, so the probing semantics would have to be supported anyway.