In my domain I have defined an entity Person
public class Person
{
public string Id { get; set; } = String.Empty;
public string Tag { get; set; } = String.Empty;
public string Notes { get; set; } = String.Empty;
}
and in my application layer I have a shallow DTO which I use to embed in reponses of which I can be certain that the user will query the details if I do not embed them:
public class PersonDetailsShallow
{
public string PersonId { get; internal set; } = null!;
public string Tag { get; internal set; } = null!;
}
Since the mapping from Person
to PersonDetailsShallow
happens in various places, I created a static helper function for that:
public static PersonDetailsShallow ToPersonDetailsShallow(this Person person)
{
return new PersonDetailsShallow()
{
PersonId = person.Id,
Tag = person.Tag
};
}
Then, whenever I need a shallow person, I create it as follows:
// using MediatR;
public async Task<MediatorResult<List<PersonDetails>>> Handle(AllPersonsQuery request, CancellationToken cancellationToken)
{
await Task.CompletedTask;
return dbContext.Persons
.Select(p => p.ToPersonDetailsShallow())
.ToList();
}
EF core however does not/ cannot recognise that the mapper function makes no use of Person.Notes
and pulls it from the database by issuing the following query:
SELECT "p"."id", "p"."notes", "p"."tag"
FROM "persons" AS "p"
instead of issuing the more efficient query
SELECT "p"."id", "p"."tag"
FROM "persons" AS "p"
when doing the mapping like this:
var allPersons = dbContext.Persons
.Select(p => new PersonDetailsShallow()
{
Id = p.Id,
Tag = p.Tag
})
.ToList();
However, since I am using PersonDetailsShallow in so many different places, it would become a maintenance-nightmare if anything ever changes in the entity Person
, because I would have the same mapping logic all over the place.
How can I have the benefit of maintaining the mapping logic in one place only while also getting the benefit of having optimised queries as I would when putting the mapping logic all over the place?
I am aware that I could just create a separate query that fetches exactly the information needed to create a PersonDetailsShallow
and return it, but then, if for example I have a set of collection of PurchaseDetails
-DTOs, each of which I would want to embed a PersonDetailsShallow
, then I would need to query the PersonDetailsShallow
for each PurchaseDetail
s separately, taking EF Core the possibility obtain all required data with one single query.
There must be some method UseMapping(person => new PersonDetailsShallow() {...})
that I am missing, where I can provide the mapping function as a parameter or something.