C#怎么使用AutoMapper实现对象映射转换?

来源:网站主作者:长沙SEO公司头衔:草根站长
导读:本期聚焦于小伙伴创作的《C#怎么使用AutoMapper实现对象映射转换?》,敬请观看详情。把领域模型转成DTO时,手写赋值代码不仅冗长还容易漏字段。AutoMapper通过配置映射规则,能在运行时自动完成对象属性拷贝。核心类型是MapperConfiguration与IMapper,前者定义源类型到目标类型的映射关系,后者执行实际转换。使用CreateMap方法指定两端类型,框架按同名属性约定匹配,遇到名称不一致可用ForMember显式绑定。忽略空值、深拷贝、集合映射等需求均有对应配置。掌握Profile封装与依赖注入集成,可让分层架构中的转换逻辑更清晰且易维护。

在C#企业级开发中,分层架构常常要求把实体类、领域模型与DTO、ViewModel之间进行转换。AutoMapper是一个流行的对象到对象映射库,能帮助开发者减少手工编写赋值代码的工作量,同时降低出错概率。它基于约定优于配置的理念,也支持高度自定义的映射规则。

C#怎么使用AutoMapper实现对象映射转换?

AutoMapper基础概念

AutoMapper的核心在于“映射配置”和“映射执行”两个动作。映射配置描述源类型与目标类型之间的属性对应关系,映射执行则是根据配置生成目标对象。最常用的两个类型是MapperConfigurationIMapper。前者在程序启动时一次性构建,后者被注入到业务代码中完成具体转换。

在没有AutoMapper的情况下,我们通常会写大量类似userDto.Name = user.Name;这样的语句。当属性达到几十个,或者存在嵌套对象时,维护成本急剧上升。AutoMapper通过反射与表达式树,在首次映射时编译出高效的赋值委托,后续调用几乎无额外性能损耗。

安装与基础映射

通过NuGet安装AutoMapper包,目前主流版本为AutoMapper 12.x。在控制台项目中可以使用如下命令:

// 使用NuGet包管理器控制台执行
// Install-Package AutoMapper

完成安装后,最基础的用法是创建配置并映射。下面示例展示如何将User实体映射为UserDto

using AutoMapper;
using System;

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
}

public class UserDto
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
}

class Program
{
    static void Main()
    {
        // 配置映射关系
        var config = new MapperConfiguration(cfg =>
        {
            cfg.CreateMap<User, UserDto>();
        });

        // 创建映射器
        IMapper mapper = config.CreateMapper();

        // 源对象
        var user = new User
        {
            Id = 1,
            Name = "张三",
            Email = "test@ipipp.com"
        };

        // 执行映射
        UserDto dto = mapper.Map<UserDto>(user);

        Console.WriteLine($"Id={dto.Id}, Name={dto.Name}, Email={dto.Email}");
    }
}

上述代码中,CreateMap<User, UserDto>()告诉AutoMapper:遇到从User到UserDto的转换请求时,按照同名属性自动赋值。因为两个类的属性名完全一致,所以无需额外配置即可工作。

这种方式的优点是直观、代码量少;缺点是配置散落在代码中,不适合大型项目。实际开发中更推荐把配置集中到Profile里,并结合依赖注入使用。

使用Profile组织映射规则

Profile是AutoMapper提供的配置封装类,允许把同一模块相关的映射写在一起。继承Profile并重写构造函数,在其中调用CreateMap即可。

using AutoMapper;
using System.Collections.Generic;

public class Order
{
    public int OrderId { get; set; }
    public decimal TotalAmount { get; set; }
    public List<OrderItem> Items { get; set; }
}

public class OrderItem
{
    public string ProductName { get; set; }
    public int Count { get; set; }
}

public class OrderDto
{
    public int OrderId { get; set; }
    public decimal TotalAmount { get; set; }
    public List<OrderItemDto> Items { get; set; }
}

public class OrderItemDto
{
    public string ProductName { get; set; }
    public int Count { get; set; }
}

public class MappingProfile : Profile
{
    public MappingProfile()
    {
        CreateMap<Order, OrderDto>();
        CreateMap<OrderItem, OrderItemDto>();
    }
}

在上面的例子中,Order包含Items集合,而OrderDto也包含Items集合。AutoMapper会自动识别集合元素的类型,并复用已配置的OrderItemOrderItemDto映射,实现嵌套对象转换。

使用Profile后,只需在初始化MapperConfiguration时传入Profile实例,就能加载全部规则。这比在多处写lambda表达式更易测试与维护。

处理属性名称不一致

实际项目中,源类与目标类的字段名往往不同。比如数据库实体使用UserName,而前端DTO使用Account。此时需要用ForMember显式指定。

using AutoMapper;
using System;

public class Member
{
    public string UserName { get; set; }
    public DateTime Birthday { get; set; }
}

public class MemberDto
{
    public string Account { get; set; }
    public int Age { get; set; }
}

public class MemberProfile : Profile
{
    public MemberProfile()
    {
        CreateMap<Member, MemberDto>()
            .ForMember(dest => dest.Account, opt => opt.MapFrom(src => src.UserName))
            .ForMember(dest => dest.Age, opt => opt.MapFrom(src =>
                DateTime.Now.Year - src.Birthday.Year));
    }
}

class Test
{
    static void Run()
    {
        var config = new MapperConfiguration(cfg => cfg.AddProfile<MemberProfile>());
        var mapper = config.CreateMapper();

        var m = new Member { UserName = "lilei", Birthday = new DateTime(1990, 5, 1) };
        var dto = mapper.Map<MemberDto>(m);
        Console.WriteLine(dto.Account + " " + dto.Age);
    }
}

ForMember的第一个参数是目标属性表达式,第二个参数通过MapFrom定义取值逻辑。除了简单字段映射,还可以调用方法、计算值、甚至忽略某些属性。

如果目标属性在源中不存在且不需要赋值,可以用.ForMember(dest => dest.X, opt => opt.Ignore())跳过,避免映射器报错或赋予默认值。

在ASP.NET Core中集成依赖注入

Web项目通常不会自己new配置,而是把IMapper注册到DI容器。AutoMapper提供了AutoMapper.Extensions.Microsoft.DependencyInjection包,一行代码即可完成。

// Program.cs 或 Startup.cs 中
using AutoMapper;
using Microsoft.Extensions.DependencyInjection;

var builder = WebApplication.CreateBuilder(args);

// 扫描程序集中所有Profile并注册IMapper
builder.Services.AddAutoMapper(typeof(Program).Assembly);

var app = builder.Build();

注册之后,在Controller或Service的构造函数中直接注入IMapper即可使用。这样配置在应用启动时构建一次,线程安全且性能稳定。

需要注意的是,若映射配置有错误(比如必填字段无法解析),会在应用启动时抛出异常,而不是在第一次请求时。这其实是一种保护机制,能尽早暴露配置问题。

集合映射与常见坑

AutoMapper天然支持ListArrayIEnumerable等集合类型映射。只要元素类型映射已配置,直接mapper.Map<List<Dto>>(sourceList)就能得到目标列表。

using AutoMapper;
using System.Collections.Generic;

var config = new MapperConfiguration(cfg =>
{
    cfg.CreateMap<User, UserDto>();
});
var mapper = config.CreateMapper();

var users = new List<User>
{
    new User { Id = 1, Name = "A", Email = "a@ipipp.com" },
    new User { Id = 2, Name = "B", Email = "b@ipipp.com" }
};

List<UserDto> dtos = mapper.Map<List<UserDto>>(users);

一个常见误区是认为AutoMapper会修改已有目标对象。实际上默认行为是创建新实例,除非调用mapper.Map(source, existingDest)重载。另外,映射是浅拷贝,如果源对象持有引用类型,目标对象中的对应属性会指向同一引用,修改可能影响原数据。

另一个容易忽视的点是循环引用。若对象图存在互相引用(如订单关联客户,客户又包含订单列表),需配置MaxDepth或改用扁平化DTO,否则映射时可能栈溢出。

总结与最佳实践

AutoMapper的价值在于把“对象转换”这件重复且易错的事标准化。对于简单同名映射,零配置即可生效;对于复杂场景,通过Profile、ForMember、自定义解析器可以灵活应对。

建议在项目中统一使用Profile管理映射,禁止在业务代码里散落new MapperConfiguration。同时把DTO设计得尽量扁平,减少嵌套与循环引用,能让映射更高效、更易排查问题。当字段极少且转换逻辑特殊时,手写映射可能比引入配置更直观,不必强行使用框架。

AutoMapperC#_object_mappingDTO_conversion修改时间:2026-08-04 22:06:19

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。