【问题标题】:Autofac.Core.DependencyResolutionException when configuring AutoMapper with custom profile使用自定义配置文件配置 AutoMapper 时出现 Autofac.Core.DependencyResolutionException
【发布时间】:2017-06-14 03:13:38
【问题描述】:

我正在使用 Autofac 4.3、Autofac.Owin 4、Autofac.Mvc5 4、AutoFac.Mvc5.Owin 4 和 AutoMapper 5.2 在 Asp.Net MVC5 应用程序中配置 AutoMapper 以将域对象从/映射到视图模型对象。

我确实决定为每个域实体创建一个 AutoMapper 配置文件,如下所示,只留下不带参数的构造函数,因为我想设置配置文件的名称。

注意:此代码在程序集 A 中。

public partial class DoctorsMappingProfile : Profile
{
    #region Constructors

    public DoctorsMappingProfile() : base(typeof(DoctorsMappingProfile).FullName)
    {
        // Code of mapping removed because it is not part of the problem
    }

    #endregion Constructors
}

注册 AutoMapper 和我遵循的配置文件 thisthis 指南由用户 mpetito there 进行了很好的解释,并且它在用户检查 here 时工作。我在 Startup 部分类中的代码是这样的:

注意 1:此代码在引用程序集 A 的程序集 B 中。

注意2:我对注册的程序集进行程序集扫描,然后将扫描的程序集数组作为参数传递给ContainerBuilder的方法RegisterAssemblyTypes

    public partial class Startup
{
    #region Methods

    public void ConfigureAutoFacContainer(IAppBuilder app)
    {

        var builder = new ContainerBuilder();
        builder.RegisterControllers(typeof(MvcApplication).Assembly); 
        builder.RegisterFilterProvider(); 
        builder.RegisterSource(new ViewRegistrationSource());
        // TODO: Logger!                        
        var assemblies = BuildManager.GetReferencedAssemblies().Cast<Assembly>().ToArray();         
        this.ConfigureAutoMapper(builder, assemblies);
        // TODO: Modules!
        var container = builder.Build();
        DependencyResolver.SetResolver(new AutofacDependencyResolver(container));                       
        app.UseAutofacMiddleware(container);
        app.UseAutofacMvc();
    }

    private void ConfigureAutoMapper(ContainerBuilder builder, Assembly[] registeredAssemblies)
    {
        //register your profiles, or skip this if you don't want them in your container
        builder.RegisterAssemblyTypes(registeredAssemblies).AssignableTo(typeof(Profile)).As<Profile>(); //.UsingConstructor(typeof(string));

        //register your configuration as a single instance
        builder.Register(ctx => new MapperConfiguration(cfg =>
        {
            //add your profiles (either resolve from container or however else you acquire them)
            foreach (var profile in ctx.Resolve<IEnumerable<Profile>>())
            {
                cfg.AddProfile(profile);
            }
        })).AsSelf().SingleInstance();

        //register your mapper
        builder.Register(ctx => ctx.Resolve<MapperConfiguration>()
            .CreateMapper(ctx.Resolve))
            .As<IMapper>()
            .InstancePerLifetimeScope();
    }

    #endregion Methods
}

当 de 应用程序运行并尝试解析配置文件时,会发生以下异常:

Autofac.Core.DependencyResolutionException 无法使用可用的服务和参数调用在类型“AutoMapper.Configuration.MapperConfigurationExpression+NamedProfile”上使用“Autofac.Core.Activators.Reflection.DefaultConstructorFinder”找到的任何构造函数:无法解析构造函数的参数“System.String profileName” Void .ctor(System.String, System.Action`1[AutoMapper.IProfileExpression])'。

当上下文尝试解析配置文件时,错误的行是 foreach 循环

foreach (var profile in ctx.Resolve<IEnumerable<Profile>>())

我相信这是由于 Autofac 默认构造函数位置约定,它正在寻找具有大多数参数的构造函数,这是 AutoMapper.Profile 类的情况:

protected Profile(string profileName, Action<IProfileExpression> configurationAction) 
    {
    }

为了解决这个问题,我替换了查找配置文件的行,即 ConfigureAutoMapper 中的第一行代码,强制它使用不带参数的构造函数:

builder.RegisterAssemblyTypes(registeredAssemblies).AssignableTo(typeof(Profile)).As<Profile>().UsingConstructor(); 

但还是不行。

我找到的解决方案/解决方法:

  1. 如果我在方法 ConfigureAutoMapper 中用我的 DoctorsMappingProfile 替换类 Profile,它会起作用,但这样做会使程序集扫描配置文件无效。

    1. 最后我通过创建一个继承自 AutoMapper.Profile 的基本抽象类并在 Startup 类的 ConfigureAutoMapper 方法中引用它来管理它,这样我可以对该类的后代执行程序集扫描...

注意:此代码在汇编 C 中

public abstract class G2AutoMapperProfile : Profile
{
    #region Constructors

    protected G2AutoMapperProfile()
    {
    }

    protected G2AutoMapperProfile(string profileName) : base(profileName)
    {
    }

    #endregion Constructors
}

注意:这段代码在汇编A中

public partial class DoctorsMappingProfile : G2AutoMapperProfile //Profile
{
    #region Constructors

    public DoctorsMappingProfile() : base(typeof(DoctorsMappingProfile).FullName)
    {
        //removed because it is not part of the problem
    }

    #endregion Constructors
}

最后,这是程序集 B 中 ConfigureAutoMapper 的代码,它可以工作:

private void ConfigureAutoMapper(ContainerBuilder builder, Assembly[] registeredAssemblies)
    {
        //register your profiles, or skip this if you don't want them in your container
        builder.RegisterAssemblyTypes(registeredAssemblies).AssignableTo(typeof(G2AutoMapperProfile)).As<G2AutoMapperProfile>();

        //register your configuration as a single instance
        builder.Register(ctx => new MapperConfiguration(cfg =>
        {
            //add your profiles (either resolve from container or however else you acquire them)
            foreach (var profile in ctx.Resolve<IEnumerable<G2AutoMapperProfile>>())
            {
                cfg.AddProfile(profile);
            }
        })).AsSelf().SingleInstance();

        //register your mapper
        builder.Register(ctx => ctx.Resolve<MapperConfiguration>()
            .CreateMapper(ctx.Resolve))
            .As<IMapper>()
            .InstancePerLifetimeScope();
    }

...但我仍然不明白为什么原始代码不适合我。

【问题讨论】:

    标签: c# asp.net-mvc-5 owin automapper autofac


    【解决方案1】:

    首先,您无需在DoctorsMappingProfile 中显式调用Profile 的受保护构造函数。原因是您传递的profileName 是AutoMapper 默认使用的here

    另外,关于您尝试在 Autofac 容器中注册配置文件的第一种方式:

    builder
        .RegisterAssemblyTypes(registeredAssemblies)
        .AssignableTo(typeof(Profile))
        .As<Profile>()
        .UsingConstructor(typeof(string));
    

    这意味着你明确要求 Autofac 使用构造函数创建你的 Profile 类的实例,该构造函数只有一个类型为 string 的参数。问题在于 Autofac 不知道它必须在构造函数中使用什么 string

    这也是错误的,因为您创建了一个无参数构造函数,而该构造函数又调用了参数化构造函数。

    我不确定在没有参数的情况下调用 UsingConstructor 会做什么,但在我看来,你最好让 Autofac 为你选择最好的构造函数,尤其是当你使用默认的无参数构造函数时想要它使用。

    通过查看您提供的解决方案/解决方法,我的感觉得到了加强。您实际上所做的是:

    • 创建一个完全模仿Profile 的基类,即一个默认的无参数构造函数和一个采用string 的构造函数
    • 通过不使用在Autofac容器中注册配置文件UsingConstructor

    所以我很确定:

    • 摆脱您的自定义基类
    • 让您的个人资料再次从 Profile 继承
    • 在不使用UsingConstructor 的情况下注册您的个人资料

    你应该没事的。

    【讨论】:

    • 我很抱歉我的错误。我更正了粘贴在原始帖子中的代码。我描述的问题在没有使用 .UsingConstructor(typeof(string)) 的情况下发生,是的,我在 Profile 的后代中只留下一个无参数构造函数,因为它是我唯一需要的。也许这个错误是因为 Autofac 正在基类 Profile 中寻找构造函数,而不是在它在注册程序集中找到的类中寻找构造函数,该类只有一个无参数构造函数。
    猜你喜欢
    • 2014-02-14
    • 2016-03-27
    • 2021-06-16
    • 1970-01-01
    • 2012-01-11
    • 2023-02-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多