MassTransit - 初始化复杂消息的最佳实践
MassTransit - best practices to initialize complex messages
假设我有一个 ASP.NET Core Web API 应用程序,我的一个操作方法接收 IEnumerable<AddressModel> addresses
,其中 AddressModel
看起来像:
public class AddressModel
{
public string Street { get; set; }
public string ZipCode { get; set; }
public string City { get; set; }
public string Country { get; set; }
}
我想用它来构造一个更复杂的消息对象并通过 MassTransit 发送它 - Addresses
将是一个嵌套的 属性,我将设置更多字段:
public interface ICreateContact
{
ContactTypeEnum Type { get; }
List<IAddress> Addresses { get; }
}
public interface IAddress
{
string Street { get; }
string ZipCode { get; }
string City { get; }
string Country { get; }
}
那么,如何以最方便可读的方式创建这样的消息呢?我看到了几个选项,但它们都有缺点:
- 最直接的选项:
await _messageBus.Send<ICreateContact>(new {
Type = ContactTypeEnum.Single,
Addresses = addresses.Select(a => new
{
Street = a.Street,
ZipCode = a.ZipCode,
City = a.City,
Country = a.Country
})
});
会工作,但我不想写很多代码来分配每个 属性 并且我不能使用 Automapper,因为 ICreateContact/IAddress
中没有设置器。
- 中级 class:
public class CreateContact
{
public ContactTypeEnum Type { get; set; }
public List<Address> Addresses { get; set; }
public class Address
{
public string Street { get; set; }
public string ZipCode { get; set; }
public string City { get; set; }
public string Country { get; set; }
}
}
var command = new CreateContact
{
Type = ContactTypeEnum.Single,
Addresses = addresses.Select(a => _mapper.Map<CreateContact.Address>(a)).ToList()
};
await _messageBus.Send<ICreateContact>(command);
看起来更好,但是如果我希望它实现 ICreateContact/IAddress
怎么办,这样编译器会判断我是否错误地构造了消息?我不能那样做,因为如果我写 CreateContact : ICreateContact
,我的 Addresses
字段必须是 List<IAddress>
类型(即使我让 Address
实现 IAddress
) .
总结一下我的问题:
- 是否可以避免中间 class 并使用选项 1 自动映射属性(有或没有 Automapper)?
- 为每个服务中的消息契约创建强类型 classes 是个好主意吗?
- 如果是 - 如何处理接口类型的嵌套属性?
- 如果不是 - 如果一个消息合约有 30 个字段,其中一个被重命名并且您需要知道哪个没有文档怎么办?
首先,您不需要立即数class,您可以使用另一个嵌套的匿名类型来初始化地址列表。 MassTransit 的消息初始值设定项按约定在类型之间进行大部分映射,包括类型转换(字符串到 int、日期等)。
由于所有 属性 名称都匹配,您可以轻松使用:
await _messageBus.Send<ICreateContact>(new {
Type = ContactTypeEnum.Single,
Addresses = addresses
});
它会用输入 addresses.
中的元素初始化地址列表
其次,关于生产者的强类型 classes 的问题?这取决于。我绝对不会在消息生成器之外共享这些具体类型。如果你想要那个级别的 属性 type/name 验证,你可以做到。在您的项目中需要管理更多 classes。我已经在一些需要为工程师提供这种指导的团队中看到它完成了,但更多时候我看到团队乐于放手并在存储库级别保护合同变更。
第三,您可以使具体类型具有正确的元素类型 (IAddress) 并向其添加地址具体类型。这消除了数组类型问题,并且仍然允许您使用实现 IAddress 的地址具体类型。
第四,严重的是,不要重命名属性!如果这是一个错误,请使用工具和 grep 全局修复它以查找跨存储库的任何用法。但是一旦它投入生产,重命名它可能会破坏一切。在那种情况下,添加一个新的 属性(拼写正确,无论如何)和更新的生产者最终发送两者,一旦系统全面升级,弃用原来的 属性.
中有更多关于 MassTransit 对消息初始化程序的支持的详细信息
假设我有一个 ASP.NET Core Web API 应用程序,我的一个操作方法接收 IEnumerable<AddressModel> addresses
,其中 AddressModel
看起来像:
public class AddressModel
{
public string Street { get; set; }
public string ZipCode { get; set; }
public string City { get; set; }
public string Country { get; set; }
}
我想用它来构造一个更复杂的消息对象并通过 MassTransit 发送它 - Addresses
将是一个嵌套的 属性,我将设置更多字段:
public interface ICreateContact
{
ContactTypeEnum Type { get; }
List<IAddress> Addresses { get; }
}
public interface IAddress
{
string Street { get; }
string ZipCode { get; }
string City { get; }
string Country { get; }
}
那么,如何以最方便可读的方式创建这样的消息呢?我看到了几个选项,但它们都有缺点:
- 最直接的选项:
await _messageBus.Send<ICreateContact>(new {
Type = ContactTypeEnum.Single,
Addresses = addresses.Select(a => new
{
Street = a.Street,
ZipCode = a.ZipCode,
City = a.City,
Country = a.Country
})
});
会工作,但我不想写很多代码来分配每个 属性 并且我不能使用 Automapper,因为 ICreateContact/IAddress
中没有设置器。
- 中级 class:
public class CreateContact
{
public ContactTypeEnum Type { get; set; }
public List<Address> Addresses { get; set; }
public class Address
{
public string Street { get; set; }
public string ZipCode { get; set; }
public string City { get; set; }
public string Country { get; set; }
}
}
var command = new CreateContact
{
Type = ContactTypeEnum.Single,
Addresses = addresses.Select(a => _mapper.Map<CreateContact.Address>(a)).ToList()
};
await _messageBus.Send<ICreateContact>(command);
看起来更好,但是如果我希望它实现 ICreateContact/IAddress
怎么办,这样编译器会判断我是否错误地构造了消息?我不能那样做,因为如果我写 CreateContact : ICreateContact
,我的 Addresses
字段必须是 List<IAddress>
类型(即使我让 Address
实现 IAddress
) .
总结一下我的问题:
- 是否可以避免中间 class 并使用选项 1 自动映射属性(有或没有 Automapper)?
- 为每个服务中的消息契约创建强类型 classes 是个好主意吗?
- 如果是 - 如何处理接口类型的嵌套属性?
- 如果不是 - 如果一个消息合约有 30 个字段,其中一个被重命名并且您需要知道哪个没有文档怎么办?
首先,您不需要立即数class,您可以使用另一个嵌套的匿名类型来初始化地址列表。 MassTransit 的消息初始值设定项按约定在类型之间进行大部分映射,包括类型转换(字符串到 int、日期等)。
由于所有 属性 名称都匹配,您可以轻松使用:
await _messageBus.Send<ICreateContact>(new {
Type = ContactTypeEnum.Single,
Addresses = addresses
});
它会用输入 addresses.
中的元素初始化地址列表其次,关于生产者的强类型 classes 的问题?这取决于。我绝对不会在消息生成器之外共享这些具体类型。如果你想要那个级别的 属性 type/name 验证,你可以做到。在您的项目中需要管理更多 classes。我已经在一些需要为工程师提供这种指导的团队中看到它完成了,但更多时候我看到团队乐于放手并在存储库级别保护合同变更。
第三,您可以使具体类型具有正确的元素类型 (IAddress) 并向其添加地址具体类型。这消除了数组类型问题,并且仍然允许您使用实现 IAddress 的地址具体类型。
第四,严重的是,不要重命名属性!如果这是一个错误,请使用工具和 grep 全局修复它以查找跨存储库的任何用法。但是一旦它投入生产,重命名它可能会破坏一切。在那种情况下,添加一个新的 属性(拼写正确,无论如何)和更新的生产者最终发送两者,一旦系统全面升级,弃用原来的 属性.
中有更多关于 MassTransit 对消息初始化程序的支持的详细信息