IT:AD:AutoMapper

Summary

Although MS's architecture document on DDD recommended AutoMapper, I was…underwhelmed by the end of the project. Too verbose, too little documentation for the hard edge cases, too time consuming.

The general consensus (even by its creator) that it's deemed to be ok for Domain Entity up to DTO, but not so hot going back.

Anyway, These are the notes developed while working with it – but next time, I'll try a different mapping strategy.

Create A Map

SS:Intro to AutoMapper 1.1 SS:AutoMapper as Operator

    //Examples of creating custom maps between values:
    Mapper.CreateMap<Source3, Destination3>();
    Mapper.CreateMap<NestedSource3, NestedDestination3>()
      .ForMember(dest => dest.Value, opt => opt.MapFrom(src => src.Val));

For ones you have to ignore, use:

      .ForMember(dest => dest.Value, opt => opt.Ignore());

Example:

    public class TestInvoiceB
    {
        public int Id { get; set; }
        public DateTime DateCreated { get; set; }
        public ICollection<TestLineItemB> LineItems { get; private set; }
        
        public TestInvoiceB()
        {
            LineItems = new Collection<TestLineItemB>();
        }
    }

    public class TestLineItemB
    {
        public int Id { get; set; }
        public int TestInvoiceBId {get;set;}
        public TestInvoiceB Invoice {get;set;}
        public string Description { get; set; }
        public int Count { get; set; }
        public decimal ItemAmount { get; set; }
        public decimal Amount { get { return Count*ItemAmount; } set {}}
    }

    public class TestInvoiceBDTO 
    {
        public int Id { get; private set; }
        public DateTime DateCreated { get; set; }
        public ICollection<TestLineItemBDTO> SomeLineItems { get { return _SomeLineItems; } private set { _SomeLineItems = value;}}
        private ICollection<TestLineItemBDTO> _SomeLineItems = new Collection<TestLineItemBDTO>();
    }

    public class TestLineItemBDTO
    {
        public int Id { get; private set; }
        public int TestInvoiceBId { get; private set; }
        public TestInvoiceB Invoice { get; set; }
        public string XDescription { get; set; }
        public int Count { get; set; }
        public decimal ItemBmount { get; set; }
        public decimal Amount { get;set;}
    }


XLib Implementation

The above gets mapped as follows:

    public class TestInvoiceBMapper : AutoMapperTypeMapperBase<TestInvoiceB, TestInvoiceBDTO>
    {
        protected override void BeforeInternalMap(ref TestInvoiceB source)
        {
            base.CreateMap<TestInvoiceB, TestInvoiceBDTO>()
                .ForMember(d => d.SomeLineItems, opt => opt.MapFrom(s => s.LineItems))
                ;

            base.CreateMap<TestLineItemB, TestLineItemBDTO>()
                .ForMember(d => d.TestInvoiceBId,
                           opt => opt.MapFrom(s => s.TestInvoiceBId))
                .ForMember(d => d.XDescription,
                           opt => opt.MapFrom(s => s.Description)).
                ForMember(d => d.ItemBmount, opt => opt.MapFrom(s => s.ItemAmount));

            //Mapper.CreateMap<ICollection<TestLineItemA>, ICollection<TestLineItemADTO>>();
        }

        protected override TestInvoiceBDTO InternalMap(TestInvoiceB source)
        {
            return base.Map<TestInvoiceB, TestInvoiceBDTO>(source);
        }
    }

Checking Configuration

After registering your mappings, you can check to see if all Target properties have been filled in with:

Mapper.AssertConfigurationIsValid();

Conditional Mapping

Working with EF CodeFirst

EF has built in change tracking, as well as integrity checks. You can't just ask Automapper to do what it normally does, and recreate an object. You have to use the objects that are there…

In other words, you'll have to state that you will be using whatever subject is there (rather than create a new one) by using the opt => opt.UseDestinationObject

See AutoMapperModifiers Example:

BeforeMap and AfterMap

There's some hooks to do some pre-work.

http://stackoverflow.com/questions/2806664/automapper-using-beforemap-and-aftermap

configure.CreateMap<IFrom, ITo>()
    .BeforeMap((x,y) => x.PrepareForMapping())
    .etc
    .AfterMap((x,y) => x.PostMapping())
    

Future

Reference