Updated on
To stop AutoMapper overwriting a destination value with a null from the source, add a condition to the mapping: .ForAllMembers(opts => opts.Condition((src, dest, srcMember) => srcMember != null)). Members whose source value is null are then skipped, and whatever the destination already held survives.
Collections need one more step. By default a null source collection maps to an empty collection instead of being skipped, so the condition alone does not protect it: opts.AllowNull() in the same block is what makes the rule apply to lists as well.
Setup AutoMapper in ASP.NET Core
First, let’s set up a simple ASP.NET Core Console application. We’ll need to install the AutoMapper NuGet package, plus the logging abstractions package its configuration type now requires:
PM> Install-Package AutoMapper -Version 16.2.0 PM> Install-Package Microsoft.Extensions.Logging.Abstractions
Prior to delving into the specifics of AutoMapper mapping, it’s essential to set up our project to utilize AutoMapper:
var config = new MapperConfiguration(cfg =>
{
cfg.AddProfile<DefaultMappingProfile>();
}, NullLoggerFactory.Instance);
IMapper mapper = config.CreateMapper();
Here, as a first step, we define an AutoMapper configuration instance by utilizing the MapperConfiguration class. It takes an action delegate in which we instruct AutoMapper to apply mapping rules defined in the DefaultMappingProfile class. We will talk about it shortly.
It also takes an ILoggerFactory, and there is no overload without one. On AutoMapper 16.2.0, the single-argument call that older samples show fails to compile with CS1729. A console application that logs nothing passes NullLoggerFactory.Instance from Microsoft.Extensions.Logging.Abstractions, which is honest about doing nothing; an application that already builds a factory passes that one instead.
After completing the configuration, we can proceed with the creation process by calling the CreateMapper() method. It generates a mapper instance IMapper which we use for mapping purposes.
For a more in-depth look at using AutoMapper, be sure to check out our article Getting Started with AutoMapper in ASP.NET Core.
Create a Mapping Profile
AutoMapper, by convention, matches source and destination properties by their names and types. But in some situations, it requires special attention to handle mapping. AutoMapper supports custom configuration through mapping profiles.
Let’s create a Mapping Profile class:
public class DefaultMappingProfile : Profile
{
public DefaultMappingProfile()
{
CreateMap<StudentItemDto, StudentEntity>()
.ForMember(dest => dest.Age, opt => opt.MapFrom(src => src.AgeInfo));
CreateMap<StudentEntity, StudentItemDto>()
.ForMember(dest => dest.AgeInfo, opt => opt.MapFrom(src => src.Age));
}
}
Here, we generate the DefaultMappingProfile class to customize the mapping rules, inheriting from the AutoMapper Profile class.
In the constructor, we define our custom mapping rules. The CreateMap<StudentItemDto, StudentEntity>() method instructs AutoMapper to map the StudentItemDto source type to the StudentEntity destination type. This method returns a mapping object of the type IMappingExpression. Utilizing the features of IMappingExpression enables us to tailor our mapping rules according to our specific requirements. For example, we call the ForMember() method to create a rule that maps the AgeInfo property from the source object to the Age property of the destination object.
In the same way, we establish a secondary mapping for our StudentEntity to correspond with the destination type StudentItemDto, forming a reverse mapping rule. Once a profile is registered, we can also inspect it; see our article on reading the configured mappings at runtime.
What Happens When the Source Is Null in AutoMapper?
It maps the null through. A null source member produces a null destination member, including when the destination already held a value, which is the case that surprises people.
That matters most on partial updates. Mapping a DTO with three populated fields onto a fully loaded entity does not merge the three; it copies all of them, and the untouched ones arrive as null and overwrite what was there.
Collections behave differently, and the difference is not a bug. A null source collection maps to an empty collection rather than null, so an entity’s populated list becomes an empty list rather than keeping its contents.
The console output below shows both behaviours in a single mapping call: Department and University arrive as null, wiping out the values the destination already held, while Grades arrives as an empty list rather than as null. Two different defaults, one Map().
Let’s see how AutoMapper maps null values with the default configuration:
private static StudentEntity MapToStudentEntity<TProfile>(StudentItemDto source)
where TProfile : Profile, new()
{
var config = new MapperConfiguration(cfg =>
{
cfg.AddProfile<TProfile>();
}, NullLoggerFactory.Instance);
IMapper mapper = config.CreateMapper();
var destination = GetSampleEntity();
destination = mapper.Map(source, destination);
Console.WriteLine("Destination : {0}",
JsonSerializer.Serialize(destination, new JsonSerializerOptions { WriteIndented = true }));
if (destination.Department == null)
throw new ArgumentNullException("Department", "Department is null");
return destination;
}
Here, we create a generic MapToStudentEntity() function that takes a source parameter of type StudentItemDto and maps it to StudentEntity. To handle the mapping process, we generate a mapper object based on the generic TProfile. We serialize the result with JsonSerializer from System.Text.Json, which ships in the box on .NET 10, purely so we can read the destination object in the console.
After that, we initialize a destination variable by calling the GetSampleEntity() method. It simply generates and returns a mock StudentEntity for testing purposes:
public static StudentEntity GetSampleEntity()
{
var entity = new StudentEntity()
{
Id = 101,
Name = "Sample",
Age = 29,
Department = "Computer Engineering",
Grades = new List<decimal>() { 2.36M, 2.72M, 3.06M },
University = new University() { Name = "Bosphorus University" }
};
return entity;
}
Then, we call the Map() method of the mapper object and it populates the destination object members with the source object members.
Now, let’s see how to call this generic mapping method:
public static StudentEntity UpdateStudent(StudentItemDto source)
{
return MapToStudentEntity<DefaultMappingProfile>(source);
}
Here, we create the UpdateStudent() method. For the sake of simplicity, it calls the MapToStudentEntity() method with the DefaultMappingProfile.
Let’s examine the mapping result displayed in the console output:
Source : {
"Id": 0,
"Name": "Test",
"AgeInfo": 29,
"Grades": null,
"Department": null,
"University": null,
"IsGraduated": false
}
Using DefaultMappingProfile:
Destination : {
"Id": 0,
"Name": "Test",
"Age": 29,
"Grades": [],
"Department": null,
"University": null
}
Mapped with exception : Department is null (Parameter 'Department')
Here, we see that AutoMapper maps all null values from the source object to the destination object. Only, the Grades property that has the type List mapped to an empty list instead of a null value. Additionally, our application throws an exception because AutoMapper attempts to populate the Department member with a null value. At this point, if our application requires no null or empty values for certain properties, then it may end up crashing.
We can solve this problem by checking null properties manually and initializing them with a default value, but this is not practical and it shadows the power of AutoMapper. AutoMapper provides a graceful solution for such situations.
So, let’s handle null values with AutoMapper.
How Do We Ignore Null Values With AutoMapper?
By attaching a condition to the mapping that skips a member whenever its source value is null. AutoMapper’s own documentation frames it as the ability to add conditions to properties that must be met before that property will be mapped.
The condition takes the source, the destination, and the member’s source value, and returns whether to map: opts.Condition((src, dest, srcMember) => srcMember != null). When it returns false, AutoMapper leaves the destination member alone rather than writing null into it.
Applying it once per member is possible but rarely what we want. ForAllMembers() attaches the same condition to every member of a map in one call, which is the right granularity for a “patch this entity” mapping where any field may be absent.
Collections need AllowNull() in the same block. Without it, a null source collection never reaches the condition (it has already been turned into an empty collection), so the list gets wiped despite the rule.
| Setting | Scope | Default | What it does |
|---|---|---|---|
Condition((src, dest, srcMember) => srcMember != null) | Per member, or all members via ForAllMembers | Not applied | Skips the member when the source value is null, leaving the destination untouched |
AllowNull() | Per member | Off | Lets a null source value through to the member, so Condition can see it; required for collections |
AllowNullCollections | Whole configuration | false | When false, a null source collection becomes an empty destination collection instead of null |
AllowNullDestinationValues | Whole configuration | true | When true, a null source member produces a null destination member rather than the type's default |
NullSubstitute(value) | Per member | Not applied | Replaces a null source value with a fixed value instead of skipping it |
Ignore() | Per member | Not applied | Never maps the member at all, null or not |
AutoMapper’s documentation, read on 9 August 2026, describes the last of these as follows: “null substitution allows you to supply an alternate value for a destination member if the source value is null anywhere along the member chain”. (Null Substitution)
As we mentioned earlier, we configure mapping rules via mapping profile classes. Thus, it is time to create a new mapping profile that contains mapping rules to ignore null values for all source members:
public class IgnoreNullMappingProfile : Profile
{
public IgnoreNullMappingProfile()
{
CreateMap<StudentItemDto, StudentEntity>()
.ForMember(dest => dest.Age, opt => opt.MapFrom(src => src.AgeInfo))
.ForAllMembers(opts =>
{
opts.Condition((src, dest, srcMember) => srcMember != null);
});
CreateMap<StudentEntity, StudentItemDto>()
.ForMember(dest => dest.AgeInfo, opt => opt.MapFrom(src => src.Age))
.ForAllMembers(opts =>
{
opts.Condition((src, dest, srcMember) => srcMember != null);
});
}
}
Here, we create the IgnoreNullMappingProfile mapping class. Its purpose is to ignore null values from the source object. To accomplish this, we invoke the ForAllMembers() method, which takes an action delegate as an argument. Inside the delegate method, we configure AutoMapper to ignore null values for all source members by calling opts.Condition((src, dest, srcMember) => srcMember != null).
This is the all-members rule; if we only want one property left out of a map, our article on ignoring a single property instead of all nulls covers the narrower case, and projecting queries with AutoMapper covers the same configuration applied to ProjectTo().
Let’s see IgnoreNullMappingProfile in action:
public static StudentEntity UpdateStudentIgnoreNullValues(StudentItemDto source)
{
return MapToStudentEntity<IgnoreNullMappingProfile>(source);
}
Here, we create the UpdateStudentIgnoreNullValues() method. It calls the MapToStudentEntity() method with the IgnoreNullMappingProfile.
Now, we can re-check the mapping result again:
Source : {
"Id": 0,
"Name": "Test",
"AgeInfo": 29,
"Grades": null,
"Department": null,
"University": null,
"IsGraduated": false
}
Using IgnoreNullMappingProfile:
Destination : {
"Id": 0,
"Name": "Test",
"Age": 29,
"Grades": [],
"Department": "Computer Engineering",
"University": {
"Name": "Bosphorus University",
"Location": null
}
}
Mapped without exception
With the new configuration, AutoMapper ignores null values and does not map them to the destination object. Besides that, our application does not throw an exception. But still, we have a problem. The Grades property is null in the source object and mapped as an empty list to the destination object. If our application requires us to ignore null values for all source members, then we should find a way to handle null value mapping for lists or collections.
So, let’s empower the mapping configuration to ignore the null values for list or collection members.
Ignore Null Values for Lists and Collections
AutoMapper provides many functionalities to customize mapping rules. For List or Collection type members, we have seen that with default mapping rules, AutoMapper maps null values to an empty list or collection.
AutoMapper has a configuration method named AllowNull(). With this configuration, AutoMapper allows null collections from the source object and tries to map to the destination object. By using both the AllowNull() and the Condition((...) => srcMember != null) rules, we configure AutoMapper to ignore null values for list and collection types.
With this in mind, let’s see how we use the AllowNull() method in mapping configuration:
public class IgnoreNullMappingProfile : Profile
{
public IgnoreNullMappingProfile()
{
CreateMap<StudentItemDto, StudentEntity>()
.ForMember(dest => dest.Age, opt => opt.MapFrom(src => src.AgeInfo))
.ForAllMembers(opts =>
{
opts.AllowNull();
opts.Condition((src, dest, srcMember) => srcMember != null);
});
CreateMap<StudentEntity, StudentItemDto>()
.ForMember(dest => dest.AgeInfo, opt => opt.MapFrom(src => src.Age))
.ForAllMembers(opts =>
{
opts.AllowNull();
opts.Condition((src, dest, srcMember) => srcMember != null);
});
}
}
With the latest configuration modifications in place, let’s examine the mapping result once more:
Source : {
"Id": 0,
"Name": "Test",
"AgeInfo": 29,
"Grades": null,
"Department": null,
"University": null,
"IsGraduated": false
}
Using IgnoreNullMappingProfile:
Destination : {
"Id": 0,
"Name": "Test",
"Age": 29,
"Grades": [
2.36,
2.72,
3.06
],
"Department": "Computer Engineering",
"University": {
"Name": "Bosphorus University",
"Location": null
}
}
Mapped without exception
Here, AutoMapper ignores the Grades property in the source object and keeps the destination object’s original value.
Is AutoMapper Still Free to Use?
For most teams, yes. AutoMapper moved to a paid licence at version 15.0.0, with a free Community tier, and that tier is not a single revenue test: an organisation has to meet all four conditions at once.
The four are a ceiling of $5 million on annual gross revenue (or budget, for a non-profit), a ceiling of $10 million on outside capital raised, not being a government or quasi-government entity, and not being a university using the library for its own institutional software. Missing any one of them means the commercial licence applies.
There is still a free path on current versions: the library is dual-licensed, so the reciprocal open-source licence is available to anyone willing to release their own source under its terms.
Nothing in this article changes because of it. Null-handling configuration is the same code before and after the licence change, and a team that would rather not audit itself against four conditions has alternatives to move to.
Version 14.0.0 and everything before it stays MIT permanently, so pinning is a fourth option alongside the Community tier, the reciprocal licence, and the paid tiers. For the full comparison, see our article on AutoMapper’s licensing and the alternatives, which benchmarks the closest of them, Mapster, the MIT-licensed alternative.
Conclusion
In this article, we have explored AutoMapper mapping profiles. We configured AutoMapper via customized mapping rules through mapping profiles. More importantly, we delved into null value mapping problems and what consequences may occur when we don’t care about null value mapping. Subsequently, we discussed how AutoMapper provides a graceful solution to ignore null values from all source members. Finally, we examined how we address null value mapping for list or collection types by utilizing AutoMapper’s AllowNull feature.
Tested with .NET 10.0.10 and AutoMapper 16.2.0.
