使用自定义配置文件

时间:2017-01-28 14:27:36

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

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

我确实决定为每个域实体创建一个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:此代码在程序集B中引用程序集A.

注意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 application运行并尝试解析配置文件时,会发生以下异常:

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 替换了个人资料这个类,那么它可以正常工作,但这样做会使程序集扫描无效轮廓。

    1. 最后,我通过创建一个继承自 AutoMapper.Profile 的基础抽象类并在Startup类的 ConfigureAutoMapper 方法中引用它来管理它,这样我就可以了对该类'后代......
    2. 执行程序集扫描
  2. 注意:此代码在Assembly 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();
        }
    

    ...但我仍然不明白为什么原始代码对我不起作用。

1 个答案:

答案 0 :(得分:0)

首先,您无需在Profile中明确调用DoctorsMappingProfile的受保护构造函数。原因是您传递的profileName是AutoMapper默认使用的builder .RegisterAssemblyTypes(registeredAssemblies) .AssignableTo(typeof(Profile)) .As<Profile>() .UsingConstructor(typeof(string)); ,因为我们可以看到here

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

Profile

这意味着您明确要求Autofac使用具有一个string类型参数的构造函数来创建string类的实例。问题是Autofac不知道它必须在构造函数中使用UsingConstructor

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

我不确定在没有参数的情况下调用Profile是什么,但在我看来,你最好离开Autofac为你选择最好的构造函数,特别是当defaut参数少的是一个你想要它使用。

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

  • 创建一个完全模仿string所具有的基类,即默认的无参数构造函数和带UsingConstructor的构造函数
  • 通过不使用 Profile
  • 在Autofac容器中注册个人资料

所以我很确定通过:

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

你应该没事。