-1

I have an existing object that I would like to update with the latest data from my database. I can currently retrieve the EF Core object and then call Mapper.Map to update my dto. Is there a way to use ProjectTo (or something similar) to update my object from the database?

Instead of this:

var result = await context.DbSet
  .FirstOrDefaultAsync(e => e.Id == id, cancellationToken: cancellationToken)
  .ConfigureAwait(false);

return Mapper.Map(result, existingObject);

How can I:

var result = await context.DbSet
  .ProjectTo<TDest>(/* existingObject */)  // I tried .Map, but I got an error that automapper was trying to map this to a child object
  .FirstOrDefaultAsync(e => e.Id == id, cancellationToken: cancellationToken)
  .ConfigureAwait(false);

return existingObject;  // Where existingObject contains the latest database information
  • This just updated the EF entity from the database, when I am using a DTO with AutoMapper, I don't keep a reference to the original entity. I am able to get the latest data from the database, I am just hoping there is way to update my DTO directly from the query and not map the EF entity. – Matt Spinder Mar 17 '22 at 15:04
  • I would try to use DbSet.Select() – keuleJ Mar 17 '22 at 15:37
  • AutoMapper *maps*. It doesn't know anything about databases or updates. It's not even needed to work with EF Core, it's used only to make it easier to map long database DTOs to eg API DTOs based on conventions, flattening etc. `ProjectTo(configuration)` expects a *configuration* object that specifies how to map from one DTO to the other. – Panagiotis Kanavos Mar 17 '22 at 15:50
  • Why are you trying to modify existing objects? You're loading the database objects anyway, why not use them? – Panagiotis Kanavos Mar 17 '22 at 15:54
  • PS: The [documentation](https://docs.automapper.org/en/stable/Queryable-Extensions.html) explains what ProjectTo is used for. It also explains it should be the last call in a query. – Panagiotis Kanavos Mar 17 '22 at 16:16
  • @PanagiotisKanavos Thanks for the comments. So I should a) call Where before ProjectTo and not use the filter in FirstOrDefault? b) I was under the assumption that the ProjectTo bypasses the entity object creation and writes directly to the dto, am I wrong on this? And c) what I was hoping for does not exist? I will have to fetch the entity object and then map it as I currently do it (my first example)? – Matt Spinder Mar 17 '22 at 16:43

1 Answers1

0

Not sure how it is useful, but you can use the following extension:

public static class MappingExtensions
{
    public static async Task<TDest> ProjectToObjAsync<TDest>(this IQueryable source, IMapper mapper, TDest obj, cancellationToken cancellationToken = default)
    {
        var loadedObj = await source.ProjectTo<TDest>(mapper.Configuration).FirstOrDefaultAsync(cancellationToken);

        if (loadedObj != null)
        {            
            mapper.Map(loadedObj, obj);
        }

        return obj;
    }
}

And usage:

var result = await context.DbSet
    .Where(e => e.Id == id)
    .ProjectToObjAsync(Mapper, existingObject, cancellationToken)
    .ConfigureAwait(false);

return existingObject;
Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
Svyatoslav Danyliv
  • 21,911
  • 3
  • 16
  • 32
  • @PanagiotisKanavos, how I will load entire table with `FirstOrDefaultAsync` call? Please start reading before posting comments. – Svyatoslav Danyliv Mar 17 '22 at 16:04
  • I misunderstood how `ProjectTo` works. That still leaves this answer no better than the original code – Panagiotis Kanavos Mar 17 '22 at 16:10
  • Noticed in first sentence that I do not see reason of such method. – Svyatoslav Danyliv Mar 17 '22 at 16:11
  • The [docs warn against using such code](https://docs.automapper.org/en/stable/Queryable-Extensions.html) `ProjectTo must be the last call in the chain. ORMs work with entities, not DTOs. So apply any filtering and sorting on entities and, as the last step, project to DTOs.` That's why I assumed the method actually executes the results. – Panagiotis Kanavos Mar 17 '22 at 16:12
  • @PanagiotisKanavos, even worse, I know how `ProjectTo` works internally. This usage is correct – Svyatoslav Danyliv Mar 17 '22 at 16:21