How to get AutoMapper to use interface mappings and concrete mappings at the same time

2,024 views
Skip to first unread message

Shaun Smith

unread,
Nov 26, 2015, 4:10:12 AM11/26/15
to AutoMapper-users
I'm trying to get AutoMapper to update all properties on my object from the interface level (DRY approach) but only the interface properties are updating:

I've tried looking into and messing around with the include paths from the AutoMapper documentation but it isn't working the way I would expect and not sure if it's even relevant: https://github.com/AutoMapper/AutoMapper/wiki/Mapping-inheritance

Here are my models:

    public interface IEntity {
       
int Id { get; set; }
   
}


   
public interface IDatedEntity : IEntity {
       
DateTime DateCreated { get; set; }
       
DateTime DateModified { get; set; }
   
}


   
public interface IBMSEntity : IDatedEntity {
       
string Notes { get; set; }
       
bool Active { get; set; }


   
}


   
public class ProjectStatus : IBMSEntity {
       
public string Name { get; set; }


       
public StatusType Type { get; set; }


       
public bool IsDefault { get; set; }


       
#region Interface Properties
       
public int Id { get; set; }
       
public DateTime DateCreated { get; set; }
       
public DateTime DateModified { get; set; }
       
public string Notes { get; set; }
       
public bool Active { get; set; }
       
#endregion
   
}


And here is the the portion of code that accepts the IDatedEntity and maps it across for the purpose of updating using EntityFramework:


    public class BaseDatedEntityUpdate<T> : BaseEntityUpdate<T>, IUpdate<T> where T : class, IDatedEntity {


       
public BaseDatedEntityUpdate(BMSContext context, IValidationDictionary validation) : base(context, validation) { }


       
public override T Update(T entity) {
           
//setting - I'd like to put this on the base mappings also
           
var now = DateTime.Now;
            entity
.DateModified = now;


           
//mapping
           
var original = dbSet.Find(entity.Id);
           
Mapper.Map(entity, original);


           
return original;
       
}
   
}



I have 2 options for my map configuration:

**Option 1:**
I put my ignore properties on the base type:


            //testing base/interface maps
           
Mapper.CreateMap<IDatedEntity, IDatedEntity>()
               
.ForMember(x => x.DateCreated, y=> {
                    y
.UseDestinationValue();
                    y
.Ignore();
               
});


           
Mapper.CreateMap<ProjectStatus, ProjectStatus>();


This will not ignore DateCreated so I have to change
Mapper.Map(entity, original);
 to
Mapper.Map<IDatedEntity, IDatedEntity>(entity, original);
but then I have the problem where my project status properties (such as Name, Type and IsDefault) are not getting mapped across.


**Option 2:** 
I put the mappings on my concrete type:



           
//testing base/interface maps
           
Mapper.CreateMap<IDatedEntity, IDatedEntity>();


           
Mapper.CreateMap<ProjectStatus, ProjectStatus>()
               
.ForMember(x => x.DateCreated, y=> {
                    y
.UseDestinationValue();
                    y
.Ignore();
               
});


This technically works the way I want it to, but it isn't very DRY.

**Question:**

Is there a way to apply the mappings to the base type and keep my code DRY?
I.e. I want to use Option 1, whereby all ProjectStatus (and any other derivatives of IDatedEntity) properties are mapped across - without having to create detailed concrete type mappings?

I have also posted this as a question on StackOverflow at the following URL: 
http://stackoverflow.com/questions/33892416/how-to-get-automapper-to-use-interface-mappings-and-concrete-mappings

Jimmy Bogard

unread,
Nov 30, 2015, 10:11:15 AM11/30/15
to automapper-users
Hm, have you tried putting the IgnoreMap attribute on the interface?

--
You received this message because you are subscribed to the Google Groups "AutoMapper-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to automapper-use...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Shaun Smith

unread,
Nov 30, 2015, 11:09:10 AM11/30/15
to AutoMapper-users
I don't want any of the maps to be ignored, I need both the interface maps and the derivative maps to be applied under the context of interface.

Shaun Smith

unread,
Nov 30, 2015, 11:12:04 AM11/30/15
to AutoMapper-users
Sorry, I misunderstood.

Option 1 shows the ignore map on the interface, this has the problem whereby the properties on ProjectStatus do not map across.

Jimmy Bogard

unread,
Nov 30, 2015, 11:21:30 AM11/30/15
to automapper-users
Sorry, I meant the attribute, putting that on the properties on the interface. Then you don't have to do any sort of base interface CreateMap call.

On Nov 30, 2015, at 10:12 AM, Shaun Smith <shauns...@googlemail.com> wrote:

Sorry, I misunderstood.

Option 1 shows the ignore map on the interface, this has the problem whereby the properties on ProjectStatus do not map across.

Shaun Smith

unread,
Nov 30, 2015, 1:04:12 PM11/30/15
to automapp...@googlegroups.com

I’m sorry I don’t fully understand,

 

Are you able to show me an example or pseudo code example?

--
You received this message because you are subscribed to a topic in the Google Groups "AutoMapper-users" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/automapper-users/TZE4JlhYVas/unsubscribe.
To unsubscribe from this group and all its topics, send an email to automapper-use...@googlegroups.com.

Jimmy Bogard

unread,
Nov 30, 2015, 1:44:45 PM11/30/15
to automapper-users
    public interface IDatedEntity : IEntity {
        [IgnoreMap]
        
DateTime DateCreated { get; set; }
        [IgnoreMap]
        
DateTime DateModified { get; set; }

Shaun Smith

unread,
Nov 30, 2015, 3:19:26 PM11/30/15
to automapp...@googlegroups.com

Sorry Jimmy, that was ridiculously simpler than I was expecting.

 

As you can see from my code I have to use the destination value and I also wish to use other values such as mapping DateTime.Now to DateModified. Is there a way I can achieve this behaviour?

Jimmy Bogard

unread,
Nov 30, 2015, 3:24:59 PM11/30/15
to automapper-users
UseDestinationValue is only really used for collection types, not primitives. For collections/complex types it means "use the destination instance, but map all child properties/items". In your case, you don't need UseDestinationValue, just Ignore.

For the part where you're filling in DateTime.Now, that's a bit tougher. You can try IncludeBase on derived maps, and in the base map you include that behavior:

CreateMap<IDatedEntity, IDatedEntity>()
    .ForMember(d => d.DateModified, opt => opt.MapFrom(src => DateTime.Now));
CreateMap<DerivedEntity, DerivedEntity>()
   .IncludeBase<IDatedEntity, IDatedEntity>();

Or you can also try conventions, new in 4.1 https://github.com/AutoMapper/AutoMapper/wiki/Conventions

Shaun Smith

unread,
Nov 30, 2015, 3:59:42 PM11/30/15
to automapp...@googlegroups.com

Ok I think I have enough info to give this another attempt.

 

Thank you so much for all your help Jimmy, I really appreciate it.

 

p.s. If you drop this info on the StackOverflow I’ve linked at the bottom of the original article, I’ll hand you my 100 bounty points also J

Reply all
Reply to author
Forward
0 new messages