You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET插件项目配置服务时AddAutoMapper方法找不到异常排查

场景

我的情况如下:

  • 根项目(.NET 6.0 - Microsoft.NET.Sdk.Web)
  • 插件项目(.NET 6.0 - Microsoft.NET.Sdk.Razor)

插件项目通过程序集加载的方式被加载到根项目中:

private void LoadModuleAssemblies(IMvcBuilder mvcBuilder)
{
    var modules = Modules.Select(module =>
    {
        if (!File.Exists(module.ModulePath))
        {
            throw new Exception($"The DLL of module {module.Name} could not be found at ({module.ModulePath})");
        }

        var loadContext = new ModuleLoadContext(module.ModulePath);
        var assembly = loadContext.LoadFromAssemblyName(new AssemblyName(Path.GetFileNameWithoutExtension(module.ModulePath)));
        mvcBuilder.AddApplicationPart(assembly);
        module.Assembly = assembly;

        return module;
    }).ToList();

    _hbsModulesAccessor.Modules = modules;
}

ModuleLoadContext用于在不同上下文加载依赖程序集,以避免版本冲突:

public class ModuleLoadContext : AssemblyLoadContext
{
    private readonly AssemblyDependencyResolver _resolver;

    public ModuleLoadContext(string pluginPath)
    {
        _resolver = new AssemblyDependencyResolver(pluginPath);
    }

    protected override Assembly? Load(AssemblyName assemblyName)
    {
        var existingAssembly = Default.Assemblies.FirstOrDefault(x => x.GetName().Name == assemblyName.Name && x.GetName().Version == assemblyName.Version);
        if (existingAssembly != null)
        {
            return existingAssembly;
        }

        string? assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
        if (assemblyPath != null)
        {
            return LoadFromAssemblyPath(assemblyPath);
        }

        return null;
    }

    protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
    {
        string? libraryPath = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName);
        if (libraryPath != null)
        {
            return LoadUnmanagedDllFromPath(libraryPath);
        }

        return IntPtr.Zero;
    }
}

插件加载完成后,我尝试为插件配置服务:

IEnumerable<Type> moduleStartupConfigurations = AppDomainTypeFinder.FindClassesOfType<IHBSStartup>();
IOrderedEnumerable<IStartup?> instances = moduleStartupConfigurations
    .Select(s => Activator.CreateInstance(s) as IStartup)
    .OrderBy(s => s?.Order);

foreach (IStartup? instance in instances)
{
    if (instance == null) throw new Exception("Could not load plugin");
    instance.ConfigureServices(services);
}

AppDomainTypeFinder用于查找实现了IStartup的类,创建实例后会调用插件中定义的ConfigureServices方法。

问题

调用ConfigureServices方法时抛出以下异常:

Method not found: 'Microsoft.Extensions.DependencyInjection.IServiceCollection Microsoft.Extensions.DependencyInjection.ServiceCollectionExtensions.AddAutoMapper(Microsoft.Extensions.DependencyInjection.IServiceCollection, System.Reflection.Assembly[])'

插件中实现IStartup的类代码如下:

public class Startup : IStartup
{
    public int Order { get; } = 10;

    public void Configure(IApplicationBuilder application)
    {
        // Add application parts
    }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
    }
}

AddAutoMapper通过IServiceCollection注册自身服务,这个问题是否是由独立的AssemblyLoadContext导致的?

我是按照官方插件系统指南创建的这套机制。


这个问题确实和独立的AssemblyLoadContext有关,核心原因是程序集加载上下文不匹配:

  1. 根项目中的AutoMapper相关程序集加载在默认上下文(Default)中,而插件程序集在自定义的ModuleLoadContext里。当插件调用services.AddAutoMapper(...)时,插件上下文无法找到正确的AddAutoMapper方法——要么是AutoMapper.Extensions.DependencyInjection程序集没被加载到插件上下文,要么是加载的版本和根项目不一致,导致方法签名不匹配。
  2. 另外,AppDomain.CurrentDomain.GetAssemblies()仅返回默认加载上下文的程序集,插件上下文加载的程序集不会被包含,这也会导致后续AutoMapper无法找到插件内的映射配置类,但当前直接触发的是方法找不到的异常,所以重点还是加载上下文的问题。

解决办法:

1. 让插件上下文优先共享根项目的依赖程序集

修改ModuleLoadContext的Load方法,对AutoMapper、依赖注入扩展这类共享组件,直接从默认上下文加载,避免插件上下文重复加载:

protected override Assembly? Load(AssemblyName assemblyName)
{
    // 指定需要共享的依赖前缀
    var sharedDependencies = new[] { "AutoMapper", "AutoMapper.Extensions.DependencyInjection", "Microsoft.Extensions.DependencyInjection" };
    if (sharedDependencies.Any(prefix => assemblyName.Name.StartsWith(prefix)))
    {
        return Default.LoadFromAssemblyName(assemblyName);
    }

    var existingAssembly = Default.Assemblies.FirstOrDefault(x => x.GetName().Name == assemblyName.Name && x.GetName().Version == assemblyName.Version);
    if (existingAssembly != null)
    {
        return existingAssembly;
    }

    string? assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
    if (assemblyPath != null)
    {
        return LoadFromAssemblyPath(assemblyPath);
    }

    return null;
}

2. 遍历所有加载上下文获取程序集

插件中调用AddAutoMapper时,不要只拿默认上下文的程序集,而是遍历所有AssemblyLoadContext收集程序集:

public void ConfigureServices(IServiceCollection services)
{
    var allAssemblies = AssemblyLoadContext.All
        .SelectMany(ctx => ctx.Assemblies)
        .Distinct();
    services.AddAutoMapper(allAssemblies);
}

3. 统一插件和根项目的依赖版本

确保插件项目中AutoMapper和AutoMapper.Extensions.DependencyInjection的版本与根项目完全一致,避免因版本差异导致方法签名不匹配。

内容的提问来源于stack exchange,提问作者Jim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 10:32:30