ABP框架系列之四十二:(Object-To-Object-Mapping-對象映射)


Introduction

It's a common to map a similar object to another object. It's also tedious and repeating since generally both objects (classes) may have similar/same properties mapped to each other. Think on a typical application servicemethod below:

將相似的對象映射到另一個對象是很常見的。這也是乏味和重復的,因為一般兩個對象(類)可能具有相似的/相同的屬性映射到彼此。想象在一個典型的應用服務的方法:

public class UserAppService : ApplicationService
{
    private readonly IRepository<User> _userRepository;

    public UserAppService(IRepository<User> userRepository)
    {
        _userRepository = userRepository;
    }

    public void CreateUser(CreateUserInput input)
    {
        var user = new User
        {
            Name = input.Name,
            Surname = input.Surname,
            EmailAddress = input.EmailAddress,
            Password = input.Password
        };

        _userRepository.Insert(user);
    }
}

CreateUserInput is a simple DTO and User is a simple entity here. We manually created a User entity from given input. User entity will have much more properties in a real world application and manually creating it will become tedious and error-prone. Also, we should change the mapping code when we want to add new properties to User and CreateUserInput.

createuserinput是一個簡單的DTO和用戶這是一個簡單的實體。我們從給定輸入手動創建了一個用戶實體。用戶實體在實際應用程序中具有更多的屬性,手工創建它將變得單調乏味,容易出錯。同時,我們應該改變映射的代碼時,我們要對用戶和createuserinput添加新的屬性。

We can use a library to make this mapping automatically. AutoMapper is one of the best libraries for object to object mapping. ASP.NET Boilerplate defines IObjectMapper interface to abstract it and implements this interface using AutoMapper in Abp.AutoMapper package.

我們可以使用一個庫來自動繪制這個映射。AutoMapper是一個對象到對象的映射最好的庫。ASP.NET的模板定義iobjectmapper接口抽象和實現這個接口使用AutoMapper Abp.AutoMapper包。

IObjectMapper Interface

IObjectMapper is a simple abstraction that has Map methods to map an object to another. We can write the sample code above as like below:

public class UserAppService : ApplicationService
{
    private readonly IRepository<User> _userRepository;
    private readonly IObjectMapper _objectMapper;

    public UserAppService(IRepository<User> userRepository, IObjectMapper objectMapper)
    {
        _userRepository = userRepository;
        _objectMapper = objectMapper;
    }

    public void CreateUser(CreateUserInput input)
    {
        var user = _objectMapper.Map<User>(input);
        _userRepository.Insert(user);
    }
}

Map is a simple method gets the source object and creates a new destination object with the type declared as the generic parameter (User in this sample). Map method has an overload to map an object to an existing object. Assume that we already have a User entity and want to update it's properties by an object:

map是獲取源對象並創建一個新的目標對象的簡單方法,該對象聲明為泛型參數(本示例中的用戶)。映射方法具有將對象映射到現有對象的重載。假設我們已經有一個用戶實體,並且想通過對象更新它的屬性:

public void UpdateUser(UpdateUserInput input)
{
    var user = _userRepository.Get(input.Id);
    _objectMapper.Map(input, user);
}

AutoMapper Integration

Abp.AutoMapper nuget package (module) implements IObjectMapper and provides additional features.

Installation

First, install Abp.AutoMapper nuget to your project:

Install-Package Abp.AutoMapper

Then add a dependency for AbpAutoMapperModule to your module definition class:

[DependsOn(typeof(AbpAutoMapperModule))]
public class MyModule : AbpModule
{
    ...
}

Then you can safely inject and use IObjectMapper in your code. You can also use AutoMapper's own API when you need.

Creating Mappings

AutoMapper requires to define mappings between classes (by default) before using the mapping. You can see it's own documentation for details on mapping. ASP.NET Boilerplate makes it a bit easier and modular.

AutoMapper需要定義之間的映射類(默認)在使用映射。關於映射的詳細信息,您可以看到它自己的文檔。ASP.NET樣板使它更簡單和模塊化。

Auto Mapping Attributes(自動映射特性

Most of time you only want to directly (and conventionally) map classes. In that case, you can use AutoMap, AutoMapFrom and AutoMapTo attributes. For instance, as we want to map CreateUserInput class to User class in the sample above, we can use AutoMapTo attribute as shown below:

[AutoMapTo(typeof(User))]
public class CreateUserInput
{
    public string Name { get; set; }

    public string Surname { get; set; }

    public string EmailAddress { get; set; }

    public string Password { get; set; }
}

AutoMap attribute maps two classes in both direction. But in this sample, we only need to map from CreateUserInput to User, so we used AutoMapTo.

Custom Mapping(自定義映射

Simple mapping may not be suitable in some cases. For instance, property names of two classes may be a little different or you may want to ignore some properties during the mappping. In such cases you should directly use AutoMapper's API to define the mapping. Abp.AutoMapper package defines API to make this custom mapping stuff more modular.

在某些情況下,簡單映射可能不合適。例如,兩類屬性名稱可能有所不同,或者你可能想忽略的一些性質在映射。在這種情況下,你應該直接使用AutoMapper的API來定義映射。abp.automapper包定義API進行自定義映射的東西更加模塊化。

Assume that we want to ignore Password on mapping and User has Email property for email address. We can define mapping as shown below:

假設我們希望忽略映射中的密碼,用戶擁有電子郵件地址的電子郵件屬性。我們可以定義映射如下圖所示:

[DependsOn(typeof(AbpAutoMapperModule))]
public class MyModule : AbpModule
{
    public override void PreInitialize()
    {
        Configuration.Modules.AbpAutoMapper().Configurators.Add(config =>
        {
            config.CreateMap<CreateUserInput, User>()
                  .ForMember(u => u.Password, options => options.Ignore())
                  .ForMember(u => u.Email, options => options.MapFrom(input => input.EmailAddress));
        });
    }
}

AutoMapper has much more options and abilities for object to object mapping. See it's documentation for more.

MapTo Extension Methods(MapTo 擴展方法

It's suggested to inject and use IObjectMapper interface as defined before. This makes our project independent from AutoMapper as much as possible. It also makes unit testing easier since we can replace (mock) the mapping in unit tests.

Abp.AutoMapper module also defines MapTo extension methods which can be used on any object to map it to another object without injecting IObjectMapper. Example usage:

public class UserAppService : ApplicationService
{
    private readonly IRepository<User> _userRepository;

    public UserAppService(IRepository<User> userRepository)
    {
        _userRepository = userRepository;
    }

    public void CreateUser(CreateUserInput input)
    {
        var user = input.MapTo<User>();
        _userRepository.Insert(user);
    }

    public void UpdateUser(UpdateUserInput input)
    {
        var user = _userRepository.Get(input.Id);
        input.MapTo(user);
    }
}

MapTo extension methods are defined in Abp.AutoMapper namespace, so you first import this namespaces into your code file.

Since MapTo extension methods are statics, they use AutoMapper's static instance (Mapper.Instance). This is simple and fine for the application code, but can have problems in unit tests since static configuration and mapper is shared among different tests and they may effect each other.

由於擴展方法是靜態的,他們使用AutoMapper的靜態實例(制圖。實例)。這對於應用程序代碼來說是簡單而精細的,但是單元測試可能會有問題,因為靜態配置和映射器在不同的測試中共享,它們可能相互影響。

Unit Tests

We want to isolate tests from each others. To do that, we should design our project with the following rules:

我們希望從彼此之間分離測試。要做到這一點,我們應該按照以下規則來設計我們的項目:

1. Always use IObjectMapper, not use MapTo extension methods.

2. Configure Abp.AutoMapper module to use local Mapper instance (registered as singleton to dependency injection) rather than the static one (Abp.AutoMapper uses the static Mapper.Instance by default to allow to use MapTo extension methods defined above):

Configuration.Modules.AbpAutoMapper().UseStaticMapper = false;

Pre Defined Mappings(預先定義的映射

LocalizableString -> string

Abp.AutoMapper module defines a mapping to convert LocalizableString (or ILocalizableString) objects to string objects. It makes the conversion using ILocalizationManager, so localizable properties are automatically localized during the mapping process of any class.

abp.automapper模塊定義轉換localizablestring映射(或ilocalizablestring)對象的字符串對象。它使轉換使用ilocalizationmanager,所以可本地化的屬性自動定位的任何類別的映射過程。

Injecting IMapper

You may need to directly use AutoMapper's IMapper object instead of IObjectMapper abstraction. In that case, just inject IMapper in your classes and use it. Abp.AutoMapper package registers IMapper to dependency injectionas singleton.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM