欢迎访问 生活随笔!

生活随笔

当前位置: 首页 >

为什么应该用record来定义DTO(续)

发布时间:2023/12/4 49 豆豆
生活随笔 收集整理的这篇文章主要介绍了 为什么应该用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(续)的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。