I am using AutoMapper to map between DTO objects and my business objects. I've two AutoMapperConfiguration.cs files - one in my service layer and another one in my web api layer.
As shown in the answer at the following link Where to place AutoMapper.CreateMaps?
I am calling the Configure() of both these files in my Global.asax class
AutoMapperWebConfiguration.Configure();
AutoMapperServiceConfiguration.Configure();
but it seems like the my Service Configure call (the second call) is overwriting the mappings of the web api layer (the first call) and I get an exception saying the Mapping is missing.
If I reverse the Configure calls to look like this
AutoMapperServiceConfiguration.Configure();
AutoMapperWebConfiguration.Configure();
I don't get the exception for web api mapping but I get the same mapping exception for the Service layer.
Am I doing something wrong because this is clearly marked as an answer in the above stack overflow link?
Here's my code:
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>();
}
}
To get around this issue, I am adding the service profiles in the AutoMapperWebConfiguration class and only calling AutoMapperWebConfiguration.Configure() in global.asax.
To test our configuration, we simply create a unit test that sets up the configuration and executes the AssertConfigurationIsValid method: var configuration = new MapperConfiguration(cfg => cfg. CreateMap<Source, Destination>()); configuration. AssertConfigurationIsValid();
AutoMapper in C# is a library used to map data from one object to another. It acts as a mapper between two objects and transforms one object type into another. It converts the input object of one type to the output object of another type until the latter type follows or maintains the conventions of AutoMapper.
AutoMapper is a simple library that helps us to transform one object type into another. It is a convention-based object-to-object mapper that requires very little configuration. The object-to-object mapping works by transforming an input object of one type into an output object of a different type.
automapper Profiles Basic Profile Profiles permit the programmer to organize maps into classes, enhancing code readability and maintainability. Any number of profiles can be created, and added to one or more configurations as needed. Profiles can be used with both the static and instance-based APIs.
The calls to Mapper.Initialize reset the mapper so everything that's gone before is wiped.
Move the calls to AddProfile into one Mapper.Initialize call and all should be well:
Mapper.Initialize(x =>
{
x.AddProfile<CmciFlowTestToGenericFlowTestSimpleMappingProfile>();
x.AddProfile<FsrsFlowTestToGenericFlowTestSimpleMappingProfile>();
x.AddProfile<ServiceToWebApiMappingProfile>();
x.AddProfile<WebApiToServiceMappingProfile>();
});
@GruffBunny's answer is correct, but I tried to make it a bit neater for scalability (e.g. if you have many, complex Mapper.Initialize()
methods, and might add more in the future).
I did this by implementing the following structure in all of my AutoMapperConfiguration.cs
files:
Action<IConfiguration>
from your existing Mapper.Initialize()
method into a public propertyI call it ConfigAction
in each one.
public static Action<IConfiguration> ConfigAction = new Action<IConfiguration>(x =>
{
x.AddProfile<SomeProfile>();
x.AddProfile<SomeOtherProfileProfile>();
//... more profiles
});
This allows you to invoke the action from anywhere you need to call Mapper.Initialize
.
Mapper.Initialize()
inside Configure()
now just references this propertypublic static void Configure()
{
Mapper.Initialize(ConfigAction);
}
You can then invoke all your different ConfigAction
s in your single, centralized call to Mapper.Initialize()
AutoMapperConfiguration.Configure()
in Application_Start()
becomesMapper.Initialize(x =>
{
Project1.AutoMapperConfiguration.ConfigAction.Invoke(x);
Project2.AutoMapperConfiguration.ConfigAction.Invoke(x);
Project3.AutoMapperConfiguration.ConfigAction.Invoke(x);
//... keep adding as your project grows
});
This eliminates the need to copy-and-paste the method body from each separate Mapper.Initialize()
call into your central call. DRY and all that.
To update @theyetiman's brilliant answer for AutoMapper 5.2 & .NET Core.
public static class AutoMapperConfiguration
{
public static void Configure()
{
Mapper.Initialize(ConfigAction);
}
public static Action<IMapperConfigurationExpression> ConfigAction = cfg =>
{
cfg.AddProfile<SomeProfile>();
cfg.AddProfile<SomeOtherProfileProfile>();
};
}
API or web startup.
public Startup(IHostingEnvironment env)
{
Mapper.Initialize(x =>
{
Project1.AutoMapperConfiguration.ConfigAction.Invoke(x);
Project2.AutoMapperConfiguration.ConfigAction.Invoke(x);
Project3.AutoMapperConfiguration.ConfigAction.Invoke(x);
});
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With