3

Wondering the best way to handle this:

public class SrcClass{
    public int? Value1 {get;set;}
    public int? Value2 {get;set;}
}

public class DestClass{
    public DestClass(int? value1 = 1){
        Value1 = value1
    }

    public int Value1 {get;set;}
    public int Value2 {get;set;
}

Mapper.createMap<SrcClass,DestClass>()
  .ConstructUsing( src => new DestClass(src.Value1));

var destObject = Mapper.Map<DestClass>(new SrcClass());

Now the problem is that destObject.Value1 is null instead of 1 since AutoMapper set the value back to the value of the src object after the constructor is run. The way I see it, I have two bad options:

  1. I can repeat the default value in the createMap method. But this will introduce bugs later if someone adds or changes a default value in the constructor for SrcClass and fails to add that into the mapper configuration
  2. I can add an ignore of Value1 after the ConstructUsing, but this suffers from the same problem as option 1. If someone comes in and adds in a new value with a default parameter, but fails to add it to the mapper, it will be a source of a bug.

What I'd really like to do is somehow tell Automapper to only map properties that weren't passed in during the constructor. Not really sure that it's even possible for the framework to handle something like that. Any other ideas?

4
  • I'm confused - you explicitly tell AutoMapper to use src.Value1 as the parameter but then expect AutoMapper not to use it? Or do you want AutoMapper not to use it if src.Value is null? Commented Aug 13, 2014 at 14:22
  • I somehow want the default value in DestClass to win. Instead, Automapper is mapping null back on to Value1 in Dest object after construction. The problem is that my app already has several DestObjects with fairly complex constructors. I'm trying to use AutoMapper to add in a model layer, and I want to make sure the default parameters win Commented Aug 13, 2014 at 14:27
  • The more I think about this, I don't know that there is a good way to handle this situation. For example, my constructor for DestClass could do some logic inside of it to set a bunch of parameters. I think explicit ignoring is the only way to go Commented Aug 13, 2014 at 14:30
  • Should DestClass.Value1 and DestClass.Value2 be nullable? Commented Aug 13, 2014 at 17:02

1 Answer 1

4

First of all, there's a difference between providing a default value for omitted parameters and setting a value to a default if null is passed in.

In other words,

(new DestClass()).Value1; // Value1 == 1
(new DestClass(null)).Value1; // Value1 == null

The default value only applies when a value is not specified. Otherwise, the value the user of your class supplies will be used.

I only mention this because right now your ConstructUsing statement will initialize Value1 to null if src.Value1 is null. In other words, you have the problem you're seeing even without AutoMapper making a second pass at your destination object.

Now, even if you fix that problem by doing something like this:

public class DestClass
{
    // Now supplying `null` will result in `Value1` equaling "1"
    public DestClass(int? value1)
    {
        Value1 = value1 ?? 1;
    }

    public int? Value1 {get; private set;}
    public int? Value2 {get; set;}
}

AutoMapper will still map to properties with private setters, meaning that you still won't get the desired result.

Fortunately, there's a configuration method called IgnoreAllPropertiesWithAnInaccessibleSetter that you can use to ignore private setters:

Mapper.CreateMap<SrcClass, DestClass>()
    .ConstructUsing(src => new DestClass(src.Value1))
    .IgnoreAllPropertiesWithAnInaccessibleSetter();

This way you'll get the results you want. However, I've heavily modified your class and this solution might not actually apply to you.

You could also just expose a getter and introduce a private readonly backing field to similar effect if that makes sense for your application.

If this doesn't work for you your best option is probably to manually ignore properties you set in the constructor.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.