Подтвердить что ты не робот

Новый API API Autofac и Automapper - ConfigurationStore отсутствует

Я использую Automapper и Autofac в приложении .Net в течение некоторого времени. Я настроил их следующим образом:

builder.RegisterAssemblyTypes(typeof (OneOfMyMappingProfiles).Assembly)
        .Where(t => t.IsSubclassOf(typeof (Profile)))
        .As<Profile>();

builder.Register(ctx => new ConfigurationStore(new TypeMapFactory(), MapperRegistry.Mappers))
        .AsImplementedInterfaces()
        .SingleInstance()
        .OnActivating(x =>
        {
            foreach (var profile in x.Context.Resolve<IEnumerable<Profile>>())
            {
                x.Instance.AddProfile(profile);
            }
        });

builder.RegisterType<MappingEngine>()
            .As<IMappingEngine>().SingleInstance();

С последней версией Automapper (4.2) API изменился, и у меня возникла проблема с переводом на новый API. ConfigurationStore больше не существует. Согласно документам, способ зарегистрироваться в МОК теперь выглядит следующим образом:

 var profiles =
        from t in typeof (AutoMapperRegistry).Assembly.GetTypes()
        where typeof (Profile).IsAssignableFrom(t)
        select (Profile)Activator.CreateInstance(t);

    var config = new MapperConfiguration(cfg =>
    {
        foreach (var profile in profiles)
        {
            cfg.AddProfile(profile);
        }
    });

    For<MapperConfiguration>().Use(config);
    For<IMapper>().Use(ctx => ctx.GetInstance<MapperConfiguration>().CreateMapper(ctx.GetInstance));

НО это использует StructureMap. Первая половина этого не проблема, но я не уверен, как перевести часть "For < > . Use()". Как это сделать в Autofac?

4b9b3361

Ответ 1

OK. Проработал. Вот замена:

var profiles =
        from t in typeof(LocationMappingProfile).Assembly.GetTypes()
        where typeof(Profile).IsAssignableFrom(t)
        select (Profile)Activator.CreateInstance(t);

        builder.Register(ctx => new MapperConfiguration(cfg =>
        {
            foreach (var profile in profiles)
            {
                cfg.AddProfile(profile);
            }
        }));

        builder.Register(ctx => ctx.Resolve<MapperConfiguration>().CreateMapper()).As<IMapper>();

UPDATE

Вот пример профиля. Супер простой. В этом случае у меня есть только одно отображение. Но я мог бы добавить других. Я пытаюсь объединить их логически вместе с Entity. Таким образом, в этом случае любое будущее сопоставление от или до ProviderDetail будет в этом файле. Сопоставления с другим объектом будут в отдельном классе сопоставления. Ничего не вводилось в класс профиля:

 public class ProviderMappingProfile : Profile
{
    protected override void Configure()
    {
        CreateMap<ProviderDetail, ProviderListItem>();
    }
}

UPDATE2

Вот пример теста, который доказывает правильность отображения:

public class ProviderMappingProfileTests
{
    [Fact]
    public void CreateMap_ProviderDetailToProviderQueryResult_IsValid()
    {
        var config = new MapperConfiguration(cfg =>
            cfg.CreateMap<ProviderDetail, ProviderListItem>()
            );

        config.AssertConfigurationIsValid();
    }
}

Ответ 2

Еще один интересный метод регистрации Automapper через Autofac с динамической регистрацией всех пользовательских профилей в ваших сборках.

Здесь вы регистрируете профили для всех необходимых сборок.

builder.RegisterAssemblyTypes(Assembly.GetAssembly(typeof(Program)))
    .Where(t => t.BaseType == typeof(Profile)
                && !t.IsAbstract && t.IsPublic)
    .As<Profile>();

И здесь вы решаете те:

builder.Register(ctx => new MapperConfiguration(cfg =>
{
    foreach (var profile in ctx.Resolve<IEnumerable<Profile>>())
        cfg.AddProfile(profile);
}));

builder.Register(ctx => ctx.Resolve<MapperConfiguration>()
                           .CreateMapper())
       .As<IMapper>()
       .SingleInstance();

Кроме того, можно использовать

.CreateMapper(ctx.Resolve)

вместо

.CreateMapper()

для разрешения внутренних зависимостей в профилях. Но для этого потребуется удалить

.SingleInstance()

от регистрации.

И вот использование BTW:

public SomeAutowiredClass
{
        public IMapper Mapper { get; set; }

        public void SomeMethod(SomeModel model){
             Mapper.Map<AnotherModel>(model)
        }
}

Ответ 3

Я согласился с решением выше. Вместо того, чтобы использовать отражение, мы не можем сделать это ниже?.

public static IMapper RegisterAutoMapper()
{
    var config = new MapperConfiguration(cfg =>
    {
        cfg.AddProfile<ApiMappingProfile1>();
        cfg.AddProfile<ApiMappingProfile2>();
        // Add all your profiles
    });
    var mapper = config.CreateMapper();
    return mapper;
}

В регистрации Autofac

builder.Register(c => AutoMapperConfig.RegisterAutoMapper()).As<IMapper>()
    .InstancePerLifetimeScope().PropertiesAutowired().PreserveExistingDefaults();

Ответ 4

Вот небольшая модификация, которая работает для меня. Он сканирует все загруженные сборки и загружает с них все профили AutoMapper.

    var autoMapperProfileTypes = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes().Where(p => typeof(Profile).IsAssignableFrom(p) && p.IsPublic && !p.IsAbstract));
    var autoMapperProfiles = autoMapperProfileTypes.Select(p => (Profile)Activator.CreateInstance(p));
    builder.Register(ctx => new MapperConfiguration(cfg =>
    {
        foreach (var profile in autoMapperProfiles)
        {
            cfg.AddProfile(profile);
        }
    }));
    builder.Register(ctx => ctx.Resolve<MapperConfiguration>().CreateMapper()).As<IMapper>().PropertiesAutowired();

Ему нужно отфильтровать абстрактные и частные классы, чтобы пропустить встроенные автомастеры.