Automapper Pre-Condition Mapping in ASP.NET Core Web API

AutoMapper Pre-Condition Mapping in ASP.NET Core Web API

In this article, I will discuss How to Implement AutoMapper Pre-Condition Mapping in ASP.NET Core Web API Application with Examples. Please read our previous article discussing AutoMapper Reverse Mapping in ASP.NET Core Web API with Examples.

Pre-Condition and Post-Condition Mapping in AutoMapper

In AutoMapper, a mapping configuration defines how objects of one type are mapped to objects of another type. Pre-Condition and Post-Condition Mapping in AutoMapper are concepts used to define conditions that must be met before a mapping operation starts (pre-condition) and to apply custom actions after a mapping operation is completed (post-condition).

AutoMapper Pre-Condition Mapping using ASP.NET Core Web API

Pre-Condition Mapping in AutoMapper is a feature that allows us to specify a condition that must be met before a mapping operation is performed on a property. This can be useful when we want to avoid overwriting values under certain circumstances or when we want to ensure that certain criteria are met before a mapping operation takes place.

In AutoMapper, a mapping configuration defines how objects of one type are mapped to objects of another type. Pre-conditions in this context allow you to add logic that determines whether a particular property should be mapped. A pre-condition is applied to individual members of the source object and evaluates whether the destination member should be mapped or not based on the defined condition.

To use a Pre-Condition Mapping in AutoMapper, we need to use the PreCondition method in our mapping configuration. This method takes a predicate that operates on the source object. The mapping for the property will only occur if the predicate returns true. Here is the syntax to use Pre Condition mapping using Automapper:

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

In this example, SomeProperty on the destination object will only be mapped if SomeOtherProperty on the source object is greater than 0.

Example to Understand AutoMapper Pre-Condition Mapping in ASP.NET Core Web API:

Let us understand AutoMapper Pre-Condition Mapping in ASP.NET Core Web API with an example. For this example, assume we have the following Product entity and a corresponding ProductDTO entity.

Product Entity

Create a class file named Product.cs, and then copy and paste the following code:

namespace AutomapperDemo.Models
{
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public decimal Price { get; set; }
        public bool InStock { get; set; }
        public string Category { get; set; }
    }
}
ProductDTO Entity

Create a class file named ProductDTO.cs, and then copy and paste the following code:

namespace AutomapperDemo.Models
{
    public class ProductDTO
    {
        public string Name { get; set; }
        public decimal Price { get; set; }
        public bool InStock { get; set; }
        public string Category { get; set; }
    }
}
Configure AutoMapper

Create a mapping profile that defines the mapping configuration. Here, you need to specify a pre-condition for mapping. So, create a class file named MyMappingProfile.cs and then copy and paste the following code. Here, the pre-condition ensures that the Price property is only mapped when the InStock property is true.

using AutoMapper;
namespace AutomapperDemo.Models
{
    public class MyMappingProfile : Profile
    {
        public MyMappingProfile()
        {
            //Configure the Mappings Between Product and ProductDTO
            CreateMap<Product, ProductDTO>()
            .ForMember(dest => dest.Price, opt => {
                opt.PreCondition(src => src.InStock); // Only map Price if InStock is true
                opt.MapFrom(src => src.Price);
            });
        }
    }
}
In the above code:
  • CreateMap<Product, ProductDTO>(): This line initiates the mapping configuration from a source type Product to a destination type ProductDTO. It tells AutoMapper that you want to map properties from Product objects to ProductDTO objects.
  • .ForMember(dest => dest.Price, opt => {…}): This method is used to configure how individual members (properties) of the destination type (ProductDTO in this case) are mapped from the source type (Product). The first argument (dest => dest.Price) specifies which member of the destination type you’re configuring.
  • opt.PreCondition(src => src.InStock);: This line specifies a pre-condition for the mapping of this member. The PreCondition method takes a lambda expression that evaluates to a boolean. The mapping of this member will only occur if this lambda expression returns true. In this case, Price will only be mapped from Product to ProductDTO if InStock is true for the Product object.
  • opt.MapFrom(src => src.Price);: This tells AutoMapper how to map the value from the source object to the destination object. It specifies that the Price property of the ProductDTO should be filled with the value from the Price property of the Product object.
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);

Use AutoMapper in the Controller

Now, you can inject IMapper into your controllers and use it to map your entities to DTOs. So, create an API Controller named Product Controller and then copy and paste the following code:

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

namespace AutomapperDemo.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ProductController : ControllerBase
    {
        //Create a variable to holder mapper instance
        private readonly IMapper _mapper;

        //Framework will inject the instance using Constructor
        public ProductController(IMapper mapper)
        {
            //Initialize the variable with the injected mapper instance
            _mapper = mapper;
        }

        private List<Product> listProducts = new List<Product>()
        {
            new Product { Id = 1001, Name="Laptop", Category="Electronics", Price = 1000, InStock = true},
            new Product { Id = 1002, Name="T-Shirt", Category="Merchandise", Price = 2000, InStock = false}
        };

        [HttpGet]
        public ActionResult<ProductDTO> GetProdcuts()
        {
            // AutoMapper uses the pre-condition to decide if Price should be mapped
            //var productDTO = _mapper.Map<List<Product>, List<ProductDTO>>(listProducts);
            var productDTO = _mapper.Map<List<ProductDTO>>(listProducts);
            return Ok(productDTO);
        }
    }
}
Testing the API:

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

API: Return All Products

URL: api/Product

Method: GET

Using Postman:

AutoMapper Pre-Condition Mapping in ASP.NET Core Web API

Creating a Custom Method and using it within the Automapper PreCondition Method:

Creating a custom method and using it within the PreCondition method in AutoMapper allows for more complex and reusable logic during the mapping process. This can be useful when you have specific conditions that determine whether or not a mapping should occur, and these conditions are too complex to inline within the mapping configuration itself.

Define the Custom Method

First, you need to define a custom method that encapsulates your complex logic. This method should return a Boolean value indicating whether the mapping should proceed. It will accept parameters based on your requirements, often including the source object or specific values from the source object.

For example, suppose you have a source object Product with properties InStock and Category, and you only want to map the Price to the destination object ProductDTO if the product is in stock and belongs to a specific category:

So, create a class file named CustomMappingLogic.cs and then copy and paste the following code. Here, we have one static method which is going to execute the custom logic and then it will return a Boolean value.

namespace AutomapperDemo.Models
{
    public class CustomMappingLogic
    {
        //Passing the Source Object as a Parameter
        public static bool ShouldMapPrice(Product source)
        {
            //return true if the Product is in Stock and it belongs to the Electronics Category
            return source.InStock && source.Category == "Electronics";
        }

        //Passing the InStock and Category Properties of the Source Object as Parameters
        public static bool ShouldMapPrice(bool InStock, string Category)
        {
            //return true if the Product is in Stock and it belongs to the Electronics Category
            return InStock && Category == "Electronics";
        }
    }
}
Use the Custom Method in the Automapper PreCondition Method

Next, we need to use the custom static method in our AutoMapper mapping configuration, specifically within a PreCondition method. This tells AutoMapper to use our custom logic to determine if a specific member should be mapped. So, modify your MyMappingProfile.cs class file as follows to include the custom method in the PreCondition:

using AutoMapper;
namespace AutomapperDemo.Models
{
    public class MyMappingProfile : Profile
    {
        public MyMappingProfile()
        {
            //Configure the Mappings Between Product and ProductDTO
            CreateMap<Product, ProductDTO>()
            .ForMember(dest => dest.Price, opt => {
                //Calling the ShouldMapPrice Static by passing the source object
                opt.PreCondition(src => CustomMappingLogic.ShouldMapPrice(src));
                opt.MapFrom(src => src.Price);
            });

            //CreateMap<Product, ProductDTO>()
            //.ForMember(dest => dest.Price, opt => {
            //    //Calling the ShouldMapPrice Static by passing the source object's InStock and Category values
            //    opt.PreCondition(src => CustomMappingLogic.ShouldMapPrice(src.InStock, src.Category));
            //    opt.MapFrom(src => src.Price);
            //});
        }
    }
}

Here, opt.PreCondition(src => CustomMappingLogic.ShouldMapPrice(src)); specifies the precondition for mapping the Price property from Product to ProductDTO. AutoMapper passes the source object (Product in this case) to your custom method, ShouldMapPrice. The mapping for Price will only occur if this method returns true.

Modifying the Product Controller:

Next, modify the Product Controller as follows to test all the scenarios:

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

namespace AutomapperDemo.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ProductController : ControllerBase
    {
        //Create a variable to holder mapper instance
        private readonly IMapper _mapper;

        //Framework will inject the instance using Constructor
        public ProductController(IMapper mapper)
        {
            //Initialize the variable with the injected mapper instance
            _mapper = mapper;
        }

        private List<Product> listProducts = new List<Product>()
        {
            new Product { Id = 1001, Name="Laptop", Category="Electronics", Price = 1000, InStock = true},
            new Product { Id = 1002, Name="T-Shirt", Category="Merchandise", Price = 2000, InStock = true},
            new Product { Id = 1003, Name="Desktop", Category="Electronics", Price = 1000, InStock = false},
            new Product { Id = 1003, Name="Jacket", Category="Merchandise", Price = 2000, InStock = false}
        };

        [HttpGet]
        public ActionResult<ProductDTO> GetProdcuts()
        {
            // AutoMapper uses the pre-condition to decide if Price should be mapped
            //var productDTO = _mapper.Map<List<Product>, List<ProductDTO>>(listProducts);
            var productDTO = _mapper.Map<List<ProductDTO>>(listProducts);
            return Ok(productDTO);
        }
    }
}

Now, run the application and access the above endpoint, and you should get the following response:

Creating a Custom Method and using it within the Automapper PreCondition Method

When Should We Use AutoMapper Pre-Condition Mapping in ASP.NET Core Web API?

A pre-condition mapping in AutoMapper is a feature that allows you to specify a condition that must be met before a particular mapping operation is performed. This can be useful in a variety of scenarios to ensure that your data mappings are not only accurate but also relevant to the current state of your application or data.

  • Conditional Property Mapping: Use pre-condition mappings when you want to map a property only if a certain condition is true. For example, you might only want to map the value of a property if it is not null or if it meets specific validation criteria.
  • Security and Privacy Considerations: In scenarios where certain information should only be mapped if the current user has the appropriate permissions, pre-conditions can enforce these rules. For instance, sensitive user details should only be mapped for users with administrative access.
  • Performance Optimization: If some mappings are expensive to compute or require external resources (like database calls), you can use pre-conditions to avoid these mappings unless they are absolutely necessary. This can help in optimizing the performance of your application.
  • Dynamic Data Handling: When dealing with dynamic data where the presence or relevance of data can change based on external factors (e.g., configuration settings, feature flags), pre-conditions allow you to flexibly map only the relevant parts of an object.
  • Complex Business Logic: In applications with complex business logic where the decision to map certain data depends on multiple factors or states of the application, pre-conditions provide a way to encapsulate this logic within your mapping configuration.
  • State-Dependent Mapping: Use pre-condition mappings when the decision to map certain properties depends on the state of the source, destination, or external factors. This is useful in applications where the state or context plays a crucial role in how data should be mapped.

In the next article, I will discuss Implementing AutoMapper Post-Condition Mapping in ASP.NET Core Web API with Examples. In this article, I explain How to Implement AutoMapper Pre-Condition Mapping in ASP.NET Core Web API with Examples. I hope you enjoy this article, “AutoMapper Pre-Condition Mapping in ASP.NET Core Web API.”

Leave a Reply

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