4

I have a Project object, which consists of many nested objects.

To save this object to the DB:

  • I change just one property within my Project (as a test), and pass it to my DAL.
  • I load the existing Project from the DB with EF.
  • I map the changed Project to the existing Project.
var existingProject = 
    db.Project.Single(p => p.ID == changedProject.ID).Include(p => p.Something);

existingProject = Mapper.Map(changedProject, existingProject);

db.SaveChanges();

This fails due to null foreign keys, which is no surprise because when I drill into the change tracker, I can see it's very confused:

var added = db.ChangeTracker.Entries().Where(e => e.State == EntityState.Added);

After the mapping takes place, a huge number of objects within the Project are marked as Added (even though nothing has been added or deleted from the project, I've only changed one property).

Is this because AutoMapper creates new instances of nested objects, and EF can't associate these with the existing objects from the DB?

I've seen this approach suggested in numerous places, is there a way for AutoMapper to work with the ChangeTracker so it understands the above - or is it better to just map everything manually? (a lot of work, in my case)

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
FBryant87
  • 4,273
  • 2
  • 44
  • 72

2 Answers2

5

Destination collections are cleared first. You need AutoMapper.Collection if you want to map to existing EF collections.

Lucian Bargaoanu
  • 3,336
  • 3
  • 14
  • 19
  • This looks promising thanks, though I'm using AutoMapper 3.3.1.0 so may need to update. – FBryant87 Aug 09 '17 at 13:38
  • So this made a difference, but now the numerous entires in ChangeTracker are marked as 'Modified' as opposed to 'Added' like before. They should be marked as unchanged, perhaps EF still recognises a change to the object because of the mapping? – FBryant87 Aug 09 '17 at 15:11
  • I'd say check the SQL, and if there are problems there, report them on github, but otherwise you should be ok. – Lucian Bargaoanu Aug 09 '17 at 15:15
  • I had an object coming through as null from my business logic, whereas the DB had it populated (so it marked all entities as different). Repopulating this fixed the problem. Great tool - this approach is much smoother than hundreds of horrific implementations I've come across! – FBryant87 Aug 09 '17 at 16:21
1

This is because when You are mapping, new entity is created and EF context does not have any information about it. You can map data from db to your 'changedProject' do all necessary changes then map again to 'existingProject' and for this entity call 'Update' method like in following example(using System.Data.EntityState.Modified) Updating records using a Repository Pattern with Entity Framework 6

Lukasz Cokot
  • 400
  • 2
  • 13
  • At the end when you map from 'changedProject' back to 'existingProject', surely this will cause the same problem? (AutoMapper will create new entities) – FBryant87 Aug 09 '17 at 12:47
  • Yes it is creating new instances but then Update method is reattaching them to context as 'modified' instead of 'Add' status – Lukasz Cokot Aug 09 '17 at 14:01
  • Understood, but wouldn't this set everything to be modified? If only 1 property has changed out of 5000, it would go and update them all. – FBryant87 Aug 09 '17 at 14:03
  • 1
    You are correct it will update all properties in this entity. It won't work for navigation properties(if you add, then it also adding all navigation properties). Probably one entity does not have 5k of columns :) but I know what You mean. There is also a way to mark only property as modified but You need to know which property has been changed, because ChangeTracker don't know that. – Lukasz Cokot Aug 09 '17 at 14:41