博主最近失業在家,找工作之餘,自己動手寫了個洋蔥架構(整潔架構)解決方案,以總結和整理以前的專案經驗,起名叫OnionArch,其目的是為了更好的實現採用DDD(領域驅動分析)和命令查詢職責分離(CQRS)的洋蔥架構。
OnionArch解決方案清晰的展示了程式各分層的職責,幫助程式設計師寫出邏輯更清晰的程式碼,提高程式的高內聚、低耦合性。提高程式邏輯的可重用性,可延伸性和可測試性。
OnionArch可降低單個微服務,特別是SAAS微服務開發的複雜度,在保證軟體質量的基礎上提高軟體開發效率。
OnionArch基於最新的開源.Net 7.0 RC1, 資料庫採用PostgreSQL, 目前實現了包括多租戶在內的12個特性。詳細內容請看:https://www.cnblogs.com/xiaozhuang/p/16772485.html
本篇主要講述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的方式以防止程式設計師寫錯。
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" = @p2 WHERE "Id" = @p3;
可以看到,SQL只更新了Description欄位,LastModified和LastModifiedBy欄位是OnionArch的實體資料審計特性自動加上的。打完收工。
接下來又到了找工作廣告時間:
▪ 博主有15年以上的軟體技術實施經驗(Technical Leader),專注於微服務和雲原生(K8s)軟體架構設計、專注於 .Net Core\Java開發和Devops構建釋出。
▪ 博主10年以上的軟體交付管理經驗(Project Manager & Product Ower),致力於敏捷(Scrum)專案管理、軟體產品業務需求分析和原型設計。
▪ 博主熟練設定和使用 Microsoft Azure雲。
▪ 博主為人誠懇,積極樂觀,工作認真負責。
我家在廣州,也可以去深圳工作。做架構和專案管理都可以,希望能從事穩定行業的業務數位化轉型。有工作機會推薦的朋友可以加我微信 15920128707,微信名字叫Jerry。