Использование преобразователя пользовательских значений AutoMapper для нескольких членов

#c# #asp.net-core #automapper

#c# #asp.net-ядро #automapper

Вопрос:

Я пытаюсь преобразовать данные элемента, расположенные в исходном типе, в элемент целевого типа, используя методологию преобразования пользовательских значений. Я максимально внимательно следил за документацией на веб-сайте AutoMapper по адресу http://docs.automapper.org/en/stable/Custom-value-resolvers.html . Я тщательно проверил, чтобы убедиться, что я следую приведенному примеру, однако с одним отличием. Элемент в источнике имеет другой тип, чем элемент в пункте назначения.

IValueResolver.cs

 public interface IValueResolver<in TSource, in TDestination, in TSourceMember, TDestinationMember>
{
    TDestinationMember Resolve(TSource source, TDestination destination, TSourceMember sourceMember, TDestinationMember destinationMember, ResolutionContext context);
}
  

DisplayIdResolver.cs

 public class DisplayIdResolver : IValueResolver<object, object, int, string>
{
    // Seems like this may be useless unless its mapping to a destination member of the same type as the source member
    public string Resolve(object source, object destination, int sourceMember, string destinationMember, ResolutionContext context)
    {
        if (source.GetType().Equals(typeof(Developer)))
        {
            return IdentifierType.ConvertToAppId(sourceMember, IdentifierType.DEVELOPER.Code);
        }
        . . . etcetera . . .
     }
}
  

AutoMapperProfile.cs

 public class AutoMapperProfile : Profile
{
    public AutoMapperProfile()
    {
        CreateMap<Developer, DeveloperDetailModel>().ForMember(dest => dest.DisplayId, opt => opt.MapFrom<DisplayIdResolver, int>(src => src.DeveloperId));
    }
}
  

Из этой одной строки кода в конструкторе AutoMapperProfile я получаю следующую ошибку во время компиляции:

Тип «Веб-сайт.Служебные программы.Автоматический преобразователь.DisplayIdResolver’ не может использоваться в качестве параметра типа ‘TValueResolver’ в общем типе или методе ‘IMemberConfigurationExpression<Разработчик, DeveloperDetailModel, string>.MapFrom<TValueResolver, TSourceMember>(выражение<Функция<Разработчик, TSourceMember>>)’. Неявное преобразование ссылок с веб-сайта отсутствует.Служебные программы.Автоматический преобразователь.DisplayIdResolver» в «AutoMapper.IMemberValueResolverModels.DAL.DTO.Developer, веб-сайт.Модели.DeveloperDetailModel, int, string>’.

Из примера, показанного на веб-сайте AutoMapper, похоже, что метод пользовательского преобразователя значений Resolve разрешает сопоставление двух элементов, только если они одного типа. Но я убедился, что тип, возвращаемый методом преобразователя, действительно является типом, который ожидает конечный член.

Очень вероятно, что меня смущают обобщения и что я совершаю простую ошибку, но я не смог понять, что это может быть, я потерял здесь всю объективность. Возможно ли то, что я делаю, или это не то, для чего был разработан преобразователь?

Редактировать

Г-н Лучан Баргаоану указал, что существует альтернативный сайт для документации AutoMapper по следующей ссылке. Поскольку в этой новой документации нет примера интерфейса, могу ли я предположить, что интерфейс для IValueConverter :

 public interface IValueConverter<in TSourceMember, TDestinationMember>
{
    TDestinationMember Resolve(TSourceMember sourceMember, TDestinationMember destinationMember, ResolutionContext context);
}
  

If that is the case, then this will be of little utility as I do need the source object type in order to map the source members to destination members. I’ve also just tried using it with an additional object parameter as the source, but the ConvertUsing method does not have an overload that takes 3 arguments. I am not seeing how I can accomplish this with the new documentation either.

EDIT 2

It was suggested that I use IMemberValueResolver. So this is what I did:

IMemberValueResolver.cs

 public interface IMemberValueResolver<in TSource, in TDestination, TSourceMember, TMember>
{
    TMember Resolve(TSource source, TDestination destination, TSourceMember sourceMember, TMember destinationMember, ResolutionContext context);
}
  

DisplayIdResolver.cs

 public class DisplayIdResolver : IMemberValueResolver<object, object, int, string>
{
    public string Resolve(object source, object destination, int sourceMember, string destinationMember, ResolutionContext context)
    {
        if (source.GetType().Equals(typeof(Developer)))
        {
            return IdentifierType.ConvertToDisplayId(sourceMember, IdentifierType.DEVELOPER.Code);
        }

        ... more type comparisons here, etc ...

        return null;
    }
}
  

AutoMapperProfile.cs

 public class AutoMapperProfile : Profile
{
    public AutoMapperProfile()
    {
        CreateMap<Developer, DeveloperDetailModel>().ForMember(dest => dest.DisplayId, opt => opt.MapFrom<DisplayIdResolver, int>(src => src.DeveloperId));
    }
}
  

Which results in the compile time error:

The type ‘WebSite.Utilities.AutoMapper.DisplayIdResolver’ cannot be
used as type parameter ‘TValueResolver’ in the generic type or method
‘IMemberConfigurationExpression<Developer, DeveloperDetailModel,
string>.MapFrom<TValueResolver,
TSourceMember>(Expression<Func<Developer,
TSourceMember>>)’. There is no implicit reference conversion
from ‘WebSite.Utilities.AutoMapper.DisplayIdResolver’ to
‘AutoMapper.IMemberValueResolver<WebSite.Models.DAL.DTO.Developer,
WebSite.Models.DeveloperDetailModel, int, string>’.

Что очень плохо, потому что я действительно хочу использовать все возможности AutoMapper. Я последовал примеру для более простого случая использования IValueResolver , и это тоже не сработало. Я использую ASP.Net Ядро 2.2 и AutoMapper.Extensions.Microsoft.DependencyInjection библиотека для Automapper, а также класс Profile, который я только что включил в этот пример. Я не могу заставить работать простейший случай на веб-сайте AutoMapper, и я также не могу заставить работать более сложный IMemberValueResolver случай, точно следуя документации, представленной на веб-сайте. На веб-сайте приведен пример класса реализации, но нет примера интерфейса. Вызов MapFrom в примере не кажется правильным, поскольку никакая комбинация аргументов не предоставляет мне синтаксис, который .ЧИСТЫЙ компилятор примет.

Пожалуйста, обратите внимание, что я знаю, что в ошибке указано следующее:

не может использоваться в качестве параметра типа ‘TValueResolver’

Тем не менее, как вы можете ясно видеть, я нигде не использую IValueResolver . Я просто реализую IMemberValueResolver интерфейс в классе DisplayIdResolver и следую примеру с веб-сайта AutoMapper относительно того, как это будет указано в вызове CreateMap профиля.

ПРАВКА 3

Используя ссылку на примеры на Github, я смог найти точный сценарий тестирования, который я пытаюсь выполнить. Однако нет никакой разницы между примером на Github и кодом, который я пытаюсь скомпилировать:

     public class CustomResolver : IMemberValueResolver<object, object, int, int>
    {
        public int Resolve(object s, object d, int source, int dest, ResolutionContext context)
        {
            return source   5;
        }
    }

    protected override MapperConfiguration Configuration { get; } = new MapperConfiguration(cfg =>
    {
        cfg.CreateMap<Source, Dest>()
            .ForMember(dto => dto.SomeValue,
                opt => opt.MapFrom<CustomResolver, int>(m => m.SomeOtherValue));

    });
  

Как вы можете видеть — они используют общий интерфейс для IMemberValueResolver использования подписи, что я и делаю, за исключением того, что возвращаемое значение представляет собой строку, а не целое число, поэтому оно становится (как я писал ранее):

 public interface IMemberValueResolver<in TSource, in TDestination, TSourceMember, TMember>
{
    TMember Resolve(TSource source, TDestination destination, TSourceMember sourceMember, TMember destinationMember, ResolutionContext context);
}
  

Однако вызов его таким же образом, как и в примере, приводит к некомпилированию кода, как упоминалось ранее (я дважды опубликовал ошибку трассировки стека):

 CreateMap<Developer, DeveloperDetailModel>().ForMember(dest => dest.DisplayId, opt => opt.MapFrom<DisplayIdResolver, int>( src => src.DeveloperId));
  

Комментарии:

1. docs.automapper.org/en/latest/Value-converters.html

2. Хорошо, если вам нужен тип источника, преобразователь значений не поможет. Но то, что вы здесь делаете, путает IMemberValueResolver с IValueResolver. Вам нужно будет выбрать один 🙂

3. Но если вам нужен тип источника, кажется, проще иметь отдельные преобразователи. Зачем повторно использовать их, а затем проверять тип источника.

4. «IMemberValueResolver с IValueResolver» — я не знал, что я это делаю, я просто следовал примеру по первой ссылке документации. Упоминание IMemberValueResolver есть только в трассировке стека из ошибки сборки. Какие методы, которые я использую выше в классе AutoMapperProfile, вызывают IMemberValueResolver?

5. Мне неясно, что вы подразумеваете под «зачем повторно использовать их, а затем проверять тип источника». Что вы имеете в виду под «повторным использованием»? Из моего кода я не вижу, где я что-либо использую повторно. Я думаю, что я использую преобразователь только один раз.