Сценарий
Я использую Google Protocol Buffers в сервис-ориентированном приложении.Чтобы уменьшить объем данных, отправляемых клиентам, у меня есть «деноминации» данного DTO.Например:
message CustomerDto {
string id = 1;
// A denomination to be used in the context of listing customers.
message ListDataDto {
string name = 1;
}
// A denomination to be used when full customer data is required.
message FullDataDto {
string name = 1;
string address = 2;
string phone = 3;
// etc.
}
oneof data {
ListDataDto list = 2;
FullDataDto full = 3;
}
}
Когда клиенты извлекают данные для отображения в списке, я могу отправить им CustomerDto
объекты с полем data
, установленным в ListDataDto
.Когда им требуются полные данные клиента, я устанавливаю data
на FullDataDto
.Он работает нормально.
За API-интерфейсом клиенты шунтируются с помощью полнофункционального прото, как это:
message Customer {
string id = 1;
string name = 2;
string address = 3;
string phone = 4;
}
Что сейчас вызывает у меня боль, так это отображение между этими типами с помощью AutoMapper.
Отображение из полноценных данных в деноминационные данные
Отображение из Customer
в CustomerDto
требует дополнительной контекстной информации, чтобы знать, какой тип data
является целевым.
В настоящее время я делаю это:
config
.CreateMap<Customer, CustomerDto>()
.ForMember(
x => x.List,
options =>
{
options
.Condition((_, __, ___, ____, context) => context.Items[nameof(CustomerDto.DataOneofCase)] is Dto.DataOneofCase.List);
options
.MapFrom(x => x);
})
.ForMember(
x => x.Full,
options =>
{
options
.Condition((_, __, ___, ____, context) => context.Items[nameof(CustomerDto.DataOneofCase)] is CustomerDto.DataOneofCase.Full);
options
.MapFrom(x => x);
});
Вызовы Condition
довольно неприятные, отчасти благодаря тому факту, что C # не позволяет использовать _
в качестве сброса в этомконтекст.Я полагаю, что я мог бы, если бы до него дошло, создать метод расширения с именем ConditionalOnDataDenomination
или что-то такое, что скрывает уродливый вызов Condition
.
Однако мне интересно, не хватает ли мне чистящего средства?подход в целом.
Отображение из деноминационных данных в данные с полной достоверностью
Отображение из CustomerDto
в Customer
еще сложнее.В настоящее время я делаю это:
config
.CreateMap<CustomerDto.Types.FullDataDto, Customer>()
.ForMember(
x => x.Id,
options => options.Ignore());
config
.CreateMap<CustomerDto, Customer>()
.ConvertUsing(
(source, destination, context) =>
{
if (source.DataCase != CustomerDto.DataOneofCase.Full)
{
throw new NotSupportedException("Can only convert from full-fidelity data.");
}
var mapper = context.Mapper;
var customer = mapper.Map<Customer>(source.Full);
customer.Id = source.Id;
return customer;
});
Этот подход состоит из двух отображений:
- От объекта
FullDataDto
с полной точностью до Customer
. - От
CustomerDto
до Customer
.
Первое отображение раздражает, потому что я должен игнорировать каждое поле, определенное в CustomerDto
, которое находится за пределами FullDataDto
.В этом случае у меня есть только 1 (Id
), но на самом деле у меня есть несколько основных частей данных, которые будут всегда отправляться (например, Id
, Revision
, Status
).Таким образом, первое отображение становится многословным.Я пытался вызывать различные Ignore
методы, но ни один из них не казался подходящим.
Второе сопоставление кажется немного ... уродливым.Это правильно гарантирует, что мы сопоставляем только данные с полной точностью (для клиента не имеет смысла отправлять, например, CustomerDto
с ListDataDto
внутри него обратно на сервер).Затем он опирается на сопоставление, созданное на шаге 1, для сопоставления большей части данных.Наконец, он вручную копирует поля, которые обязательно были проигнорированы в первом отображении.Опять же, у меня есть несколько таких полей.
Вопрос
Надеюсь, это достаточно объясняет мой сценарий.Моя проблема в том, что это шаблон, который я буду использовать в своей базе кода.Поэтому я хочу, чтобы он был максимально элегантным.Кто-нибудь может указать, как можно упростить весь этот процесс с помощью AutoMapper?