Карта AutoMapper из коллекции IConfigurationSection со сложным отображением - PullRequest
2 голосов
/ 06 июня 2019

Это мой второй вопрос по теме. Мой первый вопрос здесь . Роман Марусик дал простой ответ на вопрос. Однако в действительности у меня более сложный случай, и он станет еще сложнее. Поэтому мне нужно использовать AutoMapper для сопоставления конфигурации (хотя я был бы очень рад и удивлен, если бы связывание по умолчанию также справилось с этим).
Вот мой настоящий JSON с моделями следуют:

{
  "startupConfig": {
    "noSubscription": {
      "calls": [
        {
          "percentage": 30,
          "techPriority": 1,
          "timePriority": 2
        },
        {
          "percentage": 30,
          "techPriority": 1,
          "timePriority": 2
        }
      ],
      "profiles": [
        {
          "type": "startup",
          "percentage": 20,
          "techPriority": 2,
          "timePriority": 1
        }
      ]
    }
  }
}
namespace FeedService.FeedConfigurations
{
    public class FeedConfiguration
    {
        public ICollection<CallConfiguration> CallsConfig { get; set; }

        public ICollection<ProfileConfiguration> ProfilesConfig { get; set; }
    }

    public class ProfileConfiguration
    {
        public CompanyTypeEnum CompanyTypeEnum { get; set; }
        public int Percentage { get; set; }
        public int TechPriority { get; set; }
        public int TimePriority { get; set; }
    }
    public class CallConfiguration
    {
        public int Percentage { get; set; }
        public int TechPriority { get; set; }
        public int TimePriority { get; set; }
    }
}

Здесь, как вы видите, мне нужно, чтобы значение конфигурации profiles:type было сопоставлено свойству с типом enum по имени. По-видимому, связыватель конфигурации по умолчанию не делает магию для меня. Я также не хотел бы менять тип свойства на строку или тип значения на целое число. Поэтому мне все еще нужен ответ на исходный вопрос для сопоставления с AutoMapper (где укороченного примера достаточно, чтобы расширить его для второй части).
=== Оригинальный вопрос скопирован для удобства ===
У меня есть следующий файл конфигурации json:

{
  "startupConfig": {
    "noSubscription": {
      "calls": [
        {
          "percentage": 30,
          "techPriority": 1,
          "timePriority": 2
        },
        {
          "percentage": 30,
          "techPriority": 1,
          "timePriority": 2
        }
      ]
    }
  }
}

А вот мое чтение кода из файла:

var config = _mapper.Map<FeedConfiguration>(_configuration
    .GetSection("startupConfig").GetChildren()
    .FirstOrDefault(cc => cc.Key == "noSubscription")?.GetChildren());

Однако отображение не работает. Вот код конфигурации сопоставления:

CreateMap<IEnumerable<IConfigurationSection>, CallConfiguration>()
    .ForMember(cc => cc.Percentage,
        mo => mo.MapFrom(css => css.FirstOrDefault(cs => cs.Key == "percentage").Value))
    .ForMember(cc => cc.TechPriority,
        mo => mo.MapFrom(css => css.FirstOrDefault(cs => cs.Key == "techPriority").Value))
    .ForMember(cc => cc.TimePriority,
        mo => mo.MapFrom(css => css.FirstOrDefault(cs => cs.Key == "timePriority").Value));
CreateMap<IEnumerable<IConfigurationSection>, FeedConfiguration>()
    .ForMember(fc => fc.CallsConfig,
        mo => mo.MapFrom(css => css.FirstOrDefault(cs => cs.Key == "calls").GetChildren()));

Классы, на которые я отображаю:

namespace FeedService.FeedConfigurations
{
    public class FeedConfiguration
    {
        public ICollection<CallConfiguration> CallsConfig { get; set; }
    }

    public class CallConfiguration
    {
        public int Percentage { get; set; }
        public int TechPriority { get; set; }
        public int TimePriority { get; set; }
    }
}

И вот исключение, которое я получаю:

AutoMapper.AutoMapperConfigurationException: 
Unmapped members were found. Review the types and members below.
Add a custom mapping expression, ignore, add a custom resolver, or modify the source/destination type
For no matching constructor, add a no-arg ctor, add optional arguments, or map all of the constructor parameters
=============================================================================================================
AutoMapper created this type map for you, but your types cannot be mapped using the current configuration.
IConfigurationSection -> CallConfiguration (Destination member list)
Microsoft.Extensions.Configuration.IConfigurationSection -> FeedService.FeedConfigurations.CallConfiguration (Destination member list)

Unmapped properties:
Percentage
TechPriority
TimePriority

Был бы очень благодарен за вашу помощь!

1 Ответ

1 голос
/ 07 июня 2019

Ваш профиль должен выглядеть следующим образом

namespace FeedService.FeedConfigurations
{
    public class FeedConfigurationMappingProfile : Profile
    {
        public FeedConfigurationMappingProfile()
        {
            CreateMap<IConfigurationSection, FeedConfiguration>()
                .ForMember(fc => fc.Calls,
                    mo => mo.MapFrom(fc => fc.Get<FeedConfiguration>().Calls));

            CreateMap<IConfigurationSection, CallConfiguration>()
                .ForMember(cc => cc.Percentage,
                    mo => mo.MapFrom(css => css.GetChildren().FirstOrDefault(cs => cs.Key == "percentage").Value))
                .ForMember(cc => cc.TechPriority,
                    mo => mo.MapFrom(css => css.GetChildren().FirstOrDefault(cs => cs.Key == "techPriority").Value))
                .ForMember(cc => cc.TimePriority,
                    mo => mo.MapFrom(css => css.GetChildren().FirstOrDefault(cs => cs.Key == "timePriority").Value));
        }

    }
}

, а затем использовать маппер

_mapper.Map<FeedConfiguration>(_configuration.GetSection("startupConfig:noSubscription"));

РЕДАКТИРОВАТЬ (дополнительная опция)

CreateMap<IConfigurationSection, FeedConfiguration>()
                .ForMember(fc => fc.Calls,
                    mo => mo.MapFrom(fc => fc.GetChildren().FirstOrDefault(fd=>fd.Key == "calls").GetChildren()));
...