preface

During the actual project development, the various ORM components we use can easily bind the data we get into the corresponding List

collection, because the data we want to display on the page may be very different from the database entity class. Therefore, the more common approach here is to create some viewmodel classes corresponding to the page data display, through secondary processing of the obtained data, so as to meet the needs of the actual page display.

Therefore, how to more convenient to realize the entity mapping between database persistent objects and view objects, avoid us to manually implement this process again and again in the code, can reduce the workload of development, and AutoMapper is to help us convenient realization of the entity conversion process. So, in this chapter, we will learn how to map entities in ASP.NET Core projects using AutoMapper.

Of course, if you’re used to using database entities for everything from view presentations to persistence to databases, this article probably won’t help you at all.

Storage address: github.com/Lanesra712/…

Step by Step

AutoMapper is an OOM(Object-object-Mapping) component. From its name, it can be seen that this series of components is mainly to help us achieve the transformation between entities, so as to avoid the way of manually writing code for transformation. Before using OOM component, if we need to achieve similar data in different client display different fields, we can only manually, one by one attribute assignment way to achieve data transfer between various client data types, and OOM component can be very convenient to help us achieve this demand.

A few concepts

Above we have mentioned the database persistent object and view object these two concepts, in fact, in addition to these two concepts of objects, there is also a concept of data transfer object, here we will briefly explain the concept of these three objects.

Persistent Object: As the name implies, this Object is used to persist our data to the database. In general, the fields in the Persistent Object will be consistent with the corresponding table in the database.

Here, if you use DDD to guide the design of the system architecture, in fact, the final implementation of our code is actually a Domain Object. The most significant difference between the Domain Object and the database persistent Object is that the Domain Object contains various events in the current business Domain. The database persistent object only contains the data field information of the corresponding table in the database.

View Object (View Object) : The View Object VO is oriented to the front-end user page, and generally contains all data field information contained in a page/component presented to the user.

Data Transfer Object (DTO) : DTO is generally used for Data Transfer between the front-end display layer and the background service layer, and completes the Data Transfer between the database persistent Object and the view Object in the form of a medium.

Here is a simple diagram to explain the specific use of these three objects. In this example project, I omitted the data transfer object and directly converted the database persistent object into the page display view object.

Two, component loading

First we need to load the AutoMapper into the project via Nuget. Since this sample project contains only one MVC project, there is no extra layer, so here we need to add both DLLS used to the MVC project.

Install-Package AutoMapper
Install-Package AutoMapper.Extensions.Microsoft.DependencyInjection
Copy the code

Here I added AutoMapper. Extensions. Microsoft. DependencyInjection this assembly, by the name of this assembly will see, The main purpose of this assembly is to use AutoMapper in projects through dependency injection.

In the.net Fx era, we might use AutoMapper to map entities using static methods in Mapper, as shown in the following code. In.NET Core, however, we prefer dependency injection for entity mapping.

// Build entity mapping rules
Mapper.Initialize(cfg => cfg.CreateMap<OrderModel, OrderDto>());

// Entity mapping
var order = new OrderModel{};
OrderDto dto = Mapper.Map<OrderModel,OrderDto>(order);
Copy the code

Three, use cases

Because the original example project I wanted to use was the previous project of Ingos-Server, since I am currently learning DDD knowledge and have a framework designed based on DDD idea in Microsoft’s eShopOnContainers project, DDD adjustment for my Ingos-Server project is, in fact, a copy of a copy, so the whole project has been changed in a chaotic way, which is not suitable for a sample project. Therefore, a relatively pure ASP.NET Core MVC project has been created as a demonstration project for this article.

Since the sample project is only intended to demonstrate how to use AutoMapper in an ASP.NET Core project, there is no layering. The entire sample page runs as follows: The List Action in the PostController calls the GetPostLists method in the PostAppService class to fetch all the article data, and entity mapping is performed in this method. Convert the PO objects we obtained from the PostDomain into the VO objects shown on the page. The role of each folder in the project is shown in the figure below.

The example project here is to demonstrate the entity mapping from PO to VO once we get the data we need from the database. The class definitions for PostModel (PO) and PostViewModel (VO) are shown below.

public class PostModel
{
    public Guid Id { get; set; }
    public long SerialNo { get; set; }
    public string Title { get; set; }
    public string Author { get; set; }
    public string Image { get; set; }
    public short CategoryCode { get; set; }
    public bool IsDraft { get; set; }
    public string Content { get; set; }
    public DateTime ReleaseDate { get; set; }
    public virtual IList<CommentModel> Comments { get; set; }}public class PostViewModel
{
    public Guid Id { get; set; }
    public long SerialNo { get; set; }
    public string Title { get; set; }
    public string Author { get; set; }
    public short CategoryCode { get; set; }
    public string Category => CategoryCode == 1001 ? ".NET" : "Gossip";
    public string ReleaseDate { get; set; }
    public short CommentCounts { get; set; }
    public virtual int Count { get; set; }}Copy the code

First, we need to create a configuration class for entity mapping, which needs to inherit from the Profile class of AutoMapper. In the no-argument constructor, we can use the CreateMap method to create the mapping between two entities.

public class PostProfile : Profile
{
    /// <summary>
    /// ctor
    /// </summary>
    public PostProfile()
    {
        // Configure the mapping rule
        //CreateMap<PostModel, PostViewModel>(); }}Copy the code

The generic CreateMap method allows us to map entities from PostModel(PO) to PostViewModel(VO). Of course, since AutoMapper automatically matches field names and types by default, if you’re converting two classes with different field names, you’ll need to write the conversion rules manually.

As in the example code that requires entity mapping, the CommentCounts field in PostViewModel is assigned according to the number of data in the CommentModel collection in PostModel, so here we need to change the conversion rules for this field.

In AutoMapper, we can take the mapping rules a step further with the ForMember method. Here we need to note that the value of the PostViewModel CommentCounts field is obtained by summing the Comments information in the PostModel, resulting in the conversion code shown below.

public class PostProfile : Profile
{
    /// <summary>
    /// ctor
    /// </summary>
    public PostProfile()
    {
        // Configure the mapping rule
        //CreateMap<PostModel, PostViewModel>() .ForMember(destination => destination.CommentCounts, source => source.MapFrom(i => i.Comments.Count())); }}Copy the code

The ForMember method not only converts fields with different names, it can also write rules to convert field types. For example, the ReleaseDate field in PO is actually DateTime. We need to write rules to correspond this field to the ReleaseDate field of String in VO. The final implementation code is shown as follows.

public class PostProfile : Profile
{
    /// <summary>
    /// ctor
    /// </summary>
    public PostProfile()
    {
        // Config mapping rules
        //
        CreateMap<PostModel, PostViewModel>()
            .ForMember(destination => destination.CommentCounts, source => source.MapFrom(i => i.Comments.Count()))
            .ForMember(destination => destination.ReleaseDate, source => source.ConvertUsing(new DateTimeConverter()));
    }
}

public class DateTimeConverter : IValueConverter<DateTime, string>
{
    public string Convert(DateTime source, ResolutionContext context)
        => source.ToString("yyyy-MM-dd HH:mm:ss");
}
Copy the code

Many people here might be used to putting all entity mapping rules in the same Profile. Since the project is a single architecture, there are different modules in the whole project, so I create a Profile for each module. The actual usage in the Ingos-Server project is shown in the figure below.

When we have created the corresponding mapping rule, we need to inject our matching rule into the IServiceCollection because we use dependency injection. As you can see from the Github readme description of the previously loaded assembly, we need to inject the configured Profile class through the AddAutoMapper extension method.

Because we may have multiple custom profiles in our actual project, we definitely need to inject these custom rules into the IServiceCollection. So I created an AddAutoMapperProfiles method based on the AddAutoMapper method to inject our entity mapping rules.

As you can see from AutoMapper’s description, all custom Profile classes are derived from AutoMapper’s base Profile class. By obtaining all the class files inherited from the Profile class in the assembly for batch injection into IServiceCollection, the specific implementation code is shown as follows.

/// <summary>
///Automapper Mapping rule Configures the extension method
/// </summary>
public static class AutoMapperExtension
{
    public static IServiceCollection AddAutoMapperProfiles(this IServiceCollection services)
    {
        // Get assembly information containing configuration rules from appsettings.json
        string assemblies = ConfigurationManager.GetConfig("Assembly:Mapper");

        if (!string.IsNullOrEmpty(assemblies))
        {
            var profiles = new List<Type>();

            // Get inherited Profile type information
            var parentType = typeof(Profile);

            foreach (var item in assemblies.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries))
            {
                // Get all classes that inherit from profiles
                //
                vartypes = Assembly.Load(item).GetTypes() .Where(i => i.BaseType ! =null && i.BaseType.Name == parentType.Name);

                if(types.Count() ! =0 || types.Any())
                    profiles.AddRange(types);
            }

            // Add a mapping rule
            if(profiles.Count() ! =0 || profiles.Any())
                services.AddAutoMapper(profiles.ToArray());
        }

        returnservices; }}Copy the code

Because I’m will need to load the assembly information into in the configuration file, so here we just need to include the rules of the Profile assembly is added to the corresponding configuration items below, as if contains multiple assemblies, you need to use | separation.

{
  "Assembly": {
    "Mapper": "aspnetcore-automapper-tutorial"}}Copy the code

When we inject all the entity mapping rules into the IServiceCollection, we can use these entity mapping rules in our code. In the same way as other interfaces through dependency injection, we only need to inject IMapper interface where used, and then complete the mapping between entities through the Map method, using the code as follows.

public class PostAppService : IPostAppService
{
    #region Initialize

    /// <summary>
    ///
    /// </summary>
    private readonly IPostDomain _post;

    /// <summary>
    ///
    /// </summary>
    private readonly IMapper _mapper;

    /// <summary>
    /// ctor
    /// </summary>
    /// <param name="post"></param>
    /// <param name="mapper"></param>
    public PostAppService(IPostDomain post, IMapper mapper)
    {
        _post = post ?? throw new ArgumentNullException(nameof(post));
        _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
    }

    #endregion Initialize

    /// <summary>
    ///Get all the article information
    /// </summary>
    /// <returns></returns>
    public IList<PostViewModel> GetPostLists()
    {
        var datas = _post.GetPostLists();
        return_mapper.Map<IList<PostModel>, IList<PostViewModel>>(datas); }}Copy the code

At this point, we have implemented using AutoMapper in ASP.NET Core projects. The result is shown below.

conclusion

This article demonstrates how to use AutoMapper to map entities in ASP.NET Core projects. This component has been used only in.NET Fx projects, but not in.NET Core projects. So this time during the National Day holidays to try how to in the.net Core used in the project, the entire component is actually very simple to use, but after use it can give us save a lot of things in the actual project development, so to share his method of use, if have some help to you, my pleasure ~ ~ ~

Of the pit

Personal Profile: Born in 1996, born in a fourth-tier city in Anhui province, graduated from Top 10 million universities. .NET programmer, gunslinger, cat. It will begin in December 2016. NET programmer career, Microsoft. NET technology stalwart, aspired to be the cloud cat kid programming for Google the best. NET programmer. Personal blog: yuiter.com blog garden blog: www.cnblogs.com/danvic712