Is it possible to configure/use AutoMapper in such a way where when i create an object from a mapping i allow all properties and child collections, however, when it comes to performing an update to existing object, the mapping will ignore child collection properties as they will be empty but i dont want them removed.
This is because i am working with a WCF service that sends delta changes to objects and most of my model works in a tree hierarchy:
Parent
List<Child> Children
ParentDto
List<ChildDto> Children
config.CreateMap<ParentDto, Parent>();
config.CreateMap<ChildDto, ChildDto>();
This works well and the child collection is populated first time round. However, there are scenarios where i will send the ParentDto across with just the parent POCO property changes (such as a datetime change), but the child list will be empty as none of them have changed. Normally i would do:
_Mapper.Map<ParentDto,Parent>(dto, local)
but obviously that will change the entire tree and populate the local object with an empty child list. Massively simplifying but would something like
_Mapper.Map<ParentDto, Parent>(dto, local).Ignore(p => p.Children)
be possible?
I should also add I am using SimpleInjector DI framework. So perhaps there is a way to register 2 configurations, one with ignore and one without?
Use .ForMember(dest => dest.A, opt => opt.MapFrom(src => src.B)) for mapping only properties you need to update.
For those who still struggle to find this. You can use Autommapper Conditional Mapping.
You can do it like this, in the Initialize
config.CreateMap<ChildDto, ChildDto>().ForMember(dest => dest.Children, opt => opt.Condition(source => source.TriggerChildMap));
This will ignore mapping based on the property in source object. To map against existing destination you need to use
Mapper.Map(source, destination) method and not the var result = Mapper.Map<ChildDto>(source) property.
Related
Is there a way to dynamically generate multiple maps? For example, can I map to an array of source and destination values? I am working with a legacy database and one of the tables has over 350+ columns. It will be very error prone and labor intensive to map each field manually.
//HOW CAN BELOW INCLUDE OVER 350+ MAPPED FIELDS DYNAMICALLY FROM AN ARRAY?
CreateMap<Employee, EditEmployeeModel>()
.ForMember(dest => dest.ConfirmEmail,
opt => opt.MapFrom(src => src.Email));
Basically if the property names are similar then you don't have to map source to destination manually. Automapper will do this automatically. So, if you keep the property names similar then there's no need to map.
But if that's not possible and keeping different names is a necessity then you can use .AfterMap() feature of Automapper. It is simpler than source destination mapping.
public class EditEmployeeMap : IMappingAction<Employee, EditEmployeeModel>
{
public void Process(Employee source, EditEmployeeModel destination, ResolutionContext context)
{
destination.ConfirmEmail = source.Email;
}
}
Then,
CreateMap<Employee, EditEmployeeModel>().AfterMap<EditEmployeeMap>();
Though the purpose of the above solution is not for this use case, this can make the job more simple for you.
But i would suggest to keep the property name similar, that will save a lot of hastle.
I'm working on a project and I need to map collections. I came across Automapper.Collection and am trying to user the class but its all not working. Please I need help. Here is my code.
In my startup class
services.AddAutoMapper(cfg => { cfg.AddCollectionMappers(); },typeof(Startup));
I also created a class that Inherits from the Automapper Profile class here
public class UserMappingProfile : Profile
{
public UserMappingProfile()
{
CreateMap<PhotoToUpdateDto, Photo>().EqualityComparison((src, dest) => src.PublicId.ToLower() == dest.PublicId.ToLower());
CreateMap<SocialHandles, SocialHandleDto>().ReverseMap().EqualityComparison((src, dest) => src.Name.ToLower() == dest.Name.ToLower());
}
}
Anytime I use the mapper, it creates new records in the database instead of updating the already existing. Please I need help.
I think its because of navigation property in your entity
If you have navigation property ignore them in your Createmap
For example
CreateMap<PhotoToUpdateDto, Photo>().EqualityComparison((src, dest) =>
src.PublicId.ToLower() == dest.PublicId.ToLower())
.ForMember(p => p.Photographer , opt => opt.Ignore());
I'll assume this:
You have no problem with the mapper (since it still map your object successfully).
When you get new object to interact with database, a new record created, since your make sure the id mapping was correct.
You are using EF Core or some kind of ORM to interact with the database
If i was wrong, please just ignore this answer.
Okay, If my assuming was right, that's because automapper always return a new object as result of the mapping process, which is absolutely not tracked by your DbContext yet. And then, whenever you interact with that to make some change in the database, an add operation is likely going to happen.
I think you need to add some more information about the block of code that you actually have problem with... which in this case... why the database entry keep created, but not update.
I’m trying to copy/clone entity graph with EF6.1 and getting duplicate entities.
Below is a piece of my model which consist of a Template that I want to modify, copy and assign to different users, something like Save As function.
Here is my entities model:
What I’m doing is:
var newTemplate = ctx.Templates
.Where(t => t.TemplateId == SelectedTemplate.TemplateId)
.Include(t => t.Properties.Select(p => p.PropertyCollections))
.Include(t => t.Properties.Select(p => p.Values))
.AsNoTracking()
.First();
newTemplate.TemplateName = newTemplateName;
ctx.Templates.Add(newTemplate);
ctx.SaveChanges();
And what I get is shown below where “Template1” is the source and “Template2” is the copy in which every ‘PropertyCollection’ has a duplicated entry for each ‘Property’.
Result after copy:
I understood that with AsNoTracking there is no identity mapping which is the reason behind this but I can’t find even a custom solution.
I didn't really test your code, but I think your Entities might really get messed up when doing it that way. Maybe this attempt would work for you. It's for EF4 but might work.
You are adding the whole graph, so EF is inserting everything. You are using AsNoTracking to "trick" EF instead of its original purpose.
I would suggest you to write a few lines of code to actually implement your business requirement, which is create a new Template based on another one.
So, get the template (without the AsNoTracking), and create a new template initializing the properties based on the original template values. Then add the new template to the context. EF will insert the new template and reference the existing dependent entities.
This is also a safer way to implement this, as in the future you might require to set some properties with different values in the new template.
I would like to use AutoMapper to map one collection to another. I know that the convention is to set up a mapping for the child object:
Mapper.CreateMap<User, UserDto>();
And then this works fine:
Mapper.Map<List<UserDto>>(UserRepo.GetAll());
But I'd still like to map the list anyway. For example, I'd like to do something like this:
Mapper.CreateMap<List<User>, List<UserDto>>()
.AfterMap((source, destination) =>
{
// some complex/expensive process here on the entire user list
// such as retrieving data from an external database, etc
}
That way I can still use the first map but also to do something custom with the user list as well. In my scenario, it's looking up event IDs in an external database in another datacenter and I'd like to optimise it by only looking up unique IDs, not doing it object-by-object.
However, when I attempt to map a list of User to a list of UserDto, the mapping just returns an empty list. Putting a breakpoint in the AfterMap function reveals that the destination variable holds an empty list. How can I make AutoMapper do this correctly?
Well, that was embarrassing. Five minutes later, I just figured out that you can add a ConvertUsing function which just proxies to a member-by-member mapping:
Mapper.CreateMap<List<User>, List<UserDto>>()
.ConvertUsing(source =>
{
// some complex/expensive process here on the entire user list
// such as retrieving data from an external database, etc
return source.Select(Mapper.Map<User, UserDto>).ToList());
});
Perhaps that's not how you're meant to do it but it works for me.
i have a parent object called Request and a Child object called RequestDate
A Request has a list of RequestDates
i want to have mappings so when i:
Save Parent, it saves all children
Update list on parent object (remove some items and add some new items) and save parent it updates children
Delete parent will delete all children.
is this possible. i tried using this syntax but it doesn't seem to work:
HasMany(x => x.RequestDates)
.AsBag()
.Inverse()
.Cascade.AllDeleteOrphan()
.Fetch.Select()
.BatchSize(80);
the issue is around #2. what is the way to update the list of items. I am calling Remove() to get rid of some and then calling Add() to add new ones
You've configured your collection as Inverse, which means the "other side" (i.e. a References in RequestDate) is responsible for managing the relationship.
Therefore, you need to set the reference to the Request in the RequestDate.
If you don't have such a property, then remove the Inverse() call. But NH will do an insert with NULL and then an UPDATE, which might not be what you want.