Automapper Null Substitution in ASP.NET Core Web API

Automapper Null Substitution in ASP.NET Core Web API

In this article, I will discuss Automapper Null Substitution with Simple and Complex Types in ASP.NET Core Web API Application with Examples. Please read our previous article discussing How to Ignore Property Mapping using Automapper in ASP.NET Core Web API Application with Examples.

Automapper Null Substitution in ASP.NET Core Web API

In the context of using AutoMapper in an ASP.NET Core Web API project, null substitution is a feature that allows you to define a default value for a destination property if the corresponding source property is null. That means it allows us to specify a default value that should be used when a source property is null during the mapping process.

This can be useful in scenarios where you want to ensure that the API consumers receive a meaningful default value instead of a null, which could potentially lead to errors or misunderstandings on the client side.

That means the Null Substitution allows us to supply an alternate value for a destination member if the source value is null. We need to use the Automapper NullSubstitute() method to substitute the null value.

Example to Understand Automapper Null Substitution in ASP.NET Core Web API

Let us understand how to use the Automapper NullSubstitute() method in the ASP.NET Core Web API Application with an example. Let us first create the following Employee and EmployeeDTO:

Employee Model:
namespace AutomapperDemo.Models
{
    public class Employee
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Department { get; set; }
        public string? Address { get; set; }
        public string? CreatedBy { get; set; }
        public DateTime? CreatedOn { get; set; }
    }
}
EmployeeDTO:
namespace AutomapperDemo.Models
{
    public class EmployeeDTO
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Department { get; set; }
        public string Address { get; set; }
        public string CreatedBy { get; set; }
        public DateTime CreatedOn { get; set; }
    }
}
Business Requirement:

So, our business requirement is that if the Address Property Value is null in the Source Object, then we need to store N/A in the Address property in the destination object. Similarly, if the CreatedBy property is null, we need to store the System as its value. Finally, if the CreatedOn source property value is null, then we need to return the current date and time.

For these to be implemented, we need to use the NullSubstitute() method. So, create a class file with the names MyMappingProfile.cs and copy and paste the following code.

using AutoMapper;
namespace AutomapperDemo.Models
{
    public class MyMappingProfile : Profile
    {
        public MyMappingProfile()
        {
            // Configure mapping from Employee to EmployeeDTO
            CreateMap<Employee, EmployeeDTO>() 
                //Storing N/A in the destination Address Property, if Source Address is NULL
                .ForMember(dest => dest.Address, act => act.NullSubstitute("N/A"))

                //Storing System in the destination CreatedBy Property, if Source CreatedBy is NULL
                .ForMember(dest => dest.CreatedBy, act => act.NullSubstitute("System"))

                //Storing Current Date and Time in the destination CreatedOn Property, if Source CreatedOn is NULL
                .ForMember(dest => dest.CreatedOn, act => act.NullSubstitute(DateTime.Now));
        }
    }
}
Register AutoMapper in Program.cs

Register AutoMapper and profiles with the dependency injection container in the Program.cs class file. So, please add the following statement within the Program class. This configuration tells AutoMapper to scan the assembly (or assemblies) for classes that inherit from Profile and automatically register them.

builder.Services.AddAutoMapper(typeof(Program).Assembly);

Using Automapper in a Controller

Modify the Employee Controller as follows:

using AutoMapper;
using AutomapperDemo.Models;
using Microsoft.AspNetCore.Mvc;

namespace AutomapperDemo.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class EmployeeController : ControllerBase
    {
        private readonly IMapper _mapper;

        public List<Employee> listEmployees = new List<Employee>()
        {
            new Employee(){Id = 1, Name="Pranaya", Department="IT", Address = "BBSR",
                CreatedBy=null, CreatedOn=null },
            new Employee(){Id = 2, Name="Anurag", Department="HR", Address = null,
                CreatedBy=null, CreatedOn=DateTime.Now },
            new Employee(){Id = 3, Name="Priyanla", Department="HR", Address = null,
                CreatedBy="Admin", CreatedOn=null  }
        };

        public EmployeeController(IMapper mapper)
        {
            _mapper = mapper;
        }

        [HttpGet("{Id}")]
        public ActionResult<EmployeeDTO> GetEmployee(int Id)
        {
            var employee = listEmployees.FirstOrDefault(emp => emp.Id == Id);
            if (employee == null)
            {
                return NotFound("Employee Not Found");
            }

            var employeeDTO = _mapper.Map<EmployeeDTO>(employee);

            return Ok(employeeDTO);
        }
    }
}

Testing the API:

Let us test the above endpoint using Postman and see the results.

API: Fetch the Employee Details

URL: api/Employee/1

Method: GET

Using Postman:

Automapper Null Substitution with Simple and Complex Types in ASP.NET Core Web API

Automapper Null Substitution with Complex Types in ASP.NET Core Web API

Mapping configuration with null substitution for complex types in AutoMapper involves specifying how AutoMapper should handle null values when mapping properties of complex types from a source object to a destination object.

When mapping complex types, you might encounter situations where you want to substitute null values in the source object with a default instance of the complex type in the destination object. This ensures that the destination object’s properties are not left null, which can be useful when you want to avoid null reference exceptions or when the destination object expects specific properties to always be instantiated.

Consider that you have complex types as properties and want to substitute null complex objects with new instances. Let us understand this with an example. Please create the following models and DTOs.

Address
namespace AutomapperDemo.Models
{
    public class Address
    {
        public string? City { get; set; }
        public string? State { get; set; }
        public string? Country { get; set; }
    }
}
Employee
namespace AutomapperDemo.Models
{
    public class Employee
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Department { get; set; }
        public Address? Address { get; set; }
        public string? CreatedBy { get; set; }
        public DateTime? CreatedOn { get; set; }
    }
}
AddressDTO
namespace AutomapperDemo.Models
{
    public class AddressDTO
    {
        public string City { get; set; }
        public string State { get; set; }
        public string Country { get; set; }
    }
}
EmployeeDTO
namespace AutomapperDemo.Models
{
    public class EmployeeDTO
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Department { get; set; }
        public AddressDTO Address { get; set; }
        public string CreatedBy { get; set; }
        public DateTime CreatedOn { get; set; }
    }
}
Mapping Configuration with Null Substitution for Complex Types in ASP.NET Core Web API:

Use the ForMember method to specify the behavior for a particular property. Within this method, you can use NullSubstitute to define a default value when the source property is null. However, NullSubstitute is typically used for simple types. We need to use conditional mapping for complex types to achieve null substitution. This might involve checking if the source property is null and then setting the destination property to a new complex-type instance.

Whatever null substitution we have done in our previous example, the same one will do. But this time, when the complex Address property is null, we need to create a new AddressDTO object and initialize the City, State, and Country values with NA. To do so, please modify the MyMappingProfile class as follows:

using AutoMapper;
namespace AutomapperDemo.Models
{
    public class MyMappingProfile : Profile
    {
        public MyMappingProfile()
        {
            // Configure mapping between Address and AddressDTO
            CreateMap<Address, AddressDTO>();

            // Configure mapping from Employee to EmployeeDTO
            CreateMap<Employee, EmployeeDTO>()
                //Storing N/A in the destination AddressDTO Object Properties, if Source Address is NULL
                .ForMember(dest => dest.Address, opt => opt.MapFrom(src => src.Address ?? new Address() { City = "NA", State = "NA", Country = "NA" }))
                
                //Storing System in the destination CreatedBy Property, if Source CreatedBy is NULL
                .ForMember(dest => dest.CreatedBy, act => act.NullSubstitute("System"))

                //Storing Current Date and Time in the destination CreatedOn Property, if Source CreatedOn is NULL
                .ForMember(dest => dest.CreatedOn, act => act.NullSubstitute(DateTime.Now));
        }
    }
}

In the above code, the ForMember and MapFrom methods are used to check if the source Employee object Address is null and, if so, instantiate a new AddressDTO object for the EmployeeDTO destination object. This approach ensures that complex types are substituted with a default instance rather than left null.

Using Automapper in a Controller

Next, modify the Employee Controller as follows:

using AutoMapper;
using AutomapperDemo.Models;
using Microsoft.AspNetCore.Mvc;

namespace AutomapperDemo.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class EmployeeController : ControllerBase
    {
        private readonly IMapper _mapper;

        public List<Employee> listEmployees = new List<Employee>()
        {
            new Employee(){Id = 1, Name="Pranaya", Department="IT", Address =null,
                CreatedBy=null, CreatedOn=null },
            new Employee(){Id = 2, Name="Anurag", Department="HR", 
                Address = new Address(){City="BBSR", State="Odisha", Country="India"},
                CreatedBy=null, CreatedOn=DateTime.Now },
            new Employee(){Id = 3, Name="Priyanla", Department="HR", Address = null,
                CreatedBy="Admin", CreatedOn=null  }
        };

        public EmployeeController(IMapper mapper)
        {
            _mapper = mapper;
        }

        [HttpGet("{Id}")]
        public ActionResult<EmployeeDTO> GetEmployee(int Id)
        {
            var employee = listEmployees.FirstOrDefault(emp => emp.Id == Id);
            if (employee == null)
            {
                return NotFound("Employee Not Found");
            }

            var employeeDTO = _mapper.Map<EmployeeDTO>(employee);

            return Ok(employeeDTO);
        }
    }
}
Testing the API:

Let us test the above endpoint using Postman and see the results.

API: Fetch the Employee Details

URL: api/Employee/1

Method: GET

Using Postman:

Automapper Null Substitution with Simple and Complex Types in ASP.NET Core Web API

When Should We Use Automapper Null Substitution in ASP.NET Core Web API?
  • To Avoid Null Reference Exceptions: If your business logic or downstream systems cannot handle null values and would throw a null reference exception, null substitution can provide a default value to ensure stability.
  • To Provide Default Values in Responses: When your API needs to return a response with no null values for specific fields, substituting null with a default value can make the response more consistent and user-friendly.
  • For Compatibility with Non-Nullable Types: In scenarios where the destination type is non-nullable but the source value might be null, null substitution can bridge the gap by providing a meaningful default value.
  • To Enforce Business Rules: When specific fields are required by business logic to have a value even when the source is null, null substitution can ensure that these rules are adhered to without additional conditional logic.
  • To Simplify Client-Side Logic: Providing default values for potentially null fields can simplify the logic on the client side, as the clients won’t need to implement their own handling for null values.
  • When Dealing with Legacy Systems: If you’re integrating with older systems that expect specific formats or values and cannot handle null values, null substitution can ensure compatibility.

In the next article, I will discuss How to Store Fixed and Dynamic Values in Destination Properties using AutoMapper in ASP.NET Core Web API Application with Examples. In this article, I explain Automapper Null Substitution with Simple and Complex Types in ASP.NET Core Web API with Examples. I hope you enjoy this article, “Automapper Null Substitution in ASP.NET Core Web API.”

Leave a Reply

Your email address will not be published. Required fields are marked *