为什么应该用record来定义DTO(续)
前言
上次,我們介紹了因為DTO的“不變性”,應該用record來定義DTO。
今天,我們來說明用record來定義DTO的另一個好處。
問題
首先,我們實現一個Controler,代碼如下:
[ApiController] [Route("[controller]")] public?class?UserController?:?ControllerBase {private?readonly?IMediator?_mediator;public?UserController(IMediator?mediator){this._mediator?=?mediator;}[HttpGet("{id}")]public?async?Task<UserDto>?GetById(int?id){var?request?=?new?GetUserByIdQuery?{?Id?=?id?};var?result?=?await?this._mediator.Send(request);return?result;} }public?class?UserDto {public?int?Id?{?get;?set;?}public?string?Name{?get;?set;?} }public?class?GetUserByIdQuery?:?IRequest<UserDto> {public?int?Id?{?get;?set;?} }這里,IRequest<T>可以認為是DTO。
然后,我們編寫測試用例:
[Fact] public?async?void?Test1() {var?mediatorMock?=?new?Mock<IMediator>();var?request?=?new?GetUserByIdQuery?{?Id?=?1};var?expectedUser?=?new?UserDto?{?Id?=?1,?Name?=?"My?IO"?};mediatorMock.Setup(x?=>?x.Send(request,?default(CancellationToken))).Returns(Task.FromResult(expectedUser));var?controller?=?new?UserController(mediatorMock.Object);var?result?=?await?controller.GetById(1);Assert.Equal(expectedUser,?result); }我們Mock了IMediator,期望它執行Send后返回expectedUser。
看起來都沒有問題,但是測試執行失敗:
調試代碼,可以看到傳遞的參數是正確的,但是返回值是null:
這說明實際沒有命中mediatorMock.Setup中的方法。
這是為什么呢?
原因
原因其實是,x.Send(request, default(CancellationToken))表示必須完全匹配才能返回指定的結果,但是request和GetById方法中創建的request其實是2個不同的實例,.NET并不認為它們相等。
雖然可以修改mediatorMock.Setup方法來修復測試。
但對于我來說,屬性值完全相同的DTO應該就是相等的,可以讓類實現值相等性來解決:
public?class?GetUserByIdQuery?:?IRequest<UserDto> {public?int?Id?{?get;?set;?}public?override?bool?Equals(object?obj)?=>?this.Equals(obj?as?GetUserByIdQuery);public?bool?Equals(GetUserByIdQuery?p){if?(p?is?null){return?false;}if?(Object.ReferenceEquals(this,?p)){return?true;}if?(this.GetType()?!=?p.GetType()){return?false;}return?Id?==?p.Id;}public?override?int?GetHashCode()?=>?Id.GetHashCode(); }但是,為每個DTO重寫Equals和GetHashCode也不是個事。
record的相等性
其實,更簡單的解決方法是修改定義如下:
public?record?GetUserByIdQuery?:?IRequest<UserDto> {public?int?Id?{?get;?set;?} }你會發現測試通過了。
這是因為,record在設計上就具備創建具有值相等數據類型的能力,編譯器會自動生成樣板代碼:
結論
在本文中,我們介紹了通過使用record類型,可以大大簡化定義實現值相等性DTO的代碼量。
如果你覺得這篇文章對你有所啟發,請關注我的個人公眾號”My IO“,記住我!
總結
以上是生活随笔為你收集整理的为什么应该用record来定义DTO(续)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: EF Core 异步编程注意要点
- 下一篇: NET流行高性能JSON框架-Json.