src/_Internals/AssemblyLoader.ps1

# SDK assemblies of the running edition: Assemblies\Core (PowerShell 7.4+) or Assemblies\Desktop (Windows PowerShell 5.1).
# Dot-sourced once by PowerDataOps.psm1, before the function files.
# Paths are combined, not written with "\": .NET does not read it as a separator on Linux and macOS.
$Global:PowerDataOpsAssemblyPath = [System.IO.Path]::GetFullPath([System.IO.Path]::Combine($PSScriptRoot, "..", "Assemblies", $PSVersionTable.PSEdition));

# Core is a portable build: a DLL that has a platform build also has it in runtimes\win or runtimes\unix, which replaces the one of the folder
# (the runtime would choose it from a .deps.json, which a module cannot use).
$assemblyFiles = [ordered]@{};
$assemblyPlatform = if ([System.Environment]::OSVersion.Platform -eq [System.PlatformID]::Win32NT) { "win" } else { "unix" };
foreach ($assemblyFolder in $Global:PowerDataOpsAssemblyPath, [System.IO.Path]::Combine($Global:PowerDataOpsAssemblyPath, "runtimes", $assemblyPlatform)) {
    if (Test-Path -LiteralPath $assemblyFolder) {
        foreach ($assemblyFile in @(Get-ChildItem -LiteralPath $assemblyFolder -File | Where-Object { $_.Extension -eq ".dll" })) {
            $assemblyFiles[$assemblyFile.Name] = $assemblyFile;
        }
    }
}

# Every DLL is loaded up front: PowerShell only finds a type literal ([Microsoft.Xrm.Sdk.Entity]) in an assembly already loaded.
$loadedAssemblies = [System.Collections.Generic.List[System.Reflection.Assembly]]::new();
foreach ($assemblyFile in $assemblyFiles.Values) {
    try {
        $loadedAssemblies.Add([System.Reflection.Assembly]::LoadFile($assemblyFile.FullName));
    }
    catch {
        Write-Warning "PowerDataOps: cannot load '$($assemblyFile.FullName)'. Reason = '$($_.Exception.Message)'";
    }
}

# The DLLs reference other versions of each other (e.g. Microsoft.Extensions.* 3.1.0 for 3.1.9 shipped), which a module cannot redirect
# with a configuration file: the resolver answers with the shipped one, whatever the version asked.
# It only answers for the DLLs of this folder: any other request is left to the runtime, so it never resolves for another module.
$assemblyResolverSource = @'
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Reflection;

public class PowerDataOpsAssemblyResolver
{
    private static readonly Dictionary<string, PowerDataOpsAssemblyResolver> Resolvers = new Dictionary<string, PowerDataOpsAssemblyResolver>(StringComparer.OrdinalIgnoreCase);
    private readonly ConcurrentDictionary<string, Assembly> assemblies = new ConcurrentDictionary<string, Assembly>(StringComparer.OrdinalIgnoreCase);
    private readonly string folder;

    private PowerDataOpsAssemblyResolver(string folder)
    {
        this.folder = folder;
    }

    // One handler per folder and process: a module imported again (-Force) only refreshes the loaded assemblies
    public static void Register(string folder, Assembly[] loadedAssemblies)
    {
        PowerDataOpsAssemblyResolver resolver;
        lock (Resolvers)
        {
            if (!Resolvers.TryGetValue(folder, out resolver))
            {
                resolver = new PowerDataOpsAssemblyResolver(folder);
                Resolvers[folder] = resolver;
                AppDomain.CurrentDomain.AssemblyResolve += resolver.Resolve;
            }
        }
        foreach (Assembly assembly in loadedAssemblies)
        {
            resolver.assemblies[assembly.GetName().Name] = assembly;
        }
    }

    public Assembly Resolve(object sender, ResolveEventArgs args)
    {
        string name;
        try
        {
            name = new AssemblyName(args.Name).Name;
        }
        catch (Exception)
        {
            return null;
        }

        // The copy this folder loaded at module load, even when another module loaded its own first
        Assembly assembly;
        if (assemblies.TryGetValue(name, out assembly))
        {
            return assembly;
        }

        string path = Path.Combine(folder, name + ".dll");
        if (!File.Exists(path))
        {
            return null;
        }

        // Not loaded at module load (a warning said why): a copy already loaded, else this folder's.
        // LoadFile, as at module load: LoadFrom would bind its dependencies in another context, and could load a second Microsoft.Xrm.Sdk.
        foreach (Assembly loaded in AppDomain.CurrentDomain.GetAssemblies())
        {
            if (!loaded.IsDynamic && string.Equals(loaded.GetName().Name, name, StringComparison.OrdinalIgnoreCase))
            {
                return loaded;
            }
        }
        return assemblies.GetOrAdd(name, Assembly.LoadFile(path));
    }
}
'@
;

if (-not ("PowerDataOpsAssemblyResolver" -as [type])) {
    Add-Type -TypeDefinition $assemblyResolverSource;
}
[PowerDataOpsAssemblyResolver]::Register($Global:PowerDataOpsAssemblyPath, $loadedAssemblies.ToArray());