Как передать аргументы конструктору профиля базового отображения? - PullRequest
0 голосов
/ 22 октября 2019

В настоящее время я переносю несколько профилей сопоставления из Automapper 4.2.1 в последнюю версию 9.0.0. Старые профили сопоставления построены иерархически, где абстрактный базовый класс требует аргумент типа IDatetime. Эта инъекция используется только для тестирования.

public abstract MappingProfileBase : Profile 
{
    protected MappingProfileBase(IDatetime datetime)
    {
        this.CreateMap<Foo, FooDto>()
            .ForMember(dest => dest.SomeTimeStamp, opt => opt.MapFrom(src => datetime));
            // Further mappings 
    }
}

public MappingProfileA : MappingProfileBase
{
    public MappingProfileA(IDatetime datetime) : base(datetime)
    {
        this.CreateMap<FooDerived, FooDerivedDto>()        
            // Further and more specific mappings 
    }   
}

Теперь я хотел бы перейти к новым Include и IncludeBase<> методам и отменить наследование MappingProfileA и MappingProfileBase, но просто не знаю, как обращаться с введеннымиинтерфейс. Ни один из новых методов не принимает никаких аргументов.

Вот как я думаю должен выглядеть так:

public class MappingProfileBase : Profile
{
    public MappingProfileBase(IDatetime datetime)
    {
        this.CreateMap<Foo, FooDto>()
            .ForMember(dest => dest.SomeTimeStamp, opt => opt.MapFrom(src => datetime));
            // Further mappings
    }
}

public class MappingProfileA : Profile
{
    public MappingProfileA()
    {
        this.CreateMap<FooDerived, FooDerivedDto>();
            .IncludeBase<Foo, FooDto>();
    }
}

Так как я могу передать аргументы конструктору base ? Какие еще возможности существуют?

1 Ответ

0 голосов
/ 22 октября 2019

Спасибо (снова) Люциану. Я решил это путем предоставления профиля экземпляр до AddProfile (вместо Type) с использованием Autofac. Для этого мне пришлось заранее зарегистрировать конкретный тип контейнера.

private static MapperConfiguration GetMappingConfiguration()
{
    var containerBuilder = new ContainerBuilder();
    containerBuilder.RegisterType<DateTime>().As<IDateTime>();

    var assembly = Assembly.GetExecutingAssembly();
    var loadedProfiles = assembly.ExportedTypes
        .Where(type => type.IsSubclassOf(typeof(Profile)))
        .ToArray();

    containerBuilder.RegisterTypes(loadedProfiles);

    var container = containerBuilder.Build();

    var config = new MapperConfiguration(cfg =>
    {
        cfg.ConstructServicesUsing(container.Resolve);

        foreach (var profile in loadedProfiles)
        {
            var resolvedProfile = container.Resolve(profile) as Profile;
            cfg.AddProfile(resolvedProfile);
        }
    });

    return config;
}

Вот два примера профилей:

public class BaseMappingProfile : Profile
{
    public BaseMappingProfile(IDateTime datetime)
    {
        this.CreateMap<Foo, FooDto>()
            .ForMember(d => d.Timestamp, o => o.MapFrom(s => datetime))
            .ForMember(d => d.PropertyA, o => o.MapFrom(s => s.Property1));
    }
}

public class FooMappingProfile : Profile
{
    public FooMappingProfile()
    {
        this.CreateMap<FooDerived, FooDerivedDto>()
            .IncludeBase<Foo, FooDto>()
            .ForMember(d => d.PropertyB, o => o.MapFrom(s => s.Property2));
    }
}

А затем в рамках модульного теста:

[Fact]
public void Should_Map_From_Foo_To_FooDto()
{
    var config = GetMappingConfiguration();
    var mapper = config.CreateMapper();

    var foo = new Foo {Property1 = "I am property 1"};
    var fooDto = mapper.Map<FooDto>(foo);

    // Asserts...
}

Конечно, статический метод GetMappingConfiguration требуетдальнейшая модификация. Но в принципе это работает.

Использование IncludeBase позволяет избежать дублирования кода. Но с другой стороны, более сложные профили и настройка теста особенно в этом случае.

...