views:

846

answers:

2

Update 1-13-10 I've been able to find some success using the code below for mapping. I am essentially ignoring any of the properties that do not have a mapping and mapping them afterwards. I would appreciate feedback as to whether or not I am going about this in the best way possible. In addition, I am not sure how to go about unit testing this mapping. It was my impression that using the AutoMapper should help alleviate tediousness of checking each property.

Here is my new code:

Mapper.CreateMap<MoveEntity, MoveEntityDto>()
           .ForMember(dest => dest.PrimaryOriginTransferee, opt => opt.Ignore())
           .ForMember(dest => dest.PrimaryDestinationTransferee, opt => opt.Ignore())
           .ForMember(dest => dest.Customer, opt => opt.Ignore())
           .ForMember(dest => dest.DestinationAddress, opt => opt.Ignore())
           .ForMember(dest => dest.OriginAddress, opt => opt.Ignore())
           .ForMember(dest => dest.Order, opt => opt.Ignore())
           .ForMember(dest => dest.Shipment, opt => opt.Ignore())
           .ForMember(dest => dest.SourceSystemName, opt => opt.Ignore());

        Mapper.CreateMap<ContactEntity, TransfereeEntityDto>();
        Mapper.CreateMap<CustomerEntity, CustomerEntityDto>();
        Mapper.CreateMap<AddressEntity, AddressEntityDto>();
        Mapper.CreateMap<OrderEntity, OrderEntityDto>()
            .ForMember(dest => dest.OrderForwarding, opt => opt.Ignore())
            .ForMember(dest => dest.Forwarder, opt => opt.Ignore());
        Mapper.CreateMap<ShipmentEntity, ShipmentEntityDto>()
            .ForMember(dest => dest.Services, opt => opt.Ignore());
        Mapper.CreateMap<ServiceEntity, ServiceEntityDto>()
            .ForMember(dest => dest.ServiceTypeCode, opt => opt.Ignore()) //TODO: ServiceTypeCode not being mapped, should it?
            .ForMember(dest => dest.SourceSystemName, opt => opt.MapFrom(src => Enum.GetName(typeof(SourceSystemName), src.SourceSystemName)));
        Mapper.CreateMap<OrderForwardingEntity, OrderForwardingEntityDto>();


        Mapper.AssertConfigurationIsValid();


        MoveEntityDto moveEntityDto = Mapper.Map<MoveEntity, MoveEntityDto>(moveEntity);
        moveEntityDto.PrimaryDestinationTransferee = Mapper.Map<ContactEntity, TransfereeEntityDto>(moveEntity.PrimaryDestinationTransferee);
        moveEntityDto.PrimaryOriginTransferee = Mapper.Map<ContactEntity, TransfereeEntityDto>(moveEntity.PrimaryOriginTransferee);
        moveEntityDto.Customer = Mapper.Map<CustomerEntity, CustomerEntityDto>(moveEntity.Customer);
        moveEntityDto.DestinationAddress = Mapper.Map<AddressEntity, AddressEntityDto>(moveEntity.DestinationAddress);
        moveEntityDto.OriginAddress = Mapper.Map<AddressEntity, AddressEntityDto>(moveEntity.OriginAddress);
        moveEntityDto.Order = Mapper.Map<OrderEntity, OrderEntityDto>(moveEntity.Order);
        moveEntityDto.Order.OrderForwarding = Mapper.Map<OrderForwardingEntity, OrderForwardingEntityDto>(moveEntity.Order.OrderForwarding);
        //moveEntityDto.Order.Forwarder = Mapper.Map<ForwarderEntity, ForwarderEntityDto>(moveEntity.Order.Forwarder);  //Apparently there is no forwarder entity for an Order
        moveEntityDto.Shipment = Mapper.Map<ShipmentEntity, ShipmentEntityDto>(moveEntity.Shipment);
        moveEntityDto.Shipment.Services = Mapper.Map<ServiceEntity[], ServiceEntityDto[]>(moveEntity.Shipment.ServiceEntities);

Original Post:

I'm attempting to use AutoMapper for the first time in order to map from a Bussiness Object to a DTO. I am running into issues that I do not know how to troubleshoot, including the following exception:

AutoMapper.AutoMapperMappingException: Trying to map Graebel.SP.BO.MoveEntity to Graebel.SOA.Contracts.DataContracts.SP.MoveEntity. Exception of type 'AutoMapper.AutoMapperMappingException' was thrown

Here is the AutoMapper Code that I am running:

public MoveEntityDto MapMoveEntityToMoveEntityDto(MoveEntity moveEntity)
    {
        Mapper.CreateMap<MoveEntity, MoveEntityDto>()
            .ForMember(dest => dest.PrimaryOriginTransferee, opt => opt.MapFrom(src => src.PrimaryOriginTransferee))
            .ForMember(dest => dest.PrimaryDestinationTransferee,opt => opt.MapFrom(src => src.PrimaryDestinationTransferee))
            .ForMember(dest => dest.Customer, opt => opt.MapFrom(src => src.Customer))
            .ForMember(dest => dest.DestinationAddress, opt => opt.MapFrom(src => src.DestinationAddress))
            .ForMember(dest => dest.Order, opt => opt.MapFrom(src => src.Order))
            .ForMember(dest => dest.OriginAddress, opt => opt.MapFrom(src => src.OriginAddress))
            .ForMember(dest => dest.Shipment, opt => opt.MapFrom(src => src.Shipment))
            .ForMember(dest => dest.SourceSystemName, opt => opt.Ignore());

        Mapper.AssertConfigurationIsValid();
        MoveEntityDto moveEntityDto = Mapper.Map<MoveEntity, MoveEntityDto>(moveEntity);

        return moveEntityDto;
    }

Here is the DTO (MoveEntityDto) that I am attempting to map:

public class MoveEntityDto
{       
    public bool IsOrderDetailPageModified { get; set; }  
    public bool IsRoutingPageModified { get; set; }  
    public bool IsServicePageModified { get; set; }  
    public bool IsContentAndContainerPageModified { get; set; }   
    public string FamilyRange { get; set; }  
    public string Office { get; set; }
    public string ActivityType { get; set; }
    public string ActivitySubject { get; set; }
    public string ActivityNote { get; set; }
    public TransfereeEntity PrimaryOriginTransferee { get; set; }
    public TransfereeEntity PrimaryDestinationTransferee { get; set; }
    public CustomerEntity Customer { get; set; }
    public AddressEntity OriginAddress { get; set; }
    public AddressEntity DestinationAddress { get; set; }
    public OrderEntity Order { get; set; }
    public ShipmentEntity Shipment { get; set; }
    public string PortalId { get; set; }
    public string SourceSystemId { get; set; }
    public EnterpriseEnums.SourceSystemName SourceSystemName { get; set; }

    public MoveEntity()
    {
        PrimaryOriginTransferee = new TransfereeEntity();
        PrimaryDestinationTransferee = new TransfereeEntity();
        Customer = new CustomerEntity();
        OriginAddress = new AddressEntity();
        DestinationAddress = new AddressEntity();
        Order = new OrderEntity();
        Shipment = new ShipmentEntity();
    }

    public bool HasShipment()
    {
        if (Shipment.ExternalShipmentId > 0)
        {
            return true;
        }
        return false;
    }
 }

Here is the Business Object (MoveEntity) that I am trying to map from

 public class MoveEntity
{
    public int SourceId { get; set; }
    public int MoveId { get; set; }
    public bool IsOrderDetailPageModified { get; set; }  // TODO: Internal -  Remove from data contract
    public bool IsRoutingPageModified { get; set; }  // TODO: Internal -  Remove from data contract
    public bool IsServicePageModified { get; set; }  // TODO: Internal -  Remove from data contract
    public bool IsContentAndContainerPageModified { get; set; }  // Rmove from data contract
    public string FamilyRange { get; set; } // TODO: Is this being used?
    public string Office { get; set; }
    public string ActivityType { get; set; }
    public string ActivitySubject { get; set; }
    public string ActivityNote { get; set; }
    public ContactEntity PrimaryOriginTransferee { get; set; }
    public ContactEntity PrimaryDestinationTransferee { get; set; }
    public CustomerEntity Customer { get; set; }
    public AddressEntity OriginAddress { get; set; }
    public AddressEntity DestinationAddress { get; set; }
    public OrderEntity Order { get; set; }
    public ShipmentEntity Shipment { get; set; }
    public string CreatedBy { get; set; }
    public DateTime CreatedDate { get; set; }
    public string ModifiedBy { get; set; }
    public DateTime ModifiedDate { get; set; }
    public string SourceSystemId { get; set; }
    public string SourceSystemName { get; set; }
    public string Version { get; set; }
    public string PortalId { get; set; }

    public MoveEntity()
    {
        PrimaryOriginTransferee = new ContactEntity
        {
            ContactTypeId = ContactEntity.ContactType.PrimaryOriginationTransferee
        };

        PrimaryDestinationTransferee = new ContactEntity
        {
            ContactTypeId = ContactEntity.ContactType.PrimaryDestinationTransferee
        };

        OriginAddress = new AddressEntity
        {
            AddressTypeId = AddressEntity.AddressType.Origination
        };

        DestinationAddress = new AddressEntity
        {
            AddressTypeId = AddressEntity.AddressType.Destination
        };

        Order = new OrderEntity();
        Customer = new CustomerEntity();
        Shipment = new ShipmentEntity();
    }

    public bool HasShipment()
    {
        if (Shipment.ShipmentId > 0)
        {
            return true;
        }
        return false;
    }
}

The properties within each class almost match up perfectly by name, but their types are different. Therefore I have attempted to perform a custom mapping using the "MapFrom" expression. However, AutoMapper doesn't seem to be able to allow me to point from one object type to another without complain.

I've also tried mapping property-to-property, with no luck. It looked something like this:

.ForMember(dest => dest.PrimaryOriginTransferee.Email, opt => opt.MapFrom(src => src.PrimaryOriginTransferee.Email))

However, when attempting this, I receive the following exeception:

must resolve to top-level member. Parameter name: lambdaExpression.

I have been finding the documentation available for AutoMapper difficult to follow. Can someone please point me in the right direction as to how to use this utility correctly?

Thanks in advance for any help!

Adam

A: 

You need to add mapping configurations for types of properties where type of target property is different to type of destination property.

    Mapper.CreateMap<ContactEntity, TransfereeEntity>();
Artem Govorov
+1  A: 

I finally ended up getting this to work on my own. The code I ended up using is posted below. Creating the map of objects in the correct order proved to be important. I learned a lot fighting through this thing.

I've organized my mappings into a profile, which I won't get into here, suffice to say that if you can use my example outside of a class inheriting from the AutoMapper Profile class, you'll want to use Mapper.CreateMap instead of just Create Map.

 private void CreateMaps()
    {

        CreateMap<ContactEntity, TransfereeEntityDto>();

        //ContactEntity Mapping
        CreateMap<ContactEntity, TransfereeEntityDto>();

        //CustomerEntity Mapping
        CreateMap<CustomerEntity, CustomerEntityDto>();

        //AddressEntity Mapping
        CreateMap<AddressEntity, AddressEntityDto>();

        //ServiceEntity Mapping
        CreateMap<ServiceEntity, ServiceEntityDto>()
          .ForMember(dto => dto.ServiceTypeCode, opt => opt.MapFrom(source => source.TypeCode))
          .ForMember(dto => dto.ServiceDescriptionCode, opt => opt.MapFrom(source => source.DescriptionCode))
          .ForMember(dest => dest.SourceSystemName, opt => opt.ResolveUsing<SourceSystemNameResolver>().FromMember(entity => entity.SourceSystemName));


        //VehicleEntity Mapping
        CreateMap<VehicleEntity, VehicleEntityDto>()
            .ForMember(dest => dest.SourceSystemName, opt => opt.ResolveUsing<SourceSystemNameResolver>().FromMember(entity => entity.SourceSystemName))
            .ForMember(dto => dto.PortalId, option => option.Ignore());  //TODO: Should PortalID be mapped to anything? It is not in the entity.

        //ContentEntity Mapping
        CreateMap<ContentEntity, ContentEntityDto>()
            .ForMember(dest => dest.SourceSystemName, opt => opt.ResolveUsing<SourceSystemNameResolver>().FromMember(entity => entity.SourceSystemName));

        //OrderForwardingEntity Mapping
        CreateMap<OrderForwardingEntity, OrderForwardingEntityDto>();

        //ContainerEntity Mapping
        CreateMap<ContainerEntity, ContainerEntityDto>()
            .ForMember(dest => dest.SourceSystemName, opt => opt.ResolveUsing<SourceSystemNameResolver>().FromMember(entity => entity.SourceSystemName));

        //ShipmentForwardingEntity Mapping
        CreateMap<ShipmentForwardingEntity, ShipmentForwardingEntityDto>();


        //ShipmentRouting Mapping
        CreateMap<ShipmentRoutingEntity, ShipmentRoutingEntityDto>();

        //ShipmentEntity Mapping
        CreateMap<ShipmentEntity, ShipmentEntityDto>()
            .ForMember(dest => dest.SourceSystemName, opt => opt.ResolveUsing<SourceSystemNameResolver>().FromMember(entity => entity.SourceSystemName))
            .ForMember(dto => dto.Services, option => option.MapFrom(source => source.ServiceEntities));

        //Forwarder mapping
        CreateMap<ContactEntity, ForwarderEntityDto>();
        //TODO: This property doesn't have any properties in the data contract

        //OrderEntity Mapping
        CreateMap<OrderEntity, OrderEntityDto>()
            .ForMember(dest => dest.SourceSystemName,
                       opt => opt.ResolveUsing<SourceSystemNameResolver>().FromMember(entity => entity.SourceSystemName));
            //.ForMember(dto => dto.Forwarder, option => option.MapFrom(entity=>entity.Forwarder)

        //MoveEntityMapping
        CreateMap<MoveEntity, MoveEntityDto>()
            .ForMember(dto => dto.SourceSystemName, opt => opt.ResolveUsing<SourceSystemNameResolver>().FromMember(entity => entity.SourceSystemName));

    }
letsgetsilly