How to use the new IValueResolver of AutoMapper?
Asked Answered
D

2

27

I am at a loss as to how to use the new IValueResolver interface in the new version of AutoMapper. Perhaps I used them improperly in the previous versions of AutoMapper...

I have a lot of model classes, some of them are generated from several databases on several database servers, using sqlmetal.

Some of these classes has a string property, PublicationCode, which identifies which publication the subscription, or offer, or invoice, or whatever it is, belongs to.

The publication can exist in either of two systems (the old and the new system), hence I have a bool property on the destination model classes which tells whether the publication is in the old or the new system.

Using the old version (<5?) of AutoMapper, I used a ValueResolver<string, bool> which took the PublicationCode as an input parameter, and returned a bool indicating the location of the publication (old or new system).

With the new version (5+?) of AutoMapper, this seems to no longer be possible. The new IValueResolver requires a unique implementation of each and every combination of source and destination models that I have, where src.PublicationCode needs to be resolved into a dst.IsInNewSystem.

Am I just trying to use the value resolvers in the wrong way? Is there a better way? The main reason I would like to use a resolver is that I would prefer to have services injected into the constructor, and not having to use DependencyResolver and the like in the code (I'm using Autofac).

Currently, I use it in the following way:

// Class from Linq-to-SQL, non-related properties removed.
public class FindCustomerServiceSellOffers {
    public string PublicationCode { get; set; }
}

This is one of several data model classes I have, which contains a PublicationCode property). This particular class is mapped to this view model:

public class SalesPitchViewModel {
    public bool IsInNewSystem { get; set; }
}

The mapping definition for these two classes is (where expression is an IProfileExpression), non-related mappings removed:

expression.CreateMap<FindCustomerServiceSellOffers, SalesPitchViewModel>()
          .ForMember(d => d.IsInNewSystem, o => o.ResolveUsing<PublicationSystemResolver>().FromMember(s => s.PublicationCode));

And the resolver:

public class PublicationSystemResolver : ValueResolver<string, bool>
{
    private readonly PublicationService _publicationService;
    public PublicationSystemResolver(PublicationService publicationService)
    {
        _publicationService = publicationService;
    }

    protected override bool ResolveCore(string publicationCode)
    {
        return _publicationService.IsInNewSystem(publicationCode);
    }
}

And the use of the mapper:

var result = context.FindCustomerServiceSellOffers.Where(o => someCriteria).Select(_mapper.Map<SalesPitchViewModel>).ToList();
Duky answered 11/7, 2016 at 12:2 Comment(1)
Done. I added somewhat terse code to show how I currently use AutoMapper.Duky
S
22

You can create a more general value resolver by implementing IMemberValueResolver<object, object, string, bool> and using that in your mapping configuration. You can provide a source property resolution function as before:

public class PublicationSystemResolver : IMemberValueResolver<object, object, string, bool>
{
    private readonly PublicationService _publicationService;

    public PublicationSystemResolver(PublicationService publicationService)
    {
        this._publicationService = publicationService;
    }

    public bool Resolve(object source, object destination, string sourceMember, bool destMember, ResolutionContext context)
    {
        return _publicationService.IsInNewSystem(sourceMember);
    }
}



cfg.CreateMap<FindCustomerServiceSellOffers, SalesPitchViewModel>()
    .ForMember(dest => dest.IsInNewSystem,
        src => src.ResolveUsing<PublicationSystemResolver, string>(s => s.PublicationCode));
Surprising answered 11/7, 2016 at 13:51 Comment(2)
Oh cool, didn't even see that interface. I have implemented your suggestions on all of my resolvers, but now I have some strange exception about op_Equality, and while I think it related to upgrading AutoMapper, it's probably not related to this particular question. Thank you very much for your help!!Duky
Note: whilst this answer is still perfectly valid, ResolveUsing() had been replaced with MapFrom() in later versions of AutoMapperLiter
S
1

So from my side, I want to add a few little things; try it

builder.Services.AddAutoMapper(typeof(TransactionProfile).Assembly); // working
builder.Services.AddAutoMapper(x => x.AddProfile<(TransactionProfile)>()); // not working
builder.Services.AddAutoMapper(x => x.AddMaps("Outlay.Infrastructure")); // not working
Shovelnose answered 9/12, 2022 at 3:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.