Autofac.Core.DependencyResolutionException при настройке AutoMapper с настраиваемым профилемC#

Место общения программистов C#
Anonymous
Autofac.Core.DependencyResolutionException при настройке AutoMapper с настраиваемым профилем

Сообщение Anonymous »

Я настраиваю AutoMapper для сопоставления объектов домена с объектами модели представления или с ними в приложении Asp.Net MVC5 с помощью Autofac 4.3, Autofac.Owin 4, Autofac.Mvc5 4, AutoFac.Mvc5.Owin 4 и AutoMapper 5.2.

Я решил создать один профиль 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 и профили, я следовал этому и этому руководству, которое хорошо объяснено пользователем mpetito, и что оно работает так, как пользователь проверил здесь. Мой код в частичном классе Startup следующий:

Примечание 1: этот код находится в сборке B, которая ссылается на сборку A.

Примечание 2: я выполняю сканирование зарегистрированных сборок, затем передаю в качестве параметра массив отсканированных сборок в метод RegisterAssemblyTypes ContainerBuilder

Код: Выделить всё

    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().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
(); //.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())
{
cfg.AddProfile(profile);
}
})).AsSelf().SingleInstance();

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

#endregion Methods
}
Когда приложение запускается и пытается разрешить профиль, происходит следующее исключение:

Autofac.Core.DependencyResolutionException
Ни один из конструкторов не найден с помощью «Autofac.Core.Activators.Reflection.DefaultConstructorFinder» для типа «AutoMapper.Configuration.MapperConfigurationExpression+NamedProfile» может быть вызван с доступными службами и параметрами: невозможно разрешить параметр «System.String ProfileName» конструктора «Void .ctor(System.String, System.Action`1[AutoMapper.IProfileExpression])».


строка ошибки — это цикл foreach, когда контекст пытается разрешить профиль:

Код: Выделить всё

foreach (var profile in ctx.Resolve())
Я считаю, что из-за соглашения о расположении конструктора по умолчанию Autofac он ищет конструктор с большинством параметров, как в случае с классом AutoMapper.Profile:

Код: Выделить всё

protected Profile(string profileName, Action configurationAction)
{
}
Чтобы это исправить, я заменил строку, которая ищет профили, первую строку кода в ConfigureAutoMapper, заставив ее использовать конструктор без параметров:

Код: Выделить всё

builder.RegisterAssemblyTypes(registeredAssemblies).AssignableTo(typeof(Profile)).As
().UsingConstructor();
но все равно не работает.

Решения/обходные пути, которые я нашел:
  • Если я заменю класс Profile на свой DoctorsMappingProfile в методе ConfigureAutoMapper, это работает, но это приведет к аннулированию сканирования сборки для профилей.


    Наконец, мне удалось создать базовый абстрактный класс, унаследованный от AutoMapper.Profile, и ссылаясь на него в моем методе ConfigureAutoMapper класса Startup. Таким образом, я могу выполнять сканирование сборки для потомков этого класса...

Примечание: этот код написан на ассемблере 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
}
и, наконец, это код ConfigureAutoMapper в сборке B, который работает:

Код: Выделить всё

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();

//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())
{
cfg.AddProfile(profile);
}
})).AsSelf().SingleInstance();

//register your mapper
builder.Register(ctx => ctx.Resolve()
.CreateMapper(ctx.Resolve))
.As()
.InstancePerLifetimeScope();
}
... но я все равно не понимаю, почему исходный код у меня не работает.

Вернуться в «C#»