OnionArch - 如何實現更新指定字段的通用Handler

博主最近失業在家,找工作之余,自己動手寫了個洋蔥架構(整潔架構)解決方案 , 以總結和整理以前的項目經驗,起名叫OnionArch,其目的是為了更好的實現采用DDD(領域驅動分析)和命令查詢職責分離(CQRS)的洋蔥架構 。
什么是OnionArchOnionArch解決方案清晰的展示了程序各分層的職責 , 幫助程序員寫出邏輯更清晰的代碼 , 提高程序的高內聚、低耦合性 。提高程序邏輯的可重用性 , 可擴展性和可測試性 。
OnionArch可降低單個微服務,特別是SAAS微服務開發的復雜度 , 在保證軟件質量的基礎上提高軟件開發效率 。
OnionArch基于最新的開源.Net 7.0 RC1, 數據庫采用PostgreSQL, 目前實現了包括多租戶在內的12個特性 。詳細內容請看:https://www.cnblogs.com/xiaozhuang/p/16772485.html
OnionArch如何實現更新指定字段的通用Handler本篇主要講述OnionArch如何實現更新指定字段的通用Handler,這里的Handler是指MediatR中繼承IRequestHandler接口的處理邏輯 。
Let me show you the code:
public async override Task<UpdateProductReply> UpdateProduct(UpdateProductRequest request, ServerCallContext context){UpdateCommand<UpdateProductRequest> updateProductCommand = new UpdateCommand<UpdateProductRequest>(Guid.Parse(request.ProductId),request,p => p.Description);await _mediator.Send(updateProductCommand);return new UpdateProductReply(){Message = "Update Product sucess"};}這是一個實現UpdateProduct業務用例的GRPC方法,新建一個UpdateCommand更新命令,并傳入ProductId和要更新的數據request(表單提交的ViewModel),以及要更新的屬性字段 。然后調用Mediator.Send發送該命令即可 。
UpdateCommand命令定義如下,繼承自CQRS的ICommand接口,并采用了C#的record類型,確保必填字段不為空 。
public record UpdateCommand<TModel>(Guid Id, TModel Model, params Expression<Func<TModel, object>>[] UpdateProperties) : ICommand{ }第三個參數可以傳入多個要更新的字段的Lambda 表達式,不采用字符串List的方式以防止程序員寫錯 。
【OnionArch - 如何實現更新指定字段的通用Handler】Mediator的更新命令處理對象UpdateCommandHandler 接收到該命令進行處理:
public class UpdateCommandHandler<TModel, TEntity> : IRequestHandler<UpdateCommand<TModel>> where TEntity : BaseEntity{private readonly CURDDomainService<TEntity> _curdDomainService;public UpdateCommandHandler(CURDDomainService<TEntity> curdDomainService){_curdDomainService = curdDomainService;}public async Task<Unit> Handle(UpdateCommand<TModel> request, CancellationToken cancellationToken){TEntity updateEntity = await _curdDomainService.Retrieve(request.Id);updateEntity = request.Model.Adapt(updateEntity);List<string> updateProperties = new List<string>();foreach (var expression in request.UpdateProperties){var member = expression.Body as MemberExpression;if (member != null)updateProperties.Add(member.Member.Name);}await _curdDomainService.Update(updateEntity, updateProperties);return Unit.Value;}}首先根據傳入的ProductId獲取到Product實體 , 然和將傳入的request Model數據,通過Mapster的Adapt方法賦值給該實體,該實體字段就有了最新的值 。
然后將傳入的要更新的屬性字段Lambda 表達式轉換為屬性字符串List,再調用領域層方法保存該實體到數據庫 。
領域層更新實體的倉儲方法如下:
public async Task<TEntity> Update(TEntity entity, IEnumerable<string> updateProperties){var entry = _dbContext.ChangeTracker.Entries<TEntity>().FirstOrDefault(p => p.Entity == entity);if (entry == null){entry = _dbContext.Set<TEntity>().Attach(entity);}entry.State = EntityState.Unchanged;foreach (var updateProperty in updateProperties){entry.Property(updateProperty).IsModified = true;}return entry.Entity;}可以看到,該方法只將要更新的字段設置為IsModified = true,即可達到更新特定字段的目的 。
輸出的SQL語句如下:
Executed DbCommand (11ms) [Parameters=[@p3='?' (DbType = Guid), @p0='?', @p1='?' (DbType = DateTime), @p2='?'], CommandType='Text', CommandTimeout='30']UPDATE "T_Product" SET "Description" = @p0, "LastModified" = @p1, "LastModifiedBy" = @p2WHERE "Id" = @p3;可以看到,SQL只更新了Description字段,LastModified和LastModifiedBy字段是OnionArch的實體數據審計特性自動加上的 。打完收工 。
找工作時間接下來又到了找工作廣告時間:
博主有15年以上的軟件技術實施經驗(Technical Leader),專注于微服務和云原生(K8s)軟件架構設計、專注于 .Net Core\Java開發和Devops構建發布 。博主10年以上的軟件交付管理經驗(Project Manager & Product Ower),致力于敏捷(Scrum)項目管理、軟件產品業務需求分析和原型設計 。博主熟練配置和使用 Microsoft Azure云 。博主為人誠懇,積極樂觀,工作認真負責 。

推薦閱讀