AutoMapper
AutoMapper 是一个在 .NET 应用程序中自动将一个对象的属性映射到另一个对象的属性的开源库。它旨在减少代码的重复性和提高开发人员的生产力。
在实际应用中,我们常常需要将一个实体对象转换为另一个实体对象,或者从数据访问层获取到的数据对象映射到业务逻辑层的对象。手动实现这种转换往往需要大量的重复代码,而 AutoMapper 则可以大大简化这个过程。
AutoMapper 的主要特点包括:
- 支持对象间复杂映射关系的配置;
- 支持映射关系的自动发现,从而减少手动配置的工作量;
- 支持 Fluent 接口,使得映射关系的配置更加清晰明了;
- 支持 LINQ 查询中的投影映射;
- 支持批量映射;
- 可以与 DI 容器集成;
- 具有良好的性能。
# 安装和配置
AutoMapper 可以通过 NuGet 包管理器进行安装,也可以手动下载安装。
在 Visual Studio 中使用 NuGet 安装 AutoMapper,可以通过以下命令:
Install-Package AutoMapper
安装完成后,在代码中使用以下命名空间:
using AutoMapper;
# 使用示例
以下是一个简单的使用示例,我们将从一个实体对象 Person
转换为另一个实体对象 PersonDto
:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
public class PersonDto
{
public string Name { get; set; }
public int Age { get; set; }
}
// 配置映射关系
Mapper.Initialize(cfg => cfg.CreateMap<Person, PersonDto>());
// 创建源对象
var person = new Person { Name = "张三", Age = 18 };
// 执行映射
var personDto = Mapper.Map<PersonDto>(person);
在上面的示例中,我们使用 Mapper.Initialize
方法进行映射关系的配置。该方法接受一个 Action<IMapperConfigurationExpression>
参数,可以使用其中的 CreateMap<TSource, TDestination>
方法进行源类型和目标类型之间的映射关系配置。
在配置完成后,我们可以通过 Mapper.Map<TDestination>(TSource source)
方法执行映射操作。该方法会自动根据映射关系将源对象转换为目标对象。
接下来,我们将介绍 AutoMapper 的更多功能和用法。
# 基本映射
除了上面的示例,我们还可以通过 Fluent 接口进行映射关系的配置。例如,我们可以将上面的映射关系改写为:
Mapper.Initialize(cfg =>
{
cfg.CreateMap<Person, PersonDto>()
.ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Name))
.ForMember(dest => dest.Age, opt => opt.MapFrom(src => src.Age));
});
```markdown
在上面的示例中,我们使用 `ForMember` 方法指定了源类型和目标类型之间的映射关系。该方法接受两个参数,第一个参数是目标属性的表达式,第二个参数是选项。
选项中包含多个配置项,可以用于控制映射的行为,例如:
- `MapFrom`:指定源属性的表达式;
- `Ignore`:忽略该属性;
- `Condition`:根据条件判断是否映射该属性;
- `NullSubstitute`:指定当源属性为 null 时,目标属性应该使用的默认值;
- `ConvertUsing`:指定转换器,用于将源属性转换为目标属性。
在实际使用中,我们还可以通过 `ReverseMap` 方法自动生成反向映射关系。例如:
```csharp
Mapper.Initialize(cfg =>
{
cfg.CreateMap<Person, PersonDto>()
.ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Name))
.ForMember(dest => dest.Age, opt => opt.MapFrom(src => src.Age))
.ReverseMap();
});
在上面的示例中,我们使用了 ReverseMap
方法,自动生成了从 PersonDto
到 Person
的反向映射关系。
# 投影映射
除了基本映射,AutoMapper 还支持在 LINQ 查询中使用投影映射。例如,我们可以通过以下代码将 Person
实体对象列表中的所有对象转换为 PersonDto
对象:
var persons = new List<Person>
{
new Person { Name = "张三", Age = 18 },
new Person { Name = "李四", Age = 20 },
new Person { Name = "王五", Age = 22 }
};
var personDtos = persons.Select(p => Mapper.Map<PersonDto>(p)).ToList();
在上面的示例中,我们使用 Select
方法对 persons
列表进行投影映射,自动将 Person
对象转换为 PersonDto
对象。
# 忽略某个属性不映射
CreateMap<DeClass , SoClass>()
//跳过成员AAA的映射 忽略AAA的映射
.ForMember( dest => dest.AAA , opt => opt.Ignore() )
# null替换
// 当源name中是null时,给目标一个默认值
CreateMap<Sou , Des>().ForMember( destination => destination.name , opt => opt.NullSubstitute( "默认值来了(搞个默认名字)" ) );
# 批量映射
在实际应用中,我们常常需要将多个实体对象转换为另一个实体对象。如果使用循环逐一转换,代码会非常繁琐。而 AutoMapper 可以通过批量映射实现快速转换。
例如,我们可以通过以下代码将 Person
实体对象列表转换为 PersonDto
实体对象列表:
var persons = new List<Person>
{
new Person { Name = "张三", Age = 18 },
new Person { Name = "李四", Age = 20 },
new Person { Name = "王五", Age = 22 }
};
var personDtos = Mapper.Map<List<PersonDto>>(persons);
在上面的示例中,我们直接调用 Mapper.Map
方法,将源对象列表转换为目标对象列表。AutoMapper 会自动遍历源列表,依次将每个对象转换为目标对象。
# 嵌套映射
在实际应用中,我们常常需要将一个复杂的对象转换为另一个复杂的对象。例如,我们有一个包含多个 Person
对象的 Department
对象,我们需要将其转换为一个包含多个 PersonDto
对象的 DepartmentDto
对象。
这时,我们可以使用嵌套映射来解决这个问题。例如,我们可以定义以下实体对象和 DTO 对象:
public class Department
{
public string Name { get; set; }
public List<Person> Persons { get; set; }
}
public class DepartmentDto
{
public string Name { get; set; }
public List<PersonDto> PersonDtos { get; set; }
}
然后,我们可以通过以下代码实现嵌套映射:
Mapper.Initialize(cfg =>
{
cfg.CreateMap<Person, PersonDto>();
cfg.CreateMap<Department, DepartmentDto>()
.ForMember(dest => dest.PersonDtos, opt => opt.MapFrom(src => src.Persons));
});
var department = new Department
{
Name = "IT部门",
Persons = new List<Person>
{
new Person { Name = "张三", Age = 18 },
new Person { Name = "李四", Age = 20 },
new Person { Name = "王五", Age = 22 }
}
};
var departmentDto = Mapper.Map<DepartmentDto>(department);
在上面的示例中,我们先使用 CreateMap
方法定义了 Person
和 PersonDto
之间的映射关系,然后定义了 Department
和 DepartmentDto
之间的映射关系。其中,我们使用 ForMember
方法指定了 PersonDtos
属性与 Persons
属性之间的映射关系。
# 映射前后处理(BeforeMap AfterMap)
# 方式1:Profile中直接使用
CreateMap<SourceClass , DestinationClass>()
.ForMember( dest => dest.year , opt => opt.MapFrom( src => src.age + 2000 ) )
.AfterMap( ( src , dest ) =>
{
DateTime now = DateTime.Now;
dest.name = "admin";
dest.now = now;
} );
# 方式2:实现接口类
实现接口IMappingAction
public class BookAction : IMappingAction<Book , BookDto>
{
//private readonly IHttpContextAccessor _httpContextAccessor;
//public BookAction ( IHttpContextAccessor httpContextAccessor )
//{
// 如果需要:可以在这里搞依赖注入
// _httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException( nameof( httpContextAccessor ) );
//}
public void Process ( Book source , BookDto destination , ResolutionContext context )
{
DateTime now = DateTime.Now;
destination.now = now;
}
}
CreateMap<Book , BookDto>()
.ForMember( dest => dest.url , opt => opt.MapFrom( src => src.InfoUrl ) )
//.AfterMap( ( src , dest ) =>
//{
// DateTime now = DateTime.Now;
// dest.now = now;
//} );
.AfterMap<BookAction>();
# 方式3:转换时直接使用
BookDto _BookDto2 = this.Mapper.Map<Book , BookDto>( newbook , opt =>
{
//opt.BeforeMap( ( Book src , BookDto dest ) => src.Value = src.Value + i );
opt.AfterMap( ( Book src , BookDto dest ) => dest.url = dest.url + "___" );
} );
var _SPIBOARDBINDSList = this._Mapper.Map<List<BoardBindReturnDto> , List<SPIBOARDBINDS>>( _BoardBindReturnDtoList , ( opt ) =>
{
//把id传递过去
opt.AfterMap( ( List<BoardBindReturnDto> src , List<SPIBOARDBINDS> dest ) =>
{
dest.ForEach( it =>
{
it.PID = ids;
} );
} );
} );
# 自定义转换器
在实际应用中,我们常常需要将一种类型的值转换为另一种类型的值。例如,我们需要将一个日期字符串转换为 DateTime
类型的值,或者将一个数字字符串转换为 decimal
类型的值。
AutoMapper 提供了 ConvertUsing
方法,用于自定义转换器。例如,我们可以定义以下转换器,将一个日期字符串转换为 DateTime
类型的值:
Mapper.Initialize(cfg =>
{
cfg.CreateMap<string, DateTime>().ConvertUsing(s => DateTime.ParseExact(s, "yyyyMMdd", CultureInfo.InvariantCulture));
});
var dateStr = "20230420";
var date = Mapper.Map<DateTime>(dateStr);
在上面的示例中,我们使用 ConvertUsing
方法自定义了一个转换器,将日期字符串转换为 DateTime
类型的值。在 ConvertUsing
方法中,我们传入一个转换函数,该函数接受源类型的值作为参数,返回目标类型的值。
# 结语
在本文中,我们详细介绍了 AutoMapper 的使用方法。除了基本映射、投影映射、批量映射、嵌套映射和自定义转换器等基本功能之外,AutoMapper 还支持很多高级特性,例如:
- 基于条件的映射;
- 支持嵌套集合的映射;
- 映射前后处理;
- 支持多个源对象的映射;
- 支持构造函数注入等等。
在实际应用中,我们可以根据实际需求选择适合的映射方式,从而提高开发效率和代码可读性。
最后,建议开发人员在使用 AutoMapper 时,注意映射关系的配置和调试,以避免出现错误和性能问题。同时,建议避免过多使用映射操作,从而保证应用程序的性能和可维护性。