Skip to content

Nullable source property to value type #2918

Description

@jhenderson2099

Documentation Request

Please document how the (default) value for a destination property that doesn't exist in the source object is determined.

This is being logged at the request of @jbogard . Please refer to https://groups.google.com/forum/#!topic/automapper-users/UX-liQGboMY

(Basically, "This is how it works if you works if the mapping is/isn't explicit/implicit and what you get as a default)

Suggestion is document it as part of Inline Mapping
[https://automapper.readthedocs.io/en/latest/Inline-Mapping.html]
(https://automapper.readthedocs.io/en/latest/Inline-Mapping.html)

Example Source/destination types

    public class Source
    {
        public double? Balance { get; set; }

    }


    public class Destination
    {
        public double Balance { get; set; }   // Not nullable!
    }

Mapping configuration

CreateMap<Source, Destination>(); // Implicit property-to-property mapping

or

// Explicit property-to-property mapping
CreateMap<Source, Destination>()
    .ForMember(dest => dest.Balance, opt => opt.MapFrom(src => src.Balance))

Version: x.y.z

7.0.0

Questions to be answered

  • If Source.Balance == null, then what will be the value of Destination.Balance?
  • Does AutoMapper convert the null to zero, or does the instance of Destination simply get initialized to 0 and no mapping occurs?
  • What would happen in this scenario if the two property names did not match and there was no explicit mapping? What would be the value of Destination.Balance?

I think it works like this: if you have a nullable source property and the value of the source property is nulland a non-nullable destination property (based upon my testing):

  • If you have a mapping (explicit or implicit), then you get the default value for that type (e.g., default(int))
  • If you don't supply a mapping, and there is no implicit automapping that will work, and you don't supply a value in the Destination class's constructor, then you get the default value for that type
  • If you don't supply a mapping, and there is no implicit automapping that will work, and you do supply a value in the Destination class's constructor, then you get the value that you specified in the constructor

Steps to reproduce

N/A

Activity

  1. lbargaoanu commented on Dec 16, 2018

    @lbargaoanu
    Contributor

    But all of this is quite unsurprising. So what's really the point of documenting it? Surely shorter documentation is better. It's easy to find out what happens at runtime using the execution plan. And that's also an unnecessary constraint for us in the future. We might need to change some details. It doesn't seem like a good idea to me.

  2. jbogard commented on Dec 17, 2018

    @jbogard
    Contributor

    The explicit/implicit mapping confuses things - there's not separate behaviors for auto-mapped members and explicitly mapped members. Auto-mapped is just "we've found a source member already".

    I do like the idea of documenting our execution plan a bit more - it's still not that easy to use the debug view.

    There are additional nuances to nullable behavior, as you'll get slightly different behavior if the parent object is null, but we can roughly document it as:

    dest.NullableInt = src?.Int;
    dest.Int = src?.NullableInt ?? default(int);

    Expressions don't support the null propagation operator but this is the general idea. We don't "decide" to map 0, we have to supply a default value so we use the default value of the type.

  3. lbargaoanu commented on Dec 17, 2018

    @lbargaoanu
    Contributor

    But if the destination is int, what default value it can possible have. It has to be zero. It seems obvious to me.

  4. jbogard commented on Dec 17, 2018

    @jbogard
    Contributor

    @lbargaoanu I think what might be able to be clarified is what happens when the value is missing - do we still set the value, or does it get skipped?

    Something like this I'd see in a general "mapping behavior" section or similar. Something that shows simplified/pseudo-code of the expressions that get built up.

  5. changed the title [-]Docs: Document how the (default) value for a destination property is determined when the property is nullable in the source object[/-] [+]Document how the (default) value for a destination property is determined when the property is nullable in the source object[/+] on Dec 17, 2018
  6. lbargaoanu commented on Dec 17, 2018

    @lbargaoanu
    Contributor

    Whenever there is a match, an assignment happens. I guess that might not be obvious.

  7. jhenderson2099 commented on Dec 17, 2018

    @jhenderson2099
    Author

    I guess the bigger question would be "Does a match occur if one property is nullable type and the other is not a nullable type?"

  8. jbogard commented on Dec 17, 2018

    @jbogard
    Contributor
  9. changed the title [-]Document how the (default) value for a destination property is determined when the property is nullable in the source object[/-] [+]Nullable source property to value type[/+] on Dec 18, 2018
  10. lock commented on May 5, 2019

    @lock

    This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

  11. locked as resolved and limited conversation to collaborators on May 5, 2019
  12. jbogard commented on Sep 4, 2026

    @jbogard
    Contributor

    Reopening for the docs half of this. Conditional-mapping.md in #4655 now documents the ?? default(T) assignment rule for a null Nullable<T> source member, why a name match always produces an assignment, and the PreCondition pattern for skipping it.

  13. reopened this on Sep 4, 2026
  14. added this to the 16.3.0 milestone on Sep 4, 2026
  15. jbogard commented on Sep 9, 2026

    @jbogard
    Contributor

    Fixed by #4655, merged as 6e8697b — shipping in 16.3.

    Condition now receives the value resolved from the source object before conversion to the destination member type when the condition's member parameter can hold it, so a ForAllMembers condition sees a null Nullable<T> instead of default(T). Conditional-mapping.md gained a "Nullable source members" section covering that, the PreCondition route for typed ForMember conditions, UseDestinationValue for the keep-the-destination case, and the ?? default(T) rule when there is no condition.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions