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.
Related
I am trying to map from a db-model to a view model. For one property I need a custom value resolver.
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Model.Db.Kontoauszug, KontoauszugDetailViewModel>()
.ForMember(dest => dest.IsTeamleiter, opt => opt.MapFrom<KontoauszugIsTeamleiterResolver>());
});
var mapper = new Mapper(config);
return mapper.Map<KontoauszugDetailViewModel>(kontoauszug);
The custom value resolver is dependent on a service as you can see here:
public class KontoauszugIsTeamleiterResolver : IValueResolver<Model.Db.Kontoauszug, KontoauszugDetailViewModel, bool>
{
private readonly ISysParamService sysParamService;
public KontoauszugIsTeamleiterResolver(ISysParamService sysParamService)
{
this.sysParamService = sysParamService;
}
public bool Resolve(Model.Db.Kontoauszug source, KontoauszugDetailViewModel destination, bool destMember, ResolutionContext context)
{
var teamleiter = this.sysParamService.GetParamValueAs<string>(KontoauszugSysParamConst.KONTOAUSZUG_TEAMLEITER_MANUMMERN).Split(";").ToList();
return teamleiter.Contains(source.MitarbeiterNr);
}
}
Unfortuantely, when running this code throws a exception that the valueresolver does not have a parameterless constructor.
I'm using the standard .net core dependency injection and in my Startup.cs I'm registering the automappers via
services.AddAutoMapper(typeof(Startup));
I've also tried to explicitly register the value resolver:
services.AddScoped<IValueResolver<Model.Db.Kontoauszug, KontoauszugDetailViewModel, bool>, KontoauszugIsTeamleiterResolver>();
But it does not work.
What am I doing wrong that this exception is thrown?
Thank in advance
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.
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<SomeSourceModel, SomeDestinationModel>();
});
config.AssertConfigurationIsValid();
var mapper = config.CreateMapper();
I am repeating these code in the project. Thinking to create a common interface IMapper so that I can invoke whenever it is needed to be used.
The solution I create is
private IMapper Mapper(TSource source, TDestination dest)
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<source, dest>();
});
config.AssertConfigurationIsValid();
return config.CreateMapper();
}
It doesn't work. The problem is I can't pass source model and destination model as parameters in this way. How to solve this?
Update 1:
As #12seconds mentioned, I start initializing MapperConfigration in Global.asax.cs
In App_Start folder, I created
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<SourceModel1, DestinationModel1>();
CreateMap<SourceModel2, DestinationModel2>();
CreateMap<SourceModel3, DestinationModel3>();
CreateMap<SourceModel4, DestinationModel4>();
CreateMap<SourceModel5, DestinationModel5>();
Mapper.AssertConfigurationIsValid();
}
}
In Global.asax.cs
public class AutoMapperConfiguration
{
public static void Configure()
{
Mapper.Initialize(x =>
{
x.AddProfile<MappingProfile>();
});
}
}
And then I tried to call AutoMapperConfiguration.Configure(); in several places. When I start running the App, I got same error messages:
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.
Where I suppose to call AutoMapperConfiguration.Configure();? Did I miss something?
Version 5.0.x +
public class AutoMapperConfiguration
{
public static void Configure()
{
Mapper.Initialize(x =>
{
x.AddProfile<MappingProfile>();
});
Mapper.AssertConfigurationIsValid();
}
}
The problem solved. Mapper.AssertConfigurationIsValid(); should be executed after Mapper initialized.
public class AutoMapperConfiguration
{
public static void Configure()
{
Mapper.Initialize(x =>
{
x.AddProfile<MappingProfile>();
});
Mapper.Configuration.AssertConfigurationIsValid();
}
}
Hi I am creating the following Web API method which returns data transfer object. The new version of the Auto Mapper works differently. Below is the old way, could somebody help me with the new approach
This is an example of AutoMapping using an old AutoMapper
public IEnumerable<NotiticationDto> GetNewNotifications()
{
var userid = User.Identity.GetUserId();
var userNotifications = _context.UserNotifications
.Where(un => un.UserId == userid)
.Select(un => un.Notification)
.Include(n => n.Gig.Artist)
.ToList();
Mapper.CreateMap<ApplicationUser, UserDto>();
Mapper.CreateMap<Gig, GigDto>();
Mapper.CreateMap<Notitication, NotiticationDto>();
});
return userNotifications.Select(Mapper.Map<Notitication>,<NotiticationDto>);
}
AutoMapping using the new approach
public IEnumerable<NotiticationDto> GetNewNotifications()
{
var userid = User.Identity.GetUserId();
var userNotifications = _context.UserNotifications
.Where(un => un.UserId == userid)
.Select(un => un.Notification)
.Include(n => n.Gig.Artist)
.ToList();
Mapper.Map<UserDto>(ApplicationUser);
Mapper.Map<GigDto>(Gig);
Mapper.Map<NotiticationDto>(Notitication);
});
}
I have created a class called MappingProfile
public class MappingProfile : Profile
{
public static void IntializeMappings()
{
Mapper.Initialize(cfg =>
{
cfg.CreateMap<ApplicationUser, UserDto>();
cfg.CreateMap<Gig, GigDto>();
cfg.CreateMap<Notitication, NotiticationDto>();
});
}
}
In Global.asax , I have written the following code
Mapper.Initialize(cfg =>
{
cfg.CreateMissingTypeMaps = true;
cfg.AddProfile<MappingProfile>();
});
I am getting compile time error in the GetNewNotifications() web api method for e.g at the following line Mapper.Map(ApplicationUser); saying the type mentioned in the destination parameter cannot be of type class. It is expecting object. Also How do I return the data transfer object in the second example using the new approach like I have done in the first example. Also if somebody can suggest a better way of implementing it ?
I am trying to figure out how to configure the new AutoMapper at the Global.asax level.
I used to do the following with old AutoMapper:
Create a class in App_Start folder called MappingProfile.cs and in the constructor I would add my mappings like this:
public MappingProfile()
{
Mapper.CreateMap<Product, ProductDto>();
Mapper.CreateMap<ApplicationUser, UserDto>();
}
Then in Global.asax call:
Mapper.Initialize(cfg => cfg.AddProfile<MappingProfile>());
Can someone please tell me how to achieve the above with the new version of AutoMapper? I have been reading the docs but can't seem to get it.
I believe I do something like this in my MappingProfile.cs file:
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Product, ProductDto>();
cfg.CreateMap<ApplicationUser, UserDto>();
});
but what do I do with the config variable?
This is how I do it.
public abstract class AutoMapperBase
{
protected readonly IMapper _mapper;
protected AutoMapperBase()
{
var config = new MapperConfiguration(x =>
{
x.CreateMap<Product, ProductDto>();
x.CreateMap<ApplicationUser, UserDto>();
});
_mapper = config.CreateMapper();
}
}
Then inherit AutoMapperBase from any class which needs to use it, and call it like this:
var foo = _mapper.Map<ProductDto>(someProduct);
You no longer need it declared or configured in Global.asax
What you need to do is the following:
Mapper.Initialize(cfg =>
{
cfg.CreateMissingTypeMaps = true;
cfg.AddProfile<MappingProfile>();
});
This will configure the static mapper instance that was exposed in the old AutoMapper so that you can do:
Mapper.Map<SomeType>(fromObject);
If you don't want to use the static instance then you need to look at the IntoNET answer.
The "CreateMissingTypeMaps" line ensures that missing mappings are created "on the fly". It's the equivalent of the old:
Mapper.DynamicMap<SomeType>(fromObject)
If you want to define each mapping by hand you can remove this line.
There was a version of AutoMapper where author tried to get rid of the exposed static for some reason but he brought it back after public outcry from what I understand. So now you can do it any way you find suitable. You can read about it here:
https://lostechies.com/jimmybogard/2016/02/24/automapper-4-2-1-released-static-is-back-limited-edition/