对象间的映射

介绍

将类似对象映射到另一个对象是很常见的。它也是繁琐和重复的,因为通常两个对象(类)可能具有相互映射的相同/相似属性。想象一下下面的典型 应用服务方法:

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是一个简单的DTO类,User是一个简单的实体我们从给定的输入手动创建了一个User实体。用户实体将在实际应用程序中具有更多属性,并且手动创建它将变得乏味且容易出错。当我们想要向User和CreateUserInput添加新属性时,我们还必须更改映射代码。

我们可以使用库来自动处理映射。 AutoMapper是用于对象到对象映射的最佳库之一。ASP.NET Boilerplate定义了一个IObjectMapper 接口来抽象它,然后使用Abp.AutoMapper 包中的AutoMapper实现这个接口

IObjectMapper接口

IObjectMapper是一个简单的抽象,它有Map方法将对象映射到另一个对象。我们可以用这个替换上面的代码,而不是:

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是一个简单的方法,它获取源对象并创建一个新的目标对象,其类型声明为泛型参数(此示例中为User)。Map方法具有将对象映射到现有对象的重载 假设我们已经有一个User实体,并希望使用一个对象来更新它的属性:

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

AutoMapper集成

所述Abp.AutoMapper NuGet包(模块)实施IObjectMapper,并提供额外的功能。

安装

首先,将Abp.AutoMapper NuGet包安装到您的项目中:

Install-Package Abp.AutoMapper

然后将AbpAutoMapperModule的依赖项添加到模块定义类:

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

然后,您可以在代码中安全地注入和使用IObjectMapper。您也可以在需要时使用 AutoMapper自己的API。

创建映射

在使用映射之前,AutoMapper要求您定义类之间的映射(默认情况下)。您可以查看AutoMapper自己的 文档以获取有关映射的详细信息。ASP.NET Boilerplate使它更容易和模块化。

自动映射属性

大多数情况下,您可能只想直接(和传统上)映射类。在这种情况下,您可以使用AutoMapAutoMapFromAutoMapTo 属性。例如,如果我们想将CreateUserInput映射上面示例中User类,我们可以使用AutoMapTo属性,如下所示:

[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属性在两个方向上映射两个类。但是在这个示例中,我们只需要从CreateUserInput映射到User,因此我们使用了AutoMapTo。

自定义映射

在某些情况下,简单映射可能不合适。例如,两个类的属性名称可能略有不同,或者您可能希望在mappping期间忽略某些属性。在这种情况下,您应该直接使用AutoMapper的API来定义映射。Abp.AutoMapper包定义了一个API,使自定义映射更加模块化。

假设我们要忽略映射上的密码,并且用户具有稍微不同的命名电子邮件属性。我们可以定义映射,如下所示:

[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具有更多用于对象到对象映射的选项和功能。有关详细信息,请参阅其文档

MapTo扩展方法

建议您注入并使用上面定义的IObjectMapper接口。这使我们的项目尽可能独立于AutoMapper。它还使单元测试更容易,因为我们可以替换(模拟)单元测试中的映射。

Abp.AutoMapper模块还定义了MapTo扩展方法,可以在任何对象上使用它来将其映射到另一个对象,而无需注入IObjectMapper。用法示例:

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扩展方法在Abp.AutoMapper命名空间中定义,因此您必须首先将此命名空间导入到您的代码文件中。

由于MapTo扩展方法是静态的,因此它们使用AutoMapper的静态实例(Mapper.Instance)。这对于应用程序代码来说很简单,但是你可能在单元测试中遇到问题,因为静态配置和映射器在不同的测试之间共享,所有测试都相互影响。

单元测试

我们希望将测试彼此隔离开来。为此,我们应该使用以下规则设计项目:

  1. 始终使用IObjectMapper,不要使用MapTo扩展方法。

  2. 配置Abp.AutoMapper模块使用本地Mapper实例(通过依赖注入注册为单例)而不是静态实例(Abp.AutoMapper默认使用静态Mapper.Instance允许您使用上面定义的MapTo扩展方法):

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

预定义映射

LocalizableString - > string

Abp.AutoMapper模块定义了一个映射,用于将LocalizableString(或ILocalizableString)对象转换为字符串对象。它使用ILocalizationManager进行转换,因此可在任何类的映射过程中自动本地化可本地化的属性。

注入IMapper

您可能需要直接使用AutoMapper的IMapper对象而不是IObjectMapper抽象。在这种情况下,只需在您的类中注入IMapper并使用它。Abp.AutoMapper包将依赖注入的IMapper注册 为单例。

nidie.com.cn - 用心与你沟通