.NET 6開發TodoList應用之實現資料塑形
目錄
- 需求
- 目標
- 原理與思路
- 實現
- 定義通用介面和泛型類實現
- 定義擴充套件方法
- 新增依賴注入
- 修改查詢請求和Controller介面
- 驗證
- 總結
需求
在查詢的場景中,還有一類需求不是很常見,就是在前端請求中指定返回的欄位,所以關於搜尋的最後一個主題我們就來演示一下關於資料塑形(Data Shaping)。
目標
實現資料塑形搜尋請求。
原理與思路
對於資料塑形來說,我們需要定義一些介面和泛型類實現來完成通用的功能,然後修改對應的查詢請求,實現具體的功能。
實現
定義通用介面和泛型類實現
IDataShaper.cs
using System.Dynamic; namespace TodoList.Application.Common.Interfaces; public interface IDataShaper<T> { IEnumerable<ExpandoObject> ShapeData(IEnumerable<T> 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<T> : IDataShaper<T> where T : class { public PropertyInfo[] Properties { get; set; } public DataShaper() { Properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance); } public IEnumerable<ExpandoObject> ShapeData(IEnumerable<T> 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<PropertyInfo> GetRequiredProperties(string? fieldString) { var requiredProperties = new List<PropertyInfo>(); 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 { www.cppcns.comrequiredProperties = Properties.ToList(); } return requiredProperties; } private IEnumerable<ExpandoObject> GetData(IEnumerable<T> entities,IEnumerable<PropertyInfo> requiredProperties) { return entities.Select(entity => GetDataForEntity(entity,requiredProperties)).ToList(); } private ExpandoObject GetDataForEntity(T entity,IEnumerable<PropertyInfo> 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<ExpandoObject> ShapeData<T>(this IEnumerable<T> entities,IDataShaper<T> shaper,string? fieldString) { return shaper.ShapeData(entities,fieldString); } }
然後再對我們之前寫的MappingExtensions靜態類中新增一個方法:
MappingExtensions.cs
// 省略其他... public static PaginatedList<TDestination> PaginatedListFromEnumerable<TDestination>(this IEnumerable<TDestination> entities,int pageNumber,int pageSize) { return PaginatedList<TDestination>.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.Quwww.cppcns.comeries.GetTodoItems; public class GetTodoItemsWithConditionQuery : IRequest<PaginatedList<ExpandoObject>> { 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 strhttp://www.cppcns.coming? SortOrder { get; set; } = "title_asc"; public int PageNumber { get; set; } = 1; public int PageSize { get; set; } = 10; } public class GetTodoItemsWithConditionQueryHandler : IRequestHandler<GetTodoItemsWithConditionQuery,PaginatedList<ExpandoObject>> { private readonly IRepository<TodoItem> _repository; private readonly IMapper _mapper; private readonly IDataShaper<TodoItemDto> _shapehttp://www.cppcns.comr; public GetTodoItemsWithConditionQueryHandler(IRepository<TodoItem> repository,IMapper mapper,IDataShaper<TodoItemDto> shaper) { _repository = repository; _mapper = mapper; _shaper = shaper; } public Task<PaginatedList<ExpandoObject>> Handle(GetTodoItemsWithConditionQuery request,CancellationToken cancellationToken) { var spec = new TodoItemSpec(request); return Task.FromResult( _repository .GetAsQueryable(spec) .ProjectTo<TodoItemDto>(_mapper.ConfigurationProvider) .AsEnumerable() // 進行資料塑形和分頁返回 .ShapeData(_shaper,request.Fields) .PaginatedListFromEnumerable(request.PageNumber,request.PageSize) ); } }
對應修改Controller:
TodoItemController.cs
[HttpGet] public async Task<ApiResponse<PaginatedList<ExpandoObject>>> GetTodoItemsWithCondition([FromQuery] GetTodoItemsWithConditionQuery query) { return ApiResponse<PaginatedList<ExpandoObject>>.Success(await _mediator.Send(query)); }
驗證
啟動Api專案,執行查詢TodoItem的請求:
請求
響應
我們再把之前講到的過濾和搜尋新增到請求裡來:
請求
響應
總結
對於資料塑形的請求,關鍵步驟就是使用反射獲取待返回物件的所有配置的可以返回的屬性,再通過前端傳入的屬性名稱進行過濾和值的重組進行返回。實現起來是比較簡單的。但是在實際的使用過程中我不推薦這樣用,除了某些非常適用的特殊場景。個人更偏向於向前端提供明確的介面定義。
到此這篇關於.NET 6開發TodoList應用之實現資料塑形的文章就介紹到這了,更多相關.NET 6資料塑形內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!