6

How to configure AutoMapper mapping when I want to use behaviour from UseDestinationValue method, but only when destination property is NOT null.

Something like that:

Mapper.CreateMap<Item, ItemViewModel>()
    .ForMember(x => x.Details, _ => _.UseDestinationValue(dontUseWhenNullDestination: true))

EDIT

class ItemDetails {
    public string Info { get; set; }
    public string ImportantData { get; set; } // only in Domain, not in ViewModel
}

class Item {
    public ItemDetails Details { get; set; }
}

class ItemDetailsViewModel {
    public string Info { get; set; }
}

class ItemViewModel {
    public ItemDetailsViewModel Details { get; set; }
}

Now example of usage. I have a ItemViewModel class and I want to map it to the Item class.

Mapping configuration:

    Mapper.CreateMap<Item, ItemViewModel>()
        .ForMember(x => x.Details, _ => _.UseDestinationValue())
  1. First case - destination property Item.Details property is NOT NULL. Now I want AutoMapper to use this destination instance of Details property, because it's not null.

    And the logic looks like this:

    var item = new Item { 
        Details = new Details {
            Info = "Old text",
            ImportantData = "Data"
        }
    };
    
    var itemViewModel = new ItemViewModel { 
        Details = new DetailsViewModel {
            Info = "New text"
        }
    };       
    
    Mapper.Map(itemViewModel, item);
    

    AutoMapper, because of presence of UseDestinationValue, will leave the item.Details instance and set only item.Details.Info property.

  2. Second case - destination property Item.Details property is NULL. Now I want AutoMapper not to use this null instance, but create new one. The question is how to configure the mapping to take into account this case?

    The logic looks like this:

    var item = new Item { 
        Details = null
    };
    
    var itemViewModel = new ItemViewModel { 
        Details = new DetailsViewModel {
            Info = "New text"
        }
    };
    
    Mapper.Map(itemViewModel, item);
    

    PROBLEM

    Here I have a problem, because after mapping, the item.Details property will be null (because of usage of UseDestinationValue which is null in this case).

REASON

NHibernate, after getting the entity from the database, puts it into a proxy. So the Details property of a loaded object is not of a type: ItemDetails, but ItemDetailsNHibernateProxy - so I have to use this type, when I want to save this existing object to the database later. But if this property is null, then I can't use a null destination value, so Automapper should create a new instance.

Thanks, Chris

cryss
  • 4,130
  • 1
  • 29
  • 34
  • So do you want to do something other than UseDestinationValue if it IS null? – PatrickSteele Sep 07 '12 at 16:52
  • Yes, I want to do something else when destination property IS NULL. – cryss Sep 11 '12 at 13:02
  • Could you provide some working sample code showing you how you would want things mapped vs. what Auotmapper currently does. I'm just not getting what you're asking. – PatrickSteele Sep 11 '12 at 16:03
  • I want automapper to use the *destination instance* of a complex property Details (not to create new) and then set the inner properties of a ItemDetails class. But this should only work, when the Details property in not null (because I want to use this instance), so when the Details property is null, then don't use this null value, but create a new instance. – cryss Sep 12 '12 at 11:12
  • See updated answer. Couldn't verify it since you don't have a working test case, but I think it'll do what you're looking for. – PatrickSteele Sep 12 '12 at 13:12
  • @ChrisJ, did you solve this problem? – Brett Feb 15 '13 at 16:04
  • 1
    Unfortunately no. Now what I'm doing is checking if the destination property value is null in BeforeMap action. – cryss Feb 15 '13 at 18:09
  • If you're still interested you may be able to adapt [this solution](http://stackoverflow.com/a/16073984/1505426) to fit your needs. – Mightymuke May 03 '13 at 03:32

3 Answers3

2

Had this same problem, but with EF. Cryss' comment about using BeforeMap pointed me in the right direction.

I ended up with code similar to:

In the Configure() method:

Mapper.CreateMap<ItemViewModel, Item>()
           .AfterMap((s, d) => { MapDetailsAction(s, d); })
           .ForMember(dest => dest.Details, opt => opt.UseDestinationValue());

Then the Action:

Action<ItemViewModel, Item> MapDetailsAction = (source, destination) =>
        {
            if (destination.Details == null)
            {
                destination.Details = new Details();
                destination.Details =
                    Mapper.Map<ItemViewModel, Item>(
                    source.Details, destination.Details);
            }
        };
Obed
  • 400
  • 1
  • 9
0

I think the NullSubstitute option would work for you. See: http://weblogs.asp.net/psteele/archive/2011/03/18/automapper-handling-null-members.aspx

EDIT

Looks like you might need to add a little conditional logic to your mapping for Details (and skip the UseDestinationValue option):

.ForMember(d => d.Details, 
    o => o.MapFrom(s => s.Details == null ? new ItemDetails() : Mapper.Map<ItemDetailsViewModel, ItemDetails>(s.Details))
PatrickSteele
  • 14,489
  • 2
  • 51
  • 54
0

I had this same problem dealing with NHibernate entities and I found quite simple solution to it.

You should initialize the Details property in the ItemViewModel constructor. This way the destination value is not null ever. Of course this does not work in more complex cases (like abstract classes).

Juho Rutila
  • 2,316
  • 1
  • 25
  • 40