How do I validate configuration with the automapper Instance API - c#

I know that using automapper's static API I can do this:
Mapper.Initialize(cfg =>
cfg.CreateMap<Source, Destination>());
Mapper.Configuration.AssertConfigurationIsValid();
but now I've switched to the instance API:
var config = new MapperConfiguration(cfg => {
cfg.AddProfile<AppProfile>();
cfg.CreateMap<Source, Dest>();
});
var mapper = config.CreateMapper();
How/where can I check if the configuration is valid using the instance API?

You can also do the validation using:
mapper.ConfigurationProvider.AssertConfigurationIsValid();

Related

Using appsettings.json in .Net 6 Worker service

Is I wanted to add a singleton service that is dependent on appsettings.
How would I do this in the .Net 6 worker service startup?
using AutoMapper;
var mapperConfig = new MapperConfiguration(mc =>
{
mc.AddProfile(new MappingProfile());
});
IMapper mapper = mapperConfig.CreateMapper();
IHost host = Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
services.AddHostedService<Worker>();
services.AddSingleton(mapper);
services.AddSingleton<IOrderNumberGenerator, OrderNumberGenerator>();
services.AddSingleton(new ServiceIwantToCall(<--I need too use apsettings here-->));
})
.Build();
await host.RunAsync();
At the moment you're trying to register an instance of ServiceIwantToCall. You can instead register a factory method to resolve one, and once it's resolved it will be a singleton. For example, this anonymous method:
services.AddSingleton(serviceProvider => {
// resolve the configuration provider from the container
IConfiguration config = serviceProvider.GetRequiredService<IConfiguration>();
// get the config value and instantiate a ServiceIwantToCall
// return the instantiated service
string someValue = config["myValueKey"];
return new ServiceIwantToCall(someValue);
});

What would be the recommended solution for this services.BuildServiceProvider().GetRequiredService?

In this Microsoft article https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-5.0 says it is not recommended to use in the ConfigureServices method, in the Startup.cs class of a .NET Core 2.2 project.
services.BuildServiceProvider().GetRequiredService()
Complete C# code below:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IMyService, MyService>();
var mappingConfig = new MapperConfiguration(cfg =>
{
cfg.AddProfile(new MappingProfileConfig(services.BuildServiceProvider().GetRequiredService<IMyService>()));
});
var mapper = mappingConfig.CreateMapper();
services.AddSingleton(mapper);
}
Has anyone got any best practice suggestion for this please?
Thank you

ForMember setting does not working when using AutoMapper in .Net Core project

I'm using AutoMapper in my .NET Core project. The default mapper function is working well, but when I use .ForMember() in myProfile.cs class, it doesn't work.
myProfile.cs just like :
public class ServiceProfile : Profile
{
public ServiceProfile()
{
CreateMap<Organization, OrganizationDto>()
.ForMember(x => x.Active, opt => opt.MapFrom(src => src.Disabled));
}
}
The configuration in startup.cs like this:
public void ConfigureServices(IServiceCollection services)
{
var mappingConfig = new MapperConfiguration(mc =>
{
mc.AddProfile(new ServiceProfile());
});
IMapper mapper = mappingConfig.CreateMapper();
services.AddSingleton(mapper);
services.AddMvc();
}
Then
input.Disabled=0;
var output = _mapper.Map<Organization>(input);
I expect the output.Active to be 0, but the actual output is null.
UPDATE:
I'm sorry, the method has no problem, it's because I'm using dapper and skip the map step.
It could be because the mapping method is CreateMap<TSource, TDestination>, source is the first generics parameter, but in the code below your mapping is from the DTO class to organization class (in the other direction) for which the mapping is probably not specified.
You may need to create mapping also for the other direction from OrganizationDto to Organization.
Update: there now is an easy way to add mapping for the other direction by adding .ReverseMap() to the end of CreateMap call chain.

Automapper: map child property with existing mapper using instance API

I'm trying to map one complex object to another using instance API:
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Student, PersonType>();
cfg.CreateMap<Professor, PersonType>();
cfg.CreateMap<Branch, BranchType>()
.ForMember(x => x.Departments, opt => opt.MapFrom(src =>
new DepartmentType[] {
new DepartmentType
{
Students = Mapper.Map<Student[], PersonType[]> (src.Students),
Professors = Mapper.Map<Professor[], PersonType[]> (src.Professors),
Name = src.DepartmentName
}
}))
.ForMember(x => x.Name, opt => opt.MapFrom(src => src.Name))
.ForAllMembers(opts => opts.Condition((src, dest, srcMember) => srcMember != null));
});
var mapper = config.CreateMapper();
var test = mapper.Map<BranchType>(source);
The problem is I don't know how to achieve this without mixing instance and static API which is not working. Here is the error:
InvalidOperationException: Mapper not initialized. Call Initialize with appropriate configuration. If you are trying to use mapper instances through a container or otherwise, make sure you do not have any calls to the static Mapper.Map methods, and if you're using ProjectTo or UseAsDataSource extension methods, make sure you pass in the appropriate IConfigurationProvider instance.
Apparently mixing of the static and instance based approaches is not allowed:
Students = Mapper.Map<Student[], PersonType[]> (src.Students)
How to use existing map to apply it to a property of the complex object with instance API?

Using Automapper static API in the latest release?

I've upgraded Automapper from 4.2.1 to 5.0.0. I'm using the static API in a WebApi2 project and I'm trying to get the mapping to work, so I tried following this SO answer.
So I changed the code to the following:
public static class AutoMapping
{
public static void Config()
{
Mapper.Initialize(main =>
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMissingTypeMaps = true;
cfg.CreateMap<MyModel, MyDto>().ReverseMap();
});
config.AssertConfigurationIsValid();
});
}
}
The above is called from Global.asax.
However, I get exception:
Mapper not initialized. Call Initialize with appropriate configuration.
What is the correct way to initialize Automapper, and do I need to change all my controllers now for mapping?
EDIT1
Firstly, the code above must be:
Mapper.Initialize(cfg =>
{
cfg.CreateMissingTypeMaps = true;
cfg.CreateMap<MyModel, MyDto>().ReverseMap();
});
Mapper.Configuration.AssertConfigurationIsValid();
Secondly, the problem might be in the following method which I use to ignore missing properties:
public static IMappingExpression<TSource, TDestination> IgnoreUnmapped<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
{
var typeMap = Mapper.Configuration.FindTypeMapFor<TSource, TDestination>();
if (typeMap != null)
{
foreach (var unmappedPropertyName in typeMap.GetUnmappedPropertyNames())
{
expression.ForMember(unmappedPropertyName, opt => opt.Ignore());
}
}
return expression;
}
I'm assuming 'Mapper.Configuration' is not yet configured because the above method is called within Initialize which configures the mapping.
Is there an existing method within Automapper itself which I can use instead of the above?
EDIT2
Would the following syntax work?
cfg.CreateMap<MyModel, MyDto>().ReverseMap().ForAllMembers(opt => opt.Ignore());
Actually your code does nothing now. You have to change it like this:
public static class AutoMapping
{
public static void Config()
{
Mapper.Initialize(cfg =>
{
cfg.CreateMissingTypeMaps = true;
cfg.CreateMap<MyModel, MyDto>().ReverseMap();
});
Mapper.AssertConfigurationIsValid();
}
}
UPD (after EDIT1):
Try to use expression.TypeMap instead of Mapper.Configuration.FindTypeMapFor<TSource, TDestination>()
Maybe this helps:
Setup the Configuration:
var config = new MapperConfiguration(cfg => cfg.CreateMap<Order, OrderDto>());
Then where the mapping should take place:
var mapper = new Mapper(config);
OrderDto dto = mapper.Map<OrderDto>(order);
You could also expose the 'config' as a static property, and use that in your project. There's also an alternative to create a static 'Mapper' property that is configured. Then you can use that static 'Mapper' property in your project.

Categories

Resources