web-dev-qa-db-ja.com

AutoMapperのMapperConfiguration内でmapper.Mapを使用する方法は?

AutoMapperを使用してオブジェクトを別のオブジェクトにマップする必要があります。トリッキーな質問は、マッピング構成内またはカスタムタイプコンバーター内でマッパーのインスタンス(IMapperのインスタンス)にどのようにアクセスできますか?

以下のコードは機能しませんが、私が達成したい例です-mapper.Map呼び出しに注意し、マッピングCustomer => CustomerDtoおよびCustomer => DetailedCustomerDtoが定義されていると仮定してください。

var config = new MapperConfiguration(
    cfg => cfg.CreateMap<Order, OrderDto>()
        .ForMember(dst => dst.Customer, src => src.ResolveUsing(o => {
            return o.Type == 1
                ? mapper.Map<Customer, CustomerDto>(o.Customer)
                : mapper.Map<Customer, DetailedCustomerDto>(o.Customer)
            })
    );

クライアント部分は次のとおりです。

var mapper = config.CreateMapper();
var orderDto = mapper.Map<Order, OrderDto>(order);

マッピングするオブジェクトの簡略版は次のとおりです。

public class Order
{
    public int Type { get; set; }
    public Customer Customer { get; set; }
}

public class Customer
{
    public long Id { get; set; }
    public string Name { get; set; }
}

public class OrderDto
{
    public CustomerDto Customer { get; set; }
}

public class CustomerDto
{
    public long Id { get; set; }
}

public class DetailedCustomerDto : CustomerDto
{
    public string Name { get; set; }
}

上記のコードからわかるように、Order.Typeの値に基づいて、マッパーはOrder.Customerプロパティを異なるターゲットにマップする必要があります。 1つのターゲット(DetailedCustomerDto)が他のターゲット(CustomerDto)から継承するため、少し注意が必要です。

旧式で非推奨の静的メソッドMapper.Mapの使用はオプションではないことに注意してください

20
Anton

AutoMapper 5.1.1以降

4つのパラメーターを持つResolveUsingの別のオーバーロードを使用してマッパーにアクセスできます。4番目のパラメーターはResolutionContextcontext.Mapper):

var config = new MapperConfiguration(
    cfg => {
        cfg.CreateMap<Customer, CustomerDto>();
        cfg.CreateMap<Customer, DetailedCustomerDto>();
        cfg.CreateMap<Order, OrderDto>()
             .ForMember(dst => dst.Customer, src => src.ResolveUsing((order, orderDto, i, context) => {
                return order.Type == 1
                ? context.Mapper.Map<Customer, CustomerDto>(order.Customer)
                : context.Mapper.Map<Customer, DetailedCustomerDto>(order.Customer);
        }));
 });

 var orderTypeOne = new Order();
 orderTypeOne.Type = 1;
 orderTypeOne.Customer = new Customer() {
    Id = 1
 };

 var dto = config.CreateMapper().Map<Order, OrderDto>(orderTypeOne);
 Debug.Assert(dto.Customer.GetType() == typeof (CustomerDto));

 var orderTypeTwo = new Order();
 orderTypeTwo.Type = 2;
 orderTypeTwo.Customer = new Customer() {
     Id = 1
 };
 dto = config.CreateMapper().Map<Order, OrderDto>(orderTypeTwo);
 Debug.Assert(dto.Customer.GetType() == typeof (DetailedCustomerDto));

AutoMapper 5.1.1より前

2つのパラメーターを持つResolveUsingの別のオーバーロードを使用してマッパーにアクセスできます。最初のパラメーターはResolutionResultresult.Context.Engine.Mapper):

var config = new MapperConfiguration(
    cfg => {
        cfg.CreateMap<Customer, CustomerDto>();
        cfg.CreateMap<Customer, DetailedCustomerDto>();
        cfg.CreateMap<Order, OrderDto>()
             .ForMember(dst => dst.Customer, src => src.ResolveUsing((result, order) => {
                return order.Type == 1
                ? result.Context.Engine.Mapper.Map<Customer, CustomerDto>(order.Customer)
                : result.Context.Engine.Mapper.Map<Customer, DetailedCustomerDto>(order.Customer);
        }));
 });

 var orderTypeOne = new Order();
 orderTypeOne.Type = 1;
 orderTypeOne.Customer = new Customer() {
    Id = 1
 };

 var dto = config.CreateMapper().Map<Order, OrderDto>(orderTypeOne);
 Debug.Assert(dto.Customer.GetType() == typeof (CustomerDto));

 var orderTypeTwo = new Order();
 orderTypeTwo.Type = 2;
 orderTypeTwo.Customer = new Customer() {
     Id = 1
 };
 dto = config.CreateMapper().Map<Order, OrderDto>(orderTypeTwo);
 Debug.Assert(dto.Customer.GetType() == typeof (DetailedCustomerDto));
43
Evk

Evkの素晴らしい回答に加えて、カスタムコンストラクター(つまり、型に既定のコンストラクターがない)を必要とするconfig/profile内のマッピング内でマッピングを行う必要がある場合、v5.2.0で次のように機能します:

CreateMap<Models.Job, Models.API.Job>(MemberList.Source);

CreateMap<StaticPagedList<Models.Job>, StaticPagedList<Models.API.Job>>()
                .ConstructUsing((source, context) => new StaticPagedList<Models.API.Job>(
                    context.Mapper.Map<List<Models.Job>, List<Models.API.Job>>(source.ToList()),
                    source.PageNumber,
                    source.PageSize,
                    source.TotalItemCount));

この例では、あるオブジェクトタイプのX.PagedListカスタムコレクションタイプを、別のオブジェクトタイプの同等のコレクションにマッピングしています。 lamdba式の最初のパラメーターはソースオブジェクトであり、2番目はResolutionContextであり、そこからマッパーインスタンスにアクセスしてマップすることができます。

7
Breeno