Particular/Workshop

.NET Core assembly loading might fail with FileNotFoundException

Closed this issue · 8 comments

When dynamically loading assemblies in .NET Core the full framework approach of using Assembly.Load might fail with FileNotFoundException even if the file is available and in the expected location.

Various reasons might lead to that error depending on the packaging configuration and/or if the assembly is already referenced.

The following snippet solves the issue by testing if the assembly is already referenced at compile time or run time (deployment package) and choosing the right approach to load the assembly.

static class AssemblyLoader
{
    public static Assembly Load(string assemblyFullPath)
    {
        var fileNameWithOutExtension = Path.GetFileNameWithoutExtension(assemblyFullPath);

        var inCompileLibraries= DependencyContext.Default.CompileLibraries.Any(l => l.Name.Equals(fileNameWithOutExtension, StringComparison.OrdinalIgnoreCase));
        var inRuntimeLibraries = DependencyContext.Default.RuntimeLibraries.Any(l => l.Name.Equals(fileNameWithOutExtension, StringComparison.OrdinalIgnoreCase));

        var assembly = (inCompileLibraries || inRuntimeLibraries)
            ? Assembly.Load(new AssemblyName(fileNameWithOutExtension))
            : AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyFullPath);

        return assembly;
    }
}

@mauroservienti is this a bug in both samples?

The bug might affect all the .NET Core demos, so both ASP.Net Core API Gateway and ASP.Net MVC.

The problem is the following: to keep the demo as simple as possible frontend projects directly reference ViewModel and UI Composition assemblies for the sole purpose of:

  • Having them in bin directory
  • Benefiting of the fact that having VS understanding dependencies via references automatically create the expected projects build order

At sample startup time we scan the bin looking for ViewModel and UI Composition assemblies, in order to scan types we need to load the assembly, in .NET Core assemblies can load in 2 ways 😕

Assemblies can be referenced as compile time assemblies or as runtime assemblies, and to complicate things the way probing works depends on the deployment type

  1. If the assembly is referenced via Assembly.Load
  2. If the assembly is not referenced via AssemblyLoadContext.Default.LoadFromAssemblyPath

As it stands now samples work using the first approach. The problem I see is that users want to play at home and one of the thing they try is to remove the reference and create a sample deployment via some scripting...and kaboom, the assembly is not referenced anymore and we fall now in the second way of loading assemblies. The above sample code utilizes the Microsoft.Extensions.DependencyModel to understand in which way assemblies should be loaded.
I'm still playing with the sample to understand if it covers all the scenarios, and so far it seems so.

To complicate things, Visual Studio 2017 post build events are broken when it comes to .NET Core projects. We could workaround it but I'd prefer to stay with references as of now.

References:

Removing myself from this since the fix is pending in #59, once that's merged this is done.

@mauroservienti to clarify, does this bug currently exist in the asp-net-mvc sample, but does not exist in the new asp-net-core sample in #59?

It doesn't exist in the asp-net-mvc and it's fixed in the asp-net-core via 3b5833d

OK, so given that the fix also exists in the branch for #59, and therefore the new demo doesn't have the bug, the bug doesn't actually "exist" at all, so can we withdraw this?

this will be automatically closed as we merge #59, is there any advantage in closing this before the PR is merged? Just wondering, it's not a problem at all to close this right now linking to the commit with the fix.

Discussed with @mauroservienti and agreed to close, since the bug no longer exists in #59.