Search code examples
c#entity-frameworkentity-framework-6generic-collectionsautomapper-6

AutoMapper.Collections.EntityFramework


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


Solution

  • 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();