Skip to content

Commit

Permalink
📖 Document loading .NET assemblies
Browse files Browse the repository at this point in the history
  • Loading branch information
pleonex committed Nov 30, 2023
1 parent d2f49c5 commit 2058397
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 13 deletions.
2 changes: 1 addition & 1 deletion docs/articles/core/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,5 @@
href: ../plugins/overview.md
- name: Loading assemblies
href: ../plugins/load-assembly.md
- name: Locating converter types
- name: Find converters
href: ../plugins/locate-types.md
89 changes: 87 additions & 2 deletions docs/articles/plugins/load-assembly.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,89 @@
# Loading .NET assemblies

The .NET libraries for _reflection_ provide already APIs to load additional .NET
assemblies.
.NET provide already APIs to load additional assemblies via
[`AssemblyLoadContext`](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.loader.assemblyloadcontext).
Yarhl provides extensions methods for `AssemblyLoadContext` to facilitate
loading files from disk.

You can use the main `AssemblyLoadContext` from `AssemblyLoadContext.Default` to
load them. For advanced use cases, it's possible to create a new
`AssemblyLoadContext` that would provide isolation.

> [!TIP]
> If you plan to use [`ConverterLocator`](./locate-types.md#converterlocator),
> remember to call `ScanAssemblies` after loading new assemblies.
<!-- ignore markdown warning -->

> [!WARNING]
> Loading a .NET assembly may load also its required dependencies. You may run
> into dependency issues if they use different versions of a base library such
> as Yarhl or Newtonsoft.Json.
<!-- ignore markdown warning -->

> [!IMPORTANT]
> There may a security risk by loading **untrusted** assemblies from a file or a
> directory. .NET does provide any security feature to validate it's not
> malicious code.
## Load from file paths

The method
[`TryLoadFromAssemblyPath`](<xref:Yarhl.Plugins.AssemblyLoadContextExtensions.TryLoadFromAssemblyPath(System.Runtime.Loader.AssemblyLoadContext,System.String)>)
will try to load the .NET assembly in the given path. If this assembly fails to
load (e.g. it's not a .NET binary) it will return `null`.

Similar, the method
[`TryLoadFromAssembliesPath`](<xref:Yarhl.Plugins.AssemblyLoadContextExtensions.TryLoadFromAssembliesPath(System.Runtime.Loader.AssemblyLoadContext,System.Collections.Generic.IEnumerable{System.String})>)
will try to load every assembly in the list of paths given. If any of them fails
to load, no exception will be raised and it would be skipped.

Additionally, this API will skip any file where its name starts with any of the
following prefixes. The goal is to prevent loading unwanted dependencies. If you
want to force loading them, use `TryLoadFromAssemblyPath`.

- `System.`
- `Microsoft.`
- `netstandard`
- `nuget`
- `nunit`
- `testhost`

## Load from a directory

The method
[`TryLoadFromDirectory`](<xref:Yarhl.Plugins.AssemblyLoadContextExtensions.TryLoadFromDirectory(System.Runtime.Loader.AssemblyLoadContext,System.String,System.Boolean)>)
will try to load every file in the given directory with an extension `.dll` or
`.exe`. If any of them fails, no error will be reported and it would be skipped.

Via an argument it's possible to configure if it should load files from the
given directory or from its subdirectories recursively as well.

## Load from executing directory

A common use case it's to load every assembly from the executable directory.
Because .NET will load an assembly lazily, only when type actually need it, upon
startup not every assembly from the executable directory could be loaded.

The method
[`TryLoadFromBaseLoadDirectory`](<xref:Yarhl.Plugins.AssemblyLoadContextExtensions.TryLoadFromBaseLoadDirectory(System.Runtime.Loader.AssemblyLoadContext)>)
addresses this use case by loading every `.dll` and `.exe` from the current
`AppDomain.CurrentDomain.BaseDirectory`.

> [!TIP]
> To use _plugins_ in a _controlled way_, the application may add a set of
> `PackageReference`s. After running `dotnet publish` these dependencies will be
> copied to the output directory. At startup call
> `AssemblyLoadContext.Default.TryLoadFromBaseLoadDirectory` to load all of
> them. Otherwise, unless the application also references their types, the
> assemblies will not be loaded.
<!-- ignore warning -->

> [!NOTE]
> It does not use `Environment.ProcessPath` because sometimes the application
> (or tests) may run by passing the main library file to the `dotnet` host
> application (e.g. `dotnet MyApp.dll`). In that case it would scan the
> installation path of the .NET SDK instead of the application installation
> directory.
6 changes: 6 additions & 0 deletions docs/articles/plugins/locate-types.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# Locate types

## TypeLocator

The `TypeLocator` is a _singleton_ class that p

## ConverterLocator

TODO
15 changes: 6 additions & 9 deletions docs/articles/plugins/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,14 @@ The _plugins_ are regular .NET libraries or executable that contains
implementations of _converters_ and _formats_. They don't need to implement any
additional interface or fullfil other requirements.

> [!WARNING]
> Loading a .NET assembly will load also its dependencies. You may run into
> dependency issues if they use different versions of a base library such as
> Yarhl or Newtonsoft.Json.
The main APIs are:

- `AssemblyLoadContextExtensions`: extension methods for `AssemblyLoadContext`
to load .NET assemblies from disk.
- `TypeLocator`: find types that implement a specific interface.
- `ConverterLocator`: find _converter_ and _format_ types.
- [`AssemblyLoadContextExtensions`](./load-assembly.md): extension methods for
`AssemblyLoadContext` to load .NET assemblies from disk.
- [`TypeLocator`](./locate-types.md#typelocator): find types that implement a
specific interface.
- [`ConverterLocator`](./locate-types.md#converterlocator): find _converter_ and
_format_ types.

```mermaid
flowchart TB
Expand Down
3 changes: 2 additions & 1 deletion docs/docfx.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
{
"files": [
"Yarhl/*.csproj",
"Yarhl.Media.Text/*.csproj"
"Yarhl.Media.Text/*.csproj",
"Yarhl.Plugins/*.csproj",
],
"src": "../src"
}
Expand Down

0 comments on commit 2058397

Please sign in to comment.