1

Asking same question differently!

Its seems clear I need to elaborate on this question because I have no viable responses.

Based on this AutoMapper registration code:

Mapper.Initialize(cfg =>
{
   cfg.AddCollectionMappers();
   cfg.SetGeneratePropertyMaps<GenerateEntityFrameworkPrimaryKeyPropertyMaps<DbContext>>();
 });

AutoMapper adds support for "updating" DbSet collections using this line:

Mapper.Map<List<DTO>, List<Entity>>(dtoCollection, entityCollection);

Saving changes through an open context should result in updating the database:

using (var context = factory.CreateContext())
{
  Mapper.Map<List<DTO>, List<Entity>>(dtoCollection, await 
  context.dbSet.TolistAsync());
  await context.SaveChangesAsync();
}

This does nothing!

So back to my original question. If calling the mapper with the dto and current state of the entity collection returns an updated entity collection based on the comparison Mapping Created here:

cfg.SetGeneratePropertyMaps<GenerateEntityFrameworkPrimaryKeyPropertyMaps<DbContext>>();

produces entity collection here:

var entities =  Mapper.Map<List<DTO>, List<Entity>>(dtoCollection, await 
context.dbSet.TolistAsync());

Am I support to iterate the new collection and update EF manually using this new collection? Its not clear what I am suppose to do at this point? Is this what I am suppose to do with the resulting collection?

        // map dto's to entities
        var entities = Mapper.Map(collection, await dbSet.ToListAsync());

        // add new records
        var toAdd = entities.Where(e => e.Id == 0);
        dbSet.AddRange(toAdd);

        // delete old records   
        var toDelete = entities.Where(entity => collection.All(e => e.Id > 0 && e.Id != entity.Id));
        dbSet.RemoveRange(toDelete);

        // update existing records
        var toUpdate = entities.Where(entity => collection.All(i => i.Id > 0 && i.Id == entity.Id)).ToList();
        foreach (var entity in toUpdate)
        {
            context.Entry(entity).State = EntityState.Modified;
        }

        await context.SaveChangesAsync();

This is my original question. If so it seems redundant. So I feel like I am missing something.

I appreciate some useful feedback. Please help!

Thanks

Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343
tsage080667
  • 89
  • 1
  • 10
  • I think I start understanding you question. `Mapper.Map(dtoCollction, entityCollection)` is supposed to be used for *collection navigation properties*, while you want to update the whole *table*, correct? – Ivan Stoev May 04 '18 at 19:10
  • Yes, the whole table. I am using this API to synchronize changes to the table from a daily import process. What I like about the API is that it allows me to abstract away the Entity interaction and only deal with Dto's. – tsage080667 May 04 '18 at 20:38

1 Answers1

4

EF DbSets are not collections. Basically they represent a database table and provide query and DML operations for it.

Looks like you want to synchronize the whole table with the DTO list. You can do that by loading the whole table locally using the Load or LoadAsync methods, and then Map the DTO collection to the entity DbSet.Local property. The difference with your attempts is that the Local property is not a simple list, but observable collection directly bound to the context local store and change tracker, so any modification (Add, Remove) will be applied to the database.

Something like this:

await dbSet.LoadAsync();
Mapper.Map(dtoCollection, dbSet.Local);
await context.SaveChangesAsync();
Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343
  • 1
    This is exactly what I was missing. I am not sure how other folks are using this API, but I was under the impression that this is how it should be used. The dbSet.Local was the lynch pin for me. The fact that it is an observable collection so the change can update the context based on changes from the Dto is what I needed to see. Thanks for the info and the help! – tsage080667 May 04 '18 at 20:33
  • I think this needs a word of caution, when adding new entities with owned/nested entities, because the [Add method of LocalView](https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.changetracking.localview-1.add?view=efcore-6.0#microsoft-entityframeworkcore-changetracking-localview-1-add(-0)) does not track other entities than the one being added: `Note that only the given entity is tracked. Any related entities discoverable from the given entity are not automatically tracked. ` – Xriuk Jul 26 '22 at 09:17