您现在的位置是:网站首页> 编程资料编程资料
.NET 6开发TodoList应用之实现数据塑形_实用技巧_
2023-05-24
308人已围观
简介 .NET 6开发TodoList应用之实现数据塑形_实用技巧_
需求
在查询的场景中,还有一类需求不是很常见,就是在前端请求中指定返回的字段,所以关于搜索的最后一个主题我们就来演示一下关于数据塑形(Data Shaping)。
目标
实现数据塑形搜索请求。
原理与思路
对于数据塑形来说,我们需要定义一些接口和泛型类实现来完成通用的功能,然后修改对应的查询请求,实现具体的功能。
实现
定义通用接口和泛型类实现
IDataShaper.cs
using System.Dynamic; namespace TodoList.Application.Common.Interfaces; public interface IDataShaper{ IEnumerable ShapeData(IEnumerable entities, string fieldString); ExpandoObject ShapeData(T entity, string fieldString); }
并实现通用的功能:
DataShaper.cs
using System.Dynamic; using System.Reflection; using TodoList.Application.Common.Interfaces; namespace TodoList.Application.Common; public class DataShaper: IDataShaper where T : class { public PropertyInfo[] Properties { get; set; } public DataShaper() { Properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance); } public IEnumerable ShapeData(IEnumerable entities, string? fieldString) { var requiredProperties = GetRequiredProperties(fieldString); return GetData(entities, requiredProperties); } public ExpandoObject ShapeData(T entity, string? fieldString) { var requiredProperties = GetRequiredProperties(fieldString); return GetDataForEntity(entity, requiredProperties); } private IEnumerable GetRequiredProperties(string? fieldString) { var requiredProperties = new List (); if (!string.IsNullOrEmpty(fieldString)) { var fields = fieldString.Split(',', StringSplitOptions.RemoveEmptyEntries); foreach (var field in fields) { var property = Properties.FirstOrDefault(pi => pi.Name.Equals(field.Trim(), StringComparison.InvariantCultureIgnoreCase)); if (property == null) { continue; } requiredProperties.Add(property); } } else { requiredProperties = Properties.ToList(); } return requiredProperties; } private IEnumerable GetData(IEnumerable entities, IEnumerable requiredProperties) { return entities.Select(entity => GetDataForEntity(entity, requiredProperties)).ToList(); } private ExpandoObject GetDataForEntity(T entity, IEnumerable requiredProperties) { var shapedObject = new ExpandoObject(); foreach (var property in requiredProperties) { var objectPropertyValue = property.GetValue(entity); shapedObject.TryAdd(property.Name, objectPropertyValue); } return shapedObject; } }
定义扩展方法
为了使我们的Handle方法调用链能够直接应用,我们在Application/Extensions中新增一个DataShaperExtensions:
DataShaperExtensions.cs
using System.Dynamic; using TodoList.Application.Common.Interfaces; namespace TodoList.Application.Common.Extensions; public static class DataShaperExtensions { public static IEnumerable ShapeData(this IEnumerable entities, IDataShaper shaper, string? fieldString) { return shaper.ShapeData(entities, fieldString); } } 然后再对我们之前写的MappingExtensions静态类中添加一个方法:
MappingExtensions.cs
// 省略其他... public static PaginatedListPaginatedListFromEnumerable (this IEnumerable entities, int pageNumber, int pageSize) { return PaginatedList .Create(entities, pageNumber, pageSize); }
添加依赖注入
在Application的DependencyInjection.cs中添加依赖注入:
DependencyInjection.cs
// 省略其他 services.AddScoped(typeof(IDataShaper<>), typeof(DataShaper<>));
修改查询请求和Controller接口
我们在上一篇文章实现排序的基础上增加一个字段用于指明数据塑形字段并对应修改Handle方法:
GetTodoItemsWithConditionQuery.cs
using System.Dynamic; using AutoMapper; using AutoMapper.QueryableExtensions; using MediatR; using TodoList.Application.Common.Extensions; using TodoList.Application.Common.Interfaces; using TodoList.Application.Common.Mappings; using TodoList.Application.Common.Models; using TodoList.Application.TodoItems.Specs; using TodoList.Domain.Entities; using TodoList.Domain.Enums; namespace TodoList.Application.TodoItems.Queries.GetTodoItems; public class GetTodoItemsWithConditionQuery : IRequest> { public Guid ListId { get; set; } public bool? Done { get; set; } public string? Title { get; set; } // 前端指明需要返回的字段 public string? Fields { get; set; } public PriorityLevel? PriorityLevel { get; set; } public string? SortOrder { get; set; } = "title_asc"; public int PageNumber { get; set; } = 1; public int PageSize { get; set; } = 10; } public class GetTodoItemsWithConditionQueryHandler : IRequestHandler > { private readonly IRepository _repository; private readonly IMapper _mapper; private readonly IDataShaper _shaper; public GetTodoItemsWithConditionQueryHandler(IRepository repository, IMapper mapper, IDataShaper shaper) { _repository = repository; _mapper = mapper; _shaper = shaper; } public Task > Handle(GetTodoItemsWithConditionQuery request, CancellationToken cancellationToken) { var spec = new TodoItemSpec(request); return Task.FromResult( _repository .GetAsQueryable(spec) .ProjectTo (_mapper.ConfigurationProvider) .AsEnumerable() // 进行数据塑形和分页返回 .ShapeData(_shaper, request.Fields) .PaginatedListFromEnumerable(request.PageNumber, request.PageSize) ); } }
对应修改Controller:
TodoItemController.cs
[HttpGet] public async Task>> GetTodoItemsWithCondition([FromQuery] GetTodoItemsWithConditionQuery query) { return ApiResponse >.Success(await _mediator.Send(query)); }
验证
启动Api项目,执行查询TodoItem的请求:
请求

响应

我们再把之前讲到的过滤和搜索添加到请求里来:
请求

响应

总结
对于数据塑形的请求,关键步骤就是使用反射获取待返回对象的所有配置的可以返回的属性,再通过前端传入的属性名称进行过滤和值的重组进行返回。实现起来是比较简单的。但是在实际的使用过程中我不推荐这样用,除了某些非常适用的特殊场景。个人更偏向于向前端提供明确的接口定义。
到此这篇关于.NET 6开发TodoList应用之实现数据塑形的文章就介绍到这了,更多相关.NET 6数据塑形内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!
相关内容
- .NET 6开发TodoList应用之实现查询排序_实用技巧_
- gojs一些实用的高级用法_实用技巧_
- .NET 6中间件Http Logging使用介绍_实用技巧_
- .NET 6开发TodoList应用之请求日志组件HttpLogging介绍_实用技巧_
- .NET 6开发TodoList应用之实现查询分页_实用技巧_
- .Net Core下使用Dapper的方法_自学过程_
- ORM框架之Dapper简介和性能测试_基础应用_
- .NetCore使用Swagger+API多版本控制的流程分析_实用技巧_
- .NET 6开发TodoList应用之实现ActionFilter_实用技巧_
- 基于ABP框架实现RBAC(角色访问控制)_实用技巧_
