如何使用AutoMapper实现GET请求

发布时间:2021-12-27 16:54:52 作者:iii
来源:亿速云 阅读:168

这篇文章主要讲解了“如何使用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<T>
{
    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<T>接口,因为此Dto不涉及特殊字段的Mapping规则
// 并且属性名称与领域实体保持一致,根据Convention规则默认可以完成Mapping,不需要额外实现
public class TodoListBriefDto : IMapFrom<Domain.Entities.TodoList>
{
    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<List<TodoListBriefDto>>
{
}

public class GetTodosQueryHandler : IRequestHandler<GetTodosQuery, List<TodoListBriefDto>>
{
    private readonly IRepository<Domain.Entities.TodoList> _repository;
    private readonly IMapper _mapper;

    public GetTodosQueryHandler(IRepository<Domain.Entities.TodoList> repository, IMapper mapper)
    {
        _repository = repository;
        _mapper = mapper;
    }

    public async Task<List<TodoListBriefDto>> Handle(GetTodosQuery request, CancellationToken cancellationToken)
    {
        return await _repository
            .GetAsQueryable()
            .AsNoTracking()
            .ProjectTo<TodoListBriefDto>(_mapper.ConfigurationProvider)
            .OrderBy(t => t.Title)
            .ToListAsync(cancellationToken);
    }
}

最后实现Controller层的逻辑:

TodoListController.cs

// 省略其他...
[HttpGet]
public async Task<ActionResult<List<TodoListBriefDto>>> 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<T>接口
public class TodoItemDto : IMapFrom<TodoItem>
{
    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<TodoItem, TodoItemDto>()
            .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<T>接口,因为此Dto不涉及特殊字段的Mapping规则
// 并且属性名称与领域实体保持一致,根据Convention规则默认可以完成Mapping,不需要额外实现
public class TodoListDto : IMapFrom<Domain.Entities.TodoList>
{
    public Guid Id { get; set; }
    public string? Title { get; set; }
    public string? Colour { get; set; }

    public IList<TodoItemDto> Items { get; set; } = new List<TodoItemDto>();
}

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

TodoListSpec.cs

using Microsoft.EntityFrameworkCore;
using TodoList.Application.Common;

namespace TodoList.Application.TodoLists.Specs;

public sealed class TodoListSpec : SpecificationBase<Domain.Entities.TodoList>
{
    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<TodoListDto?>
{
    public Guid ListId { get; set; }
}

public class ExportTodosQueryHandler : IRequestHandler<GetSingleTodoQuery, TodoListDto?>
{
    private readonly IRepository<Domain.Entities.TodoList> _repository;
    private readonly IMapper _mapper;

    public ExportTodosQueryHandler(IRepository<Domain.Entities.TodoList> repository, IMapper mapper)
    {
        _repository = repository;
        _mapper = mapper;
    }

    public async Task<TodoListDto?> Handle(GetSingleTodoQuery request, CancellationToken cancellationToken)
    {
        var spec = new TodoListSpec(request.ListId, true);
        return await _repository
            .GetAsQueryable(spec)
            .AsNoTracking()
            .ProjectTo<TodoListDto>(_mapper.ConfigurationProvider)
            .FirstOrDefaultAsync(cancellationToken);
    }
}

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

TodoListController.cs

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

验证

运行Api项目

获取所有TodoList列表

请求

如何使用AutoMapper实现GET请求

响应

如何使用AutoMapper实现GET请求

获取单个TodoList详情

请求

如何使用AutoMapper实现GET请求

响应

如何使用AutoMapper实现GET请求

填一个POST文章里的坑

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

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

请求

如何使用AutoMapper实现GET请求

返回

Content部分

如何使用AutoMapper实现GET请求

以及Header部分

如何使用AutoMapper实现GET请求

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

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

推荐阅读:
  1. .NET Core中使用OOM框架,AutoMapper的使用介绍
  2. C#中如何使用AutoMapper

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

automapper get

上一篇:SAP HU上面的Obj.to Which HU Belongs栏位是怎样的

下一篇:C#如何实现多文件打包压缩

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》