Automapper Condition, PreCondition, and PostCondition in ASP.NET Core Web API

Automapper Condition, PreCondition, and PostCondition in ASP.NET Core Web API

In this article, I will discuss the Differences Between Automapper Condition, PreCondition, and PostCondition in ASP.NET Core Web API Applications with Examples. Please read our previous article discussing AutoMapper Conditional Mapping in ASP.NET Core Web API with Examples.

Automapper Condition, PreCondition, and PostCondition in ASP.NET Core Web API

AutoMapper is a popular library used for mapping between objects, mainly from data transfer objects (DTOs) to entity models and vice versa. AutoMapper provides several powerful features to customize the mapping process, including Condition, PreCondition, and AfterMap (i.e., Post Condition) methods.

Understanding the differences between Condition, PreCondition, and AfterMap (i.e., Post Condition) methods is important for effectively manipulating data mapping processes. Each of these methods serves a different purpose in the lifecycle of a mapping operation. Let’s explore these methods in detail, focusing on their purpose, usage, and some examples to illustrate their application.

Automapper Condition

Condition is used to specify a predicate that determines whether a particular property mapping should occur. The condition is evaluated for each mapping operation, and the property is only mapped if the condition returns true.

This is useful when you want to perform the mapping based on the values of the source or destination objects. For example, you might only want to copy a source property to the destination if the source property meets specific criteria (e.g., is not null or is within a specific range).

In AutoMapper, you can specify a condition using the Condition method inside your mapping configuration. This condition applies globally to the entire mapping operation for the specified member.

Syntax:
CreateMap<Source, Destination>()
.ForMember(dest => dest.SomeProperty, opt => opt.Condition(src => src.SomeOtherProperty > 0));

This configuration means SomeProperty is only mapped from Source to Destination if Source.SomeOtherProperty > 0.

When to use:
  • Use Condition when you need the decision to map a property based on the values within the source or destination objects.
  • You only want to map a property if a specific condition is met, such as only copying the value if the source property is not null or meets a specific criterion.
  • You want to prevent overwriting existing values in the destination object with null or undesired values from the source object.

Automapper PreCondition

PreCondition is similar to Condition but is evaluated before any mapping occurs for the specific property. It allows you to specify a predicate that must return true for the property mapping to proceed. If the PreCondition evaluates to false, the property will not be mapped, and the mapping operation will not affect it.

Use PreCondition when checking certain conditions before attempting to map a property. This is particularly useful if the mapping of the property should only occur under specific circumstances that can be determined before any mapping logic is applied. For example, you might use PreCondition to check if a source property is initialized or if a user has the necessary permissions. In AutoMapper, you can specify a pre-condition inside your mapping configuration using the PreCondition method.

Syntax:
CreateMap<Source, Destination>()
.ForMember(dest => dest.SomeProperty, opt => opt.PreCondition(src => src.SomeOtherProperty > 0));

Here, SomeProperty is mapped only if SomeOtherProperty > 0, similar to Condition, but it specifically applies to the member mapping operation where it’s defined.

When to use:
  • Before attempting to map a property, you must perform checks or validations on the source object itself (or other external factors).
  • You want to avoid executing potentially costly operations to resolve the source value for the mapping if certain conditions are not met.

Automapper PostCondition

PostCondition allows you to specify an action that occurs after the property has been mapped. It does not influence whether the mapping occurs, but it can modify the destination property or perform additional actions after the mapping is complete.

Use PostCondition when performing additional operations on the destination property after it has been mapped from the source. This is useful for scenarios where the destination property needs further modification that cannot be easily achieved through the standard mapping configuration or where the modification depends on the outcome of the mapping.

Syntax:
CreateMap<Source, Destination>()
        .AfterMap((src, dest) =>
        {
            // Post-condition logic here
            if (dest.SomeProperty == "SomeValue")
            {
                // Adjust the destination object as necessary
                dest.AnotherProperty = "AdjustedValue";
            }
        });

In this example, after the source object is mapped to the destination object, the AfterMap method checks if SomeProperty of the destination object has a specific value. If the condition is met, it modifies the AnotherProperty of the destination object accordingly.

When to use:
  • You want to perform additional operations or set additional properties on the destination object based on the newly mapped value.
  • You need to apply transformations to the mapped property value that were not possible or practical to achieve during the mapping configuration.
Example to Demonstrate Automapper Condition, PreCondition, and AfterMap methods in ASP.NET Core Web API

Suppose we are working on a user management system where we need to map UserDto objects to User entity objects before saving them to a database. We have certain conditions:

  • Only map the Email field if it’s not already set in the destination object.
  • Ensure the source UserName is not null or empty before the mapping.
  • After mapping, we want to log the result.
Model and DTO
User.cs
namespace AutomapperDemo.Models
{
    public class User
    {
        public int Id { get; set; }
        public string UserName { get; set; }
        public string Email { get; set; }
    }
}
UserDTO.cs
namespace AutomapperDemo.Models
{
    public class UserDTO
    {
        public string UserName { get; set; }
        public string Email { get; set; }
    }
}
Mapping Profile Configuration

Create a mapping profile that defines the mapping between UserDTO and User, incorporating Condition, PreCondition, and AfterMap. 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()
        {
            CreateMap<UserDTO, User>()
             //Condition
             .ForMember(dest => dest.Email, opt => opt.Condition(src => string.IsNullOrEmpty(src.Email)))
             
             //Pre-Condition
             .ForMember(dest => dest.UserName, opt => opt.PreCondition(src => !string.IsNullOrEmpty(src.UserName)))

             //Post-Condition
             .AfterMap((src, dest) => Console.WriteLine($"Mapped {src.UserName} to {dest.UserName}"));
        }
    }
}
Explanation
  • Condition: The Condition method checks if the destination’s Email is null or empty. If true, it will map the Email from the source. This is useful for not overwriting existing values.
  • PreCondition: The PreCondition method ensures the source UserName is not null or empty before performing the mapping. This prevents mapping invalid data.
  • AfterMap: The AfterMap action logs the mapping result, demonstrating how you can execute any post-mapping logic.
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

Finally, in your Web API controller, you can use Automapper to map UserDTO to User when handling a POST request, for example. So, modify the User Controller as follows:

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

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

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

        [HttpPost]
        public IActionResult CreateUser([FromBody] UserDTO userDTO)
        {
            var user = _mapper.Map<User>(userDTO);
            user.Id = 1;
            // Save user to database...
            return Ok(user);
        }
    }
}
Testing the API:

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

API: Create a new User

URL: api/User

Method: POST

Request Body:

{
  "userName": "Test",
  "email": "Test@Example.com"
}
Using Postman:

Differences Between Automapper Condition PreCondition and PostCondition in ASP.NET Core Web API

Summary:
  • Use Condition when the decision to map a property depends on the current state or values of the source or destination object.
  • Use PreCondition to perform checks or validations before attempting to map a property, ensuring that only properties meeting specific criteria are mapped.
  • Use PostCondition to apply additional modifications or actions to the destination property after it has been mapped.

In the next article, I will discuss How to Ignore Property Mapping using Automapper in ASP.NET Core Web API Application with Examples. In this article, I explain Automapper Condition, Pre-Condition, and Post-Condition Mapping in ASP.NET Core Web API with Examples. I hope you enjoy this article, “Between Automapper Condition, Pre-Condition and Post-Condition Mapping in ASP.NET Core Web API.

Leave a Reply

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