Advertisement
Поиск  
Always will be ready notify the world about expectations as easy as possible: job change page
Apr 16, 2022

Enjoy using Mapster in .NET 6

Автор:
Mohsen Saniee
Источник:
Просмотров:
5136

Today, developers are paying more attention to mapping libraries than ever before, because modern architecture forces them to map object to object across layers. For example, in the past I prepared a repository for clean architecture in github. So it’s a template for visual studio, you can see it and get more information about modern architecture layers.

However, based on what I know most developers do the mapping by writing many lines of code, like this:

public IActionResult Post(ProductDto dto)
{
    Product product = new()
    {
        Name = dto.Name,
        Brand = dto.Brand,
        ...
        ...
        ...
        Size = dto.Size,
        Weight = dto.Weight,
        ...
        ...
        ColorId = dto.ColorId
    };
 
    ...
    return Ok();
}

I don’t know why some programmers choose this way for mapping! but I know, I don’t like this because it’s really boring and time consuming and I prefer use Mapping libraries such as AutoMapper and Mapster.
Since they are simple and fast. In addition to “Mapster” is fun and hashigh performance! you can see its repository in github.

So let’s get started!

1. Create a new ASP.NET Core Web API Project

2. Add Entity Models

Create a folder called “Entities” and Add Product.cs, Color.cs classes. It should be like below:

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Brand { get; set; }
    
    public string WeightWithUnit { get; set; }
    public string Size { get; set; }


    public int ColorId { get; set; }
    public Color Color { get; set; }
}

public class Color
{
    public int Id { get; set; }
    public string Name { get; set; }

    public List<Product> Products { get; set; }
}

3. Install Mapster Nuget package

Add Mapster Nuget package:

PM> Install-Package Mapster

First see the basic usage:

Mapping to a new object:

var destObject = sourceObject.Adapt<Destination>();

Mapping to an existing object:

sourceObject.Adapt(destObject);

Queryable Extensions:

var destinations = context.Sources.ProjectToType<Destination>().ToList();

This was basic usage of Mapster, but in the following we show a different usage with customized configuration.

4. Add “BaseDto” Abstract class

Create a folder called “Dtos” and Add BaseDto.cs, ProductDto.cs classes. Actually our “Dto” classes implement the BaseDto class.

public abstract class BaseDto<TDto, TEntity> : IRegister
    where TDto : class, new()
    where TEntity : class, new()
{

    public TEntity ToEntity()
    {
        return this.Adapt<TEntity>();
    }

    public TEntity ToEntity(TEntity entity)
    {
        return (this as TDto).Adapt(entity);
    }

    public static TDto FromEntity(TEntity entity)
    {
        return entity.Adapt<TDto>();
    }

    private TypeAdapterConfig Config { get; set; }

    public virtual void AddCustomMappings() { }

    protected TypeAdapterSetter<TDto, TEntity> SetCustomMappings() => Config.ForType<TDto, TEntity>();

    protected TypeAdapterSetter<TEntity, TDto> SetCustomMappingsInverse() => Config.ForType<TEntity, TDto>();

    public void Register(TypeAdapterConfig config)
    {
        Config = config;
        AddCustomMappings();
    }
}
  • IRegister is an interface in Mapster library so it implemented to allow mappings to be found when scanning assemblies and place mappings is in the Register method.
  • Then we added some auxiliary methods for easier mapping:

    ToEntity()
    ToEntity(TEntity entity)
    FromEntity(TEntity entity)
  • Also we added three methods for customizing configuration of mappings:

    AddCustomMappings()
    SetCustomMappings()
    SetCustomMappingsInverse()

In the next steps, we will show how to use these methods…

5. Add ProductDto

Create the ProductDto class and implement the BaseDto class. Then override the AddCustomMappings() method and add customize mappings:

public class ProductDto : BaseDto<ProductDto,Product>
{
    public string Name { get; set; }
    public string Brand { get; set; }
    public string FullName { get; set; }

    public int Weight { get; set; }
    public string WeightUnit { get; set; }
    public string Size { get; set; }

    public int ColorId { get; set; }
    public string ColorName { get; set; }

    public override void AddCustomMappings()
    {
        SetCustomMappings()
            .Map(dest => dest.WeightWithUnit, src => src.Weight.ToString() + " " + src.WeightUnit);

        SetCustomMappingsInverse()
            .Map(dest => dest.FullName, src => $"{src.Name} ({src.Brand})")
            .Map(dest => dest.ColorName, src => src.Color.Name)
            .Map(dest => dest.Weight, src => Convert.ToInt32(src.WeightWithUnit.Split(" ",StringSplitOptions.None)[0]))
            .Map(dest => dest.WeightUnit, src => src.WeightWithUnit.Split(" ", StringSplitOptions.None)[1]);
    }
}

6. Add Mapster Configuration In Program Class

First, Create a folder called “Webframework” and add MapsterConfiguration.cs. So, add the assembly to scan classes that implemente IRegister.cs interface.

public static class MapsterConfiguration
{
    public static void AddMapster(this IServiceCollection services)
    {
        var typeAdapterConfig = TypeAdapterConfig.GlobalSettings;
        Assembly applicationAssembly = typeof(BaseDto<,>).Assembly;
        typeAdapterConfig.Scan(applicationAssembly);
    }
}

Then Call AddMapster() extensions method in program.cs class:

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddMapster(); // <========<<

builder.Services.AddControllers();

...
...
...

7. Add api Controller and test mappings

Create a new api controller class by name “ProductsController” to test the mappings. Below are the implementation details:

[ApiController]
[Route("[controller]")]
public class ProductsController : ControllerBase
{
    private readonly ApplicationDbContext _context;

    public ProductsController(ApplicationDbContext context)
    {
        _context = context;
    }

    [HttpPost]
    public async Task<IActionResult> Post(ProductDto dto)
    {
        Product product = dto.ToEntity();

        _context.Add(product);

        await _context.SaveChangesAsync();

        return Ok(product);
    }

    [HttpGet]
    public async Task<IActionResult> Get(int productId)
    {
        ProductDto productDto = await _context
            .Products
            .Where(p => p.Id == productId)
            .ProjectToType<ProductDto>()
            .FirstOrDefaultAsync();

        return Ok(productDto);
    }

    [HttpGet]
    [Route("[controller]/[action]")]
    public async Task<IActionResult> GetAll()
    {
        var result = await _context
            .Products
            .ProjectToType<ProductDto>()
            .ToListAsync();

        return Ok(result);
    }
}

* It’s worth mentioning, also we added AppilcaitonDbContext.cs, EF core and Swagger in this project, So you can see the full source of this project in this github repository.

Eventually you can run this application and tests controller actions by Swagger and you see that everything is right.

8. Final point : how to create static method for mapping configuration

For example we add “HasFavoriteColor” propery to ProductDto.cs then add “GetMapsterConfig” static method:

public class ProductDto : BaseDto<ProductDto,Product>
{
    public string Name { get; set; }
    public string Brand { get; set; }
    public string FullName { get; set; }

    public int Weight { get; set; }
    public string WeightUnit { get; set; }
    public string Size { get; set; }

    public int ColorId { get; set; }
    public string ColorName { get; set; }

    public bool HasFavoriteColor { get; set; }

    public static TypeAdapterConfig GetMapsterConfig(int userFavoriteColorId)
    {
        return new TypeAdapterConfig()
            .NewConfig<Product, ProductDto>()
                .Map(dest => dest.FullName, src => $"{src.Name} ({src.Brand})")
                .Map(dest => dest.ColorName, src => src.Color.Name)
                .Map(dest => dest.HasFavoriteColor, src => src.ColorId == userFavoriteColorId) // <======<<
                .Config;
    }
    
    ...
    ...
}

Now we can use mapping in controller this way:

[HttpGet]
[Route("[controller]/[action]")]
public async Task<IActionResult> Products(int userFavoriteColorId)
{
    var result = await _context
        .Products
        .ProjectToType<ProductDto>(ProductDto.GetMapsterConfig(userFavoriteColorId)) //<======<<
        .ToListAsync();

    return Ok(result);
}

Summary

In this post, we saw one example of usage Mapster. It’s a very fun and simple and help keep your code structure clean.

You can get the source code from this git repository.

I hope you found this post helpful and easy to follow. Please let me know if you have any corrections and/or questions in the comments below.

Happy coding.

Похожее
Apr 24, 2022
Author: Lucas Diogo
A practice approach to creating stable software.Don’t make your software unstable like a house of cards, solidify it.There are five principles to follow when we write code with object-oriented programming to make it more readable and maintainable if we don’t...
Dec 21, 2023
Author: Jeremy Wells
Introduction and prerequisitesThis post is part of an ongoing series where we build a “walking skeleton” application using ASP.NET Core and Angular as well as other technologies for deployment and testing. By now, our application is a minimally functional web...
May 27, 2023
Author: Gustavo Restani
In today’s fast-paced world of software development, it is crucial to be familiar with design patterns that can help you create robust, efficient, and maintainable code. One of the most widely used programming frameworks for enterprise applications is the .NET...
Mar 25
Author: Dayanand Thombare
Creating background services in .NET Core is a powerful way to perform long-running, background tasks that are independent of user interaction. These tasks can range from data processing, sending batch emails, to file I/O operations — all critical for today’s...
Написать сообщение
Почта
Имя
*Сообщение


© 1999–2024 WebDynamics
1980–... Sergey Drozdov
Area of interests: .NET Framework | .NET Core | C# | ASP.NET | Windows Forms | WPF | HTML5 | CSS3 | jQuery | AJAX | Angular | React | MS SQL Server | Transact-SQL | ADO.NET | Entity Framework | IIS | OOP | OOA | OOD | WCF | WPF | MSMQ | MVC | MVP | MVVM | Design Patterns | Enterprise Architecture | Scrum | Kanban