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.
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.
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.
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'm still fairly new to EF (v4.1), so correct me if I'm wrong, but if I have an InverseProperty as follows:
public virtual ICollection<ItemComment> Comments { get; set; }
This will be lazy loaded when the property is accessed. However, if I wish to filter this list - for example, to get only active comments, I could just add another property as follows:
public IEnumerable<ItemComment> ActiveComments {
get { return Comments.Where(x => x.IsActive); }
}
However, this will load the entire Comments collection first, and then filter right? So not using IQueryable? For performance, ideally I'd like to get the list using IQueryable.
So my question is, can this be done using a property of an entity like this? Or am I going to have to do a where on the ItemComments directly:
var comments = itemCommentRepository.QueryAll()
.Where(x => x.IsActive && x.ItemId == XX).
This will obviously work... but going forward I wonder if there's a better solution?
Update: It seems the entire result set IS loaded, and any filtering would be done on the whole dataset client-side. Aside from hacks, or changing the entity to pass the context in (yuck!), there doesn't appear to be an in-built way to do so. Have marked #Slauma's response as the answer.
this will load the entire Comments collection first, and then filter
right?
Yes.
can this be done using a property of an entity
In theory, by injecting the repository or even a context into the entity constructor. But you would have a dependency of your POCO entities on a data access layer. I would not like this solution.
Your proposed solution is a way. You could alternatively use explicit loading:
itemRepository.LoadActiveComments(item);
Implemented like this:
void LoadActiveComments(Item item)
{
context.Entry(item).Collection(i => i.Comments).Query()
.Where(c => c.IsActive).Load();
}
I'm working on a RoleProvider in .NET, using Fluent NHibernate to map tables in an Oracle 9.2 database.
The problem is that the many-to-many table connecting users and roles uses a primary key generated from a sequence, as opposed to a composite key. I can't really change this, because I'm writing it to be implemented in a larger existing system.
Here is my UserMap:
public UserMap()
{
this.Table("USR");
HasMany(x => x.Memberships).Cascade.All()
.Table("MEMBERSHIP").Inverse().LazyLoad();
HasManyToMany(x => x.Roles)
.Table("USR_ROLE")
.Cascade.SaveUpdate()
.ParentKeyColumn("USR_ID")
.ChildKeyColumn("ROLE_ID")
.Not.LazyLoad();
}
And my RoleMap:
public RoleMap()
{
this.Table("ROLE");
Map(x => x.Description).Column("ROLE_NAME");
Map(x => x.Comment).Column("ROLE_COMMENT");
HasManyToMany(x => x.Users)
.Table("USR_ROLE")
.ParentKeyColumn("ROLE_ID")
.ChildKeyColumn("USR_ID")
.Inverse();
}
Yet, this is giving me the error:
Type 'FluentNHibernate.Cfg.FluentConfigurationException' in assembly 'FluentNHibernate, Version=1.0.0.593, Culture=neutral, PublicKeyToken=8aa435e3cb308880' is not marked as serializable.
Is there a simple fix to allow this HasMayToMany to use my PersistentObjectMap extension? I'm thinking I may have to add a convention for this many-to-many relationship, but I don't know where to start with that, since I've just started using NHibernate and Fluent NHibernate only recently.
I've been working on this problem for a while and I can't seem to find a solution.
Any help would be much appreciated. Thanks.
EDIT: I think I've found a possible solution here: http://marekblotny.blogspot.com/2009/02/fluent-nhbernate-and-collections.html
I'll try the above method of creating an entity and a class map for the linking table and post my findings.
EDIT 2: I created a linking entity as mentioned in the above blog post and downloaded the newest binaries (1.0.0.623).
This helped me discover that the issue was with setting lazy load and trying to add roles to the user object in a completely new session.
I modified the code to move OpenSession to the BeginRequest of an HttpModule as described here. After doing this, I changed my data access code from wrapping the open session in a using statement, which closes the session when it is finished, to getting the current session and wrapping only the transaction in a using statement.
This seems to have resolved the bulk of my issue, but I am now getting an error that says "Could not insert collection" into the USR_ROLE table. And I'm wondering if the above code should work with a UserRoleMap described as:
public UserRoleMap()
{
this.Table("USR_ROLE");
/* maps audit fields id, created date/user, updated date/user */
this.PersistentObjectMap("USR_ROLE");
/* Link these tables */
References(x => x.Role).Column("ROLE_ID");
References(x => x.User).Column("USR_ID");
}
Hibernate's documentation for many-to-many relationship suggests creating an object to maintain a one-to-many/many-to-one, as in an ERD. I'm sure this would be much easier with conventional naming standards, but I have to stick with certain abbreviations and odd (and not always properly-implemented) conventions.
To fix this, I created an Entity, Mapping, and Repository for UserRole. And, instead of HasManyToMany mapping in the User and Role Entities, I have a HasMany mapping. It's a little weird, because I now have:
IList<UserRole> UserRoles {get; protected set;}
and IList<Role> Roles { get{ return UserRoles.Select(u => u.Role).ToList(); } }
This works, however, I'm not 100% sure why this works and the HasManyToMany doesn't.