千家信息网

如何使用AutoMapper实现GET请求

发表于:2024-11-18 作者:千家信息网编辑
千家信息网最后更新 2024年11月18日,这篇文章主要讲解了"如何使用AutoMapper实现GET请求",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"如何使用AutoMapper实现GET请求
千家信息网最后更新 2024年11月18日如何使用AutoMapper实现GET请求

这篇文章主要讲解了"如何使用AutoMapper实现GET请求",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"如何使用AutoMapper实现GET请求"吧!

    需求

    需求很简单:实现GET请求获取业务数据。在这个阶段我们经常使用的类库是AutoMapper。

    目标

    合理组织并使用AutoMapper,完成GET请求。

    原理与思路

    首先来简单地介绍一下这这个类库。

    关于AutoMapper

    在业务侧代码和数据库实体打交道的过程中,一个必不可少的部分就是返回的数据类型转换。对于不同的请求来说,希望得到的返回值是数据库实体的一部分/组合/计算等情形。我们就经常需要手写用于数据对象转换的代码,但是转换前后可能大部分情况下有着相同名称的字段或属性。这部分工作能避免手写冗长的代码吗?可以。

    我们希望接受的请求和返回的值(统一称为model)具有以下两点需要遵循的原则:

    1.每个model被且只被一个API消费;

    2.每个model里仅仅包含API发起方希望包含的必要字段或属性。

    AutoMapper库就是为了实现这个需求而存在的,它的具体用法请参考官方文档,尤其是关于Convention的部分,避免重复劳动。

    实现

    所有需要使用AutoMapper的地方都集中在Application项目中。

    引入AutoMapper

    $ dotnet add src/TodoList.Application/TodoList.Application.csproj package AutoMapper.Extensions.Microsoft.DependencyInjection

    然后在Application/Common/Mappings下添加配置,提供接口的原因是我们后面就可以在DTO里实现各自对应的Mapping规则,方便查找。

    IMapFrom.cs

    using AutoMapper;namespace TodoList.Application.Common.Mappings;public interface IMapFrom{    void Mapping(Profile profile) => profile.CreateMap(typeof(T), GetType());}

    MappingProfile.cs

    using System.Reflection;using AutoMapper;namespace TodoList.Application.Common.Mappings;public class MappingProfile : Profile{    public MappingProfile() => ApplyMappingsFromAssembly(Assembly.GetExecutingAssembly());    private void ApplyMappingsFromAssembly(Assembly assembly)    {        var types = assembly.GetExportedTypes()            .Where(t => t.GetInterfaces().Any(i =>                i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IMapFrom<>)))            .ToList();        foreach (var type in types)        {            var instance = Activator.CreateInstance(type);            var methodInfo = type.GetMethod("Mapping")                             ?? type.GetInterface("IMapFrom`1")!.GetMethod("Mapping");            methodInfo?.Invoke(instance, new object[] { this });        }    }}

    DependencyInjection.cs进行依赖注入:

    DependencyInjection.cs

    // 省略其他...services.AddAutoMapper(Assembly.GetExecutingAssembly());services.AddMediatR(Assembly.GetExecutingAssembly());return services;

    实现GET请求

    在本章中我们只实现TodoListQuery接口(GET),并且在结果中包含TodoItem集合,剩下的接口后面的文章中逐步涉及。

    GET All TodoLists

    Application/TodoLists/Queries/下新建一个目录GetTodos用于存放创建一个TodoList相关的所有逻辑:

    定义TodoListBriefDto对象:

    TodoListBriefDto.cs

    using TodoList.Application.Common.Mappings;namespace TodoList.Application.TodoLists.Queries.GetTodos;// 实现IMapFrom接口,因为此Dto不涉及特殊字段的Mapping规则// 并且属性名称与领域实体保持一致,根据Convention规则默认可以完成Mapping,不需要额外实现public class TodoListBriefDto : IMapFrom{    public Guid Id { get; set; }    public string? Title { get; set; }    public string? Colour { get; set; }}

    GetTodosQuery.cs

    using AutoMapper;using AutoMapper.QueryableExtensions;using MediatR;using Microsoft.EntityFrameworkCore;using TodoList.Application.Common.Interfaces;namespace TodoList.Application.TodoLists.Queries.GetTodos;public class GetTodosQuery : IRequest>{}public class GetTodosQueryHandler : IRequestHandler>{    private readonly IRepository _repository;    private readonly IMapper _mapper;    public GetTodosQueryHandler(IRepository repository, IMapper mapper)    {        _repository = repository;        _mapper = mapper;    }    public async Task> Handle(GetTodosQuery request, CancellationToken cancellationToken)    {        return await _repository            .GetAsQueryable()            .AsNoTracking()            .ProjectTo(_mapper.ConfigurationProvider)            .OrderBy(t => t.Title)            .ToListAsync(cancellationToken);    }}

    最后实现Controller层的逻辑:

    TodoListController.cs

    // 省略其他...[HttpGet]public async Task>> Get(){    return await _mediator.Send(new GetTodosQuery());}

    GET Single TodoList

    首先在Application/TodoItems/Queries/下新建目录GetTodoItems用于存放获取TodoItem相关的所有逻辑:

    定义TodoItemDtoTodoListDto对象:

    TodoItemDto.cs

    using AutoMapper;using TodoList.Application.Common.Mappings;using TodoList.Domain.Entities;namespace TodoList.Application.TodoItems.Queries.GetTodoItems;// 实现IMapFrom接口public class TodoItemDto : IMapFrom{    public Guid Id { get; set; }    public Guid ListId { get; set; }    public string? Title { get; set; }    public bool Done { get; set; }    public int Priority { get; set; }    // 实现接口定义的Mapping方法,并提供除了Convention之外的特殊字段的转换规则    public void Mapping(Profile profile)    {        profile.CreateMap()            .ForMember(d => d.Priority, opt => opt.MapFrom(s => (int)s.Priority));    }}

    TodoListDto.cs

    using TodoList.Application.Common.Mappings;using TodoList.Application.TodoItems.Queries.GetTodoItems;namespace TodoList.Application.TodoLists.Queries.GetSingleTodo;// 实现IMapFrom接口,因为此Dto不涉及特殊字段的Mapping规则// 并且属性名称与领域实体保持一致,根据Convention规则默认可以完成Mapping,不需要额外实现public class TodoListDto : IMapFrom{    public Guid Id { get; set; }    public string? Title { get; set; }    public string? Colour { get; set; }    public IList Items { get; set; } = new List();}

    创建一个根据ListId来获取包含TodoItems子项的spec:

    TodoListSpec.cs

    using Microsoft.EntityFrameworkCore;using TodoList.Application.Common;namespace TodoList.Application.TodoLists.Specs;public sealed class TodoListSpec : SpecificationBase{    public TodoListSpec(Guid id, bool includeItems = false) : base(t => t.Id == id)    {        if (includeItems)        {            AddInclude(t => t.Include(i => i.Items));        }    }}

    我们仍然为这个查询新建一个GetSingleTodo目录,并实现GetSIngleTodoQuery

    GetSingleTodoQuery.cs

    using AutoMapper;using AutoMapper.QueryableExtensions;using MediatR;using Microsoft.EntityFrameworkCore;using TodoList.Application.Common.Interfaces;using TodoList.Application.TodoLists.Specs;namespace TodoList.Application.TodoLists.Queries.GetSingleTodo;public class GetSingleTodoQuery : IRequest{    public Guid ListId { get; set; }}public class ExportTodosQueryHandler : IRequestHandler{    private readonly IRepository _repository;    private readonly IMapper _mapper;    public ExportTodosQueryHandler(IRepository repository, IMapper mapper)    {        _repository = repository;        _mapper = mapper;    }    public async Task Handle(GetSingleTodoQuery request, CancellationToken cancellationToken)    {        var spec = new TodoListSpec(request.ListId, true);        return await _repository            .GetAsQueryable(spec)            .AsNoTracking()            .ProjectTo(_mapper.ConfigurationProvider)            .FirstOrDefaultAsync(cancellationToken);    }}

    添加Controller逻辑,这里的Name是为了完成之前遗留的201返回的问题,后文会有使用。

    TodoListController.cs

    // 省略其他...[HttpGet("{id:Guid}", Name = "TodListById")]public async Task> GetSingleTodoList(Guid id){    return await _mediator.Send(new GetSingleTodoQuery    {        ListId = id    }) ?? throw new InvalidOperationException();}

    验证

    运行Api项目

    获取所有TodoList列表

    请求

    响应

    获取单个TodoList详情

    请求

    响应

    填一个POST文章里的坑

    在使用.NET 6开发TodoList应用(6)--使用MediatR实现POST请求中我们留了一个问题,即创建TodoList后的返回值当时我们是临时使用Id返回的,推荐的做法是下面这样:

    // 省略其他...[HttpPost]public async Task Create([FromBody] CreateTodoListCommand command){    var createdTodoList = await _mediator.Send(command);    // 创建成功返回201    return CreatedAtRoute("TodListById", new { id = createdTodoList.Id }, createdTodoList);}

    请求

    返回

    Content部分

    以及Header部分

    我们主要观察返回的HTTPStatusCode是201,并且在Header中location字段表明了创建资源的位置。

    感谢各位的阅读,以上就是"如何使用AutoMapper实现GET请求"的内容了,经过本文的学习后,相信大家对如何使用AutoMapper实现GET请求这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是,小编将为大家推送更多相关知识点的文章,欢迎关注!

    0