.NET框架的详细应用场景有哪些?

2026-04-27 17:261阅读0评论SEO基础
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计2116个文字,预计阅读时间需要9分钟。

.NET框架的详细应用场景有哪些?

目录需求目标原理与思路实现引入AutoMapper实现GET请求验证获取所有TodoList列表获取单个TodoList详情填写一个POST请求的坑总结需求实现GET请求以获取业务数据。需求非常简单:实现GET请求获取业务数据。

目录
  • 需求
  • 目标
  • 原理与思路
  • 实现
    • 引入AutoMapper
    • 实现GET请求
  • 验证
    • 获取所有TodoList列表
    • 获取单个TodoList详情
    • 填一个POST文章里的坑
  • 总结

    需求

    需求很简单:实现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相关的所有逻辑:

    .NET框架的详细应用场景有哪些?

    定义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列表

    请求

    响应

    获取单个TodoList详情

    请求

    响应

    填一个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); }

    请求

    返回

    Content部分

    以及Header部分

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

    总结

    其他和查询请求相关的例子我就不多举了,通过两个简单的例子想说明如何组织CQRS模式下的代码逻辑。我们可以直观地看出,CQRS操作是通过IRequest和IRequestHandler实现的,其中IRequest部分直接和API接口的请求参数直接或间接相关联,将请求参数通过注入的_mediator对象进行处理。

    同时我们在实现两个查询接口的过程中也可以发现,查询语句中的Select部分现在已经被AutoMapper的相关功能替代掉了,所以在调用Repository时,可能并不经常用到SelectXXXX相关的具有数据类型转换的接口,更多的还是使用返回IQueryable对象的接口。这和我在使用.NET 6开发TodoList应用之实现Repository模式中实践的有一点出入,在那篇文章中,我之所以把Repository的抽象层次做的很高的原因是,我希望顺便把类似的类库实现思路也梳理一下。就像评论中有朋友提出的那样,其实更多的场合下,因为会配合系统里其他组件的使用,比如这里的AutoMapper,那么对于Repository的实际需求就变成了只需要给我一个IQueryable对象即可。这也是我在那篇文章中试图强调的那样:关于Repository,每个人的理解和实现都有差别,因为取决于抽象程度和应用场合。

    这一篇文章处理了关于GET的请求,有一个小的知识点没有讲到:后台分页返回,这部分内容会在后面专门再回到查询的场景里来说。然后又留了一个小坑下一篇文章来说:全局异常处理和统一返回类型。 

    以上就是.NET 6开发TodoList应用之使用AutoMapper实现GET请求的详细内容,更多关于.NET 6 AutoMapper实现GET请求的资料请关注易盾网络其它相关文章!

    本文共计2116个文字,预计阅读时间需要9分钟。

    .NET框架的详细应用场景有哪些?

    目录需求目标原理与思路实现引入AutoMapper实现GET请求验证获取所有TodoList列表获取单个TodoList详情填写一个POST请求的坑总结需求实现GET请求以获取业务数据。需求非常简单:实现GET请求获取业务数据。

    目录
    • 需求
    • 目标
    • 原理与思路
    • 实现
      • 引入AutoMapper
      • 实现GET请求
    • 验证
      • 获取所有TodoList列表
      • 获取单个TodoList详情
      • 填一个POST文章里的坑
    • 总结

      需求

      需求很简单:实现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相关的所有逻辑:

      .NET框架的详细应用场景有哪些?

      定义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列表

      请求

      响应

      获取单个TodoList详情

      请求

      响应

      填一个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); }

      请求

      返回

      Content部分

      以及Header部分

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

      总结

      其他和查询请求相关的例子我就不多举了,通过两个简单的例子想说明如何组织CQRS模式下的代码逻辑。我们可以直观地看出,CQRS操作是通过IRequest和IRequestHandler实现的,其中IRequest部分直接和API接口的请求参数直接或间接相关联,将请求参数通过注入的_mediator对象进行处理。

      同时我们在实现两个查询接口的过程中也可以发现,查询语句中的Select部分现在已经被AutoMapper的相关功能替代掉了,所以在调用Repository时,可能并不经常用到SelectXXXX相关的具有数据类型转换的接口,更多的还是使用返回IQueryable对象的接口。这和我在使用.NET 6开发TodoList应用之实现Repository模式中实践的有一点出入,在那篇文章中,我之所以把Repository的抽象层次做的很高的原因是,我希望顺便把类似的类库实现思路也梳理一下。就像评论中有朋友提出的那样,其实更多的场合下,因为会配合系统里其他组件的使用,比如这里的AutoMapper,那么对于Repository的实际需求就变成了只需要给我一个IQueryable对象即可。这也是我在那篇文章中试图强调的那样:关于Repository,每个人的理解和实现都有差别,因为取决于抽象程度和应用场合。

      这一篇文章处理了关于GET的请求,有一个小的知识点没有讲到:后台分页返回,这部分内容会在后面专门再回到查询的场景里来说。然后又留了一个小坑下一篇文章来说:全局异常处理和统一返回类型。 

      以上就是.NET 6开发TodoList应用之使用AutoMapper实现GET请求的详细内容,更多关于.NET 6 AutoMapper实现GET请求的资料请关注易盾网络其它相关文章!