Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using the instance version of CreateMap and Map with a WCF service?

Been having some real issues with automapper. I think I have found the solution but unsure of how to implement it.

basically I am using a custom mapping with ResolveUsing and ConstructedBy to pass in params to the constructor, I understand that most people set this up in the global.asax once and forget about it.

But the problem is that my method (on a wcf) passes in different params to the constructor of a ResolveUsing ......

Before I was using the Mapper.CreateMap and Mapper.Map which are static methods and it appears that when different petitions come into the wcf service via methods (multi -user) they are conflicting with each other.

After reading something it appears I can use the instance version of CreateMap and Map so that each individual petition gets its own map and can pass in its own params.

But I can't seem to find how to do it. Can anyone explain please? I am really stuck...

Before now and again I would get duplicate key errors and also I put in a log trace on the constructor and it appears that 1 petition is overwriting the other - hence the static versions of Mapper.

Well I hope I am correct, but I can't find anything else...

EDITED - AN EXAMPLE OF WHAT I HAVE

Basically all mapping is working as it should, as I am using MapFrom in most cases.

Then I create an instance of my Resolver which I pass in a URL. I have checked the url before I pass it in and its correct. But once it returns it returns the wrong URL.

The reason I need pass in the URL is that it has variables in there so I need to replaced the variables... Basically there are 2 urls depending on the office and I have logs everywhere and I can see what I am passing in but once I pass it in - it isn't the one I passed in, if that makes sense, this is weird!!

Its a WCF service and a client has called the method twice passing in 2 different offices hence 2 different URLs. But they always return the same URL. It's like one session is overwriting the other...

I hope this makes sense.

  SalesPointResolver newSalesPointResolver = new SalesPointResolver(returnReservationUrl, reservationSite.ReservationUrl, startDate, endDate, officeCode);


        Mapper.CreateMap<Models.Custom.House, DTO.House>()
            .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id))
            .ForMember(dest => dest.TaxIncluded,
                       opt => opt.MapFrom(src => src.Segments.FirstOrDefault().TaxIncluded))
            .ForMember(dest => dest.TaxPercentage,
                       opt => opt.MapFrom(src => src.Segments.FirstOrDefault().TaxPercentage))

            .ForMember(dest => dest.SalesPoints,
                       opt =>
                       opt.ResolveUsing(newSalesPointResolver))
            ;

FOUND OUT WHERE IS FAILING - BUT UNKNOWN WHY

See my comments inline with code. In the constructor the urlTemplate arrives, I save it in a private var and then in the overridden ResolveCore it's something else :-)

I have placed some log4net logs on there, so I can see whats happening.

[Log]
public class SalesPointResolver : ValueResolver<Models.Custom.House, IList<DTO.SalesPoint>>
{
    private readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

    private string urlTemplate;

    public SalesPointResolver (bool returnReservationUrl, string urlTemplate, DateTime startDate, DateTime endDate, string officeCode)
    {
        this.urlTemplate = urlTemplate;

        log.Error("passed in " + urlTemplate); // THIS IS PERFECT
        log.Error("I am now " + this.urlTemplate); // THIS IS PERFECT
    }

    protected override IList<DTO.SalesPoint> ResolveCore(House source)
    {
        this.house = source;

        log.Error("in  resolveCore :" + this.urlTemplate); // THIS IS RETURNING THE WRONG VALUE

TEMPORARY SOLUTION

I have done a temporary solution but it's really bad. I am sure automapper can do what I am trying, but I am obviously doing something wrong.

Basically I return via LINQ a collection of records (THIS IS MY SOURCE) so I entered a new field on every record that has the correct URL template on there. And then, instead of passing in (via constructor) the url template, I have it available as a property on EVERY record on the collection (THE SOURCE) ... and it works perfect.

Of course, this really is patch and not ideal but it gets me running.

Where am I going wrong?

like image 636
mark smith Avatar asked Nov 03 '09 17:11

mark smith


2 Answers

Yes, there is a way to use an instance version of AutoMapper.

Instead of...

Mapper.CreateMap<Dto.Ticket, Entities.Ticket>()

you can use:

var configurationStore =
    new ConfigurationStore(new TypeMapFactory(), MapperRegistry.Mappers);
var mapper = new MappingEngine(configurationStore);
configurationStore.CreateMap<Dto.Ticket, Entities.Ticket>()
like image 174
root Avatar answered Nov 09 '22 07:11

root


In response to Luke Woodwards's comment on the newer syntax:

ConfigurationStore store 
   = new ConfigurationStore(new TypeMapFactory(), MapperRegistry.Mappers);
store.AssertConfigurationIsValid();
MappingEngine engine = new MappingEngine(store);

//add mappings via Profiles or CreateMap
store.AddProfile<MyAutoMapperProfile>();
store.CreateMap<Dto.Ticket, Entities.Ticket>();
like image 33
Jay Walker Avatar answered Nov 09 '22 09:11

Jay Walker