Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AutoMapper mapping using interface vs concrete map

I don't think this is possible, but it's worth the question, I suppose.

I have the following types that share an interface (I promise, this isn't the same question I've asked before).

public interface ICustomer;

public class CustomerBO : ICustomer

public class CustomerSO : ICustomer // SO is Service Object in this case.

Then, I have the following mapping:

Mapper.Map<ICustomer, ICustomer>();

Now, here's where it gets interesting / confusing.

This works:

Mapper.Map<ICustomer, ICustomer>(customerSO, new CustomerBO);

This doesn't work:

Mapper.Map(customerSO, new CustomerBO());

Now, normally I wouldn't have a problem with just typing in the first Map statement with the two interface type defined, but my problem is when the Customer object is buried somewhere.

public class CustomerOrderDTO
{
    ICustomer customer;
}

public class CustomerOrderSO
{
    CustomerSO customer;
}

Mapper.Map<CustomerOrderDTO, CustomerOrderSO>();

This doesn't work, because there's no mapping from ICustomer to CustomerSO, so config assertion fails.

Currently, I'm going around the issue by doing this:

Mapper.CreateMap<CustomerOrderDTO, CustomerOrderSO>()
    .ForMember(desc => dest.customer
        , exp => exp.MapFrom(src => Mapper.Map<ICustomer, ICustomer>(src.customer
            , new CustomerSO));

However, I would have to do this for every DTO-type object that we have, and then quite possibly have a cascading effect.

I understand that technically I could do the following to resolve the issue:

Mapper.Map<CustomerBO, CustomerSO>();

However, in CustomerBO there are a lot of other properties used in the business logic not in the interface. Similarly, there are a lot of properties in CustomerSO not in the interface. If I were to go with the above route, I would have a ton of Ignore() calls, and I'd have to map CustomerBO to CustomerSO, and then CustomerSO to CustomerBO, each with their own unique list of Ignore calls. Using the interfaces removes the need for the Ignore calls, as the data that I want to be visible from one to the other is defined in the interface.

So, in short, my question is this: is there some way I can tell AutoMapper to use the interface map when it encounters one of the implementing classes? Failing that, is there some other (read: better) way than a Map call in a MapFrom delegate to enforce my interface-to-interface mapping in a as-needed basis?

like image 717
Lao Avatar asked Sep 06 '12 20:09

Lao


1 Answers

In general, you'd want to configure a mapping to the ICustomer interface and override the destination type using .As<TDestination>(). (Maybe this is an addition since you first asked the question.)

This would work for mappings between 2 layers, like your example:

Mapper.CreateMap<CustomerDTO, ICustomer>().As<CustomerModel>();
Mapper.CreateMap<CustomerModel, ICustomer>().As<CustomerDTO>();

However, extend this to another layer, such as mapping CustomerModel to CustomerViewModel, and this falls apart. You can only tell AutoMapper one destination type to use for instantiation.

But similar to the comments above, I would question the reason for having a property typed as the interface within those other classes. Why should a CustomerOrderModel have a ICustomer customer property? Can you replace customer with an instance of a CustomerDTO and still have your program be correct? If you have common behavior for objects that contain a customer, you could have those parent objects implement an interface, ICustomerAccessor. Then, use explicit interface implementation to add the weaker-typed property, and it won't have to interfere with the name.

like image 68
drew Avatar answered Sep 18 '22 16:09

drew