Несколько AutoMapper.Configure() в Global.asax
Я использую AutoMapper для сопоставления между объектами DTO и моими бизнес-объектами. У меня два файла AutoMapperConfiguration.cs - один в моем слое сервиса и еще один в моем веб-слое api.
Как показано в ответе по следующей ссылке
Где разместить AutoMapper.CreateMaps?
Я вызываю Configure() обоих этих файлов в классе Global.asax
AutoMapperWebConfiguration.Configure();
AutoMapperServiceConfiguration.Configure();
но похоже, что мой вызов службы настройки (второй вызов) переписывает сопоставления уровня веб-api (первый вызов), и я получаю исключение, говорящее, что отображение отсутствует.
Если я отменил вызов Configure, чтобы выглядеть так:
AutoMapperServiceConfiguration.Configure();
AutoMapperWebConfiguration.Configure();
Я не получаю исключение для сопоставления веб-api, но я получаю ту же ошибку отображения для уровня сервиса.
Я делаю что-то неправильно, потому что это четко обозначено как ответ в вышеупомянутой ссылке?
Здесь мой код:
public static class AutoMapperServiceConfiguration
{
public static void Configure()
{
Mapper.Initialize(x =>
{
x.AddProfile<CmciFlowTestToGenericFlowTestSimpleMappingProfile>();
x.AddProfile<FsrsFlowTestToGenericFlowTestSimpleMappingProfile>();
});
}
}
public class FsrsFlowTestToGenericFlowTestSimpleMappingProfile : Profile
{
protected override void Configure()
{
Mapper.CreateMap<FsrsFlowTest, GenericFlowTest>()
.ConvertUsing<FsrsFlowTestToGenericFlowTestSimpleConverter>();
}
}
public class FsrsFlowTestToGenericFlowTestSimpleConverter : TypeConverter<FsrsFlowTest, GenericFlowTest>
{
protected override GenericFlowTest ConvertCore(FsrsFlowTest source)
{
if (source == null)
{
return null;
}
return new GenericFlowTest
{
FlowTestDate = source.FlowTestDates,
StaticPsi = source.HydrantStaticPsi.ToString(),
ResidualPsi = source.HydrantResidualPsi.ToString(),
TotalFlow = source.NffGallonsPerMinute.ToString(),
FlowTestLocation = source.FsrsFlowTestLocations.Any()
? source.FsrsFlowTestLocations.First().LocationDescription
: null
};
}
public class CmciFlowTestToGenericFlowTestSimpleMappingProfile : Profile
{
protected override void Configure()
{
Mapper.CreateMap<CmciFlowTest, GenericFlowTest>()
.ConvertUsing<CmciFlowTestToGenericFlowTestSimpleConverter>();
}
}
public class CmciFlowTestToGenericFlowTestSimpleConverter : TypeConverter<CmciFlowTest, GenericFlowTest>
{
protected override GenericFlowTest ConvertCore(CmciFlowTest source)
{
if (source == null)
{
return null;
}
return new GenericFlowTest
{
FlowTestDate = source.FlowTestDates,
StaticPsi = source.HydrantStaticPsi.ToString(),
ResidualPsi = source.HydrantResidualPsi.ToString(),
TotalFlow = source.CalculatedHydrantGallonsPerMinute.ToString(),
FlowTestLocation = source.StaticLocationHydrantFlowPSI
};
}
}
public static class AutoMapperWebConfiguration
{
public static void Configure()
{
Mapper.Initialize(x =>
{
x.AddProfile<ServiceToWebApiMappingProfile>();
x.AddProfile<WebApiToServiceMappingProfile>();
});
}
}
public class ServiceToWebApiMappingProfile : Profile
{
protected override void Configure()
{
Mapper.CreateMap<ServiceFlowTest, FlowTest>();
}
}
public class WebApiToServiceMappingProfile : Profile
{
protected override void Configure()
{
Mapper.CreateMap<PropertyAddress, ServicePropertyAddress>();
}
}
Чтобы обойти эту проблему, я добавляю профили службы в классе AutoMapperWebConfiguration и вызываю только AutoMapperWebConfiguration.Configure() в global.asax.
Ответы
Ответ 1
Вызов Mapper.Initialize reset сопоставителя, поэтому все, что было раньше, стирается.
Переместите вызовы в AddProfile в один Mapper.Initialize, и все должно быть хорошо:
Mapper.Initialize(x =>
{
x.AddProfile<CmciFlowTestToGenericFlowTestSimpleMappingProfile>();
x.AddProfile<FsrsFlowTestToGenericFlowTestSimpleMappingProfile>();
x.AddProfile<ServiceToWebApiMappingProfile>();
x.AddProfile<WebApiToServiceMappingProfile>();
});
Ответ 2
Ответ на @GruffBunny правильный, но я попытался сделать его немного более аккуратным для масштабируемости (например, если у вас много методов сложного Mapper.Initialize()
и может добавить в будущем больше).
Я сделал это, выполнив следующую структуру в всех моих AutoMapperConfiguration.cs
файлах:
Извлеките Action<IConfiguration>
из существующего метода Mapper.Initialize()
в общедоступное свойство
Я называю это ConfigAction
в каждом из них.
public static Action<IConfiguration> ConfigAction = new Action<IConfiguration>(x =>
{
x.AddProfile<SomeProfile>();
x.AddProfile<SomeOtherProfileProfile>();
//... more profiles
});
Это позволяет вам вызывать действие из любого места, где вам нужно позвонить Mapper.Initialize
.
Mapper.Initialize()
внутри Configure()
теперь просто ссылается на это свойство
public static void Configure()
{
Mapper.Initialize(ConfigAction);
}
Затем вы можете вызывать все ваши разные ConfigAction
в одном, централизованном вызове Mapper.Initialize()
AutoMapperConfiguration.Configure()
в Application_Start()
становится
Mapper.Initialize(x =>
{
Project1.AutoMapperConfiguration.ConfigAction.Invoke(x);
Project2.AutoMapperConfiguration.ConfigAction.Invoke(x);
Project3.AutoMapperConfiguration.ConfigAction.Invoke(x);
//... keep adding as your project grows
});
Это устраняет необходимость копирования и вставки тела метода из каждого отдельного вызова Mapper.Initialize()
в ваш центральный вызов. DRY и все такое.
Ответ 3
Обновление @theyetiman блестящего ответа для AutoMapper 5.2 и .NET Core.
public static class AutoMapperConfiguration
{
public static void Configure()
{
Mapper.Initialize(ConfigAction);
}
public static Action<IMapperConfigurationExpression> ConfigAction = cfg =>
{
x.AddProfile<SomeProfile>();
x.AddProfile<SomeOtherProfileProfile>();
};
}
API или веб-запуск.
public Startup(IHostingEnvironment env)
{
Mapper.Initialize(x =>
{
Project1.AutoMapperConfiguration.ConfigAction.Invoke(x);
Project2.AutoMapperConfiguration.ConfigAction.Invoke(x);
Project3.AutoMapperConfiguration.ConfigAction.Invoke(x);
});
}