Reputation: 415
I want to create a query for EFCore 5 using projection from Automapper 10.1.1 that will conditionally include associations. If I were to write this in a select statement, I would do the following:
_dbContext.Employees
.Select(x => new EmployeeDto()
{
Id = x.Id,
Contract = includeContract ? new ContractDto()
{
Id = x.Contract.Id
} : null
})
.FirstAsync();
I attempted it like so:
Models
//Source
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public Contract ActiveContract { get; set; }
}
public class Contract
{
public int Id { get; set; }
public DateTimeOffset StartDate { get; set; }
public DateTimeOffset EndDate { get; set; }
public Employee Employee { get; set; }
public int EmployeeId { get; set; }
}
//Destination
public class EmployeeDto
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public ContractDto ActiveContract { get; set; }
}
public class ContractDto
{
public int Id { get; set; }
public DateTimeOffset StartDate { get; set; }
public DateTimeOffset EndDate { get; set; }
}
AutoMapper Profile
public class EmployeeProfile : Profile
{
public EmployeeProfile()
{
bool includeContract = false;
CreateMap<Employee, EmployeeDto>()
.ForMember(x => x.ActiveContract, opt => opt.MapFrom(x => includeContract ? x.ActiveContract : null));
}
}
Instead of null
, I've tried default(Contract)
and (Contract)null
these produce the same result.
Also I have a mapping for Contracts, just a simple <Contract, ContractDto>
.
Query
bool includeContract = someCondition;
var result = await _dbContext.Employees
.ProjectTo<EmployeeDto>(_mapper.ConfigurationProvider, new { includeContract })
.FirstAsync(x => x.id == id);
My expected result is, I will be returned a EmployeeDto
with the Contract being null, unless the includeContract
is set to true.
However if the includeContract
is false, the following error is thrown:
System.InvalidOperationException: Expression 'NULL' in SQL tree does not have type mapping assigned
the query expression produced:
Compiling query expression:
'DbSet<Employee>()
.Select(dtoEmployee => new EmployeeDto{
Age = dtoEmployee.Age,
Id = dtoEmployee.Id,
Name = dtoEmployee.Name,
ActiveContract = null == null ? null : new ContractDto{
Id = null.Id,
StartDate = null.StartDate,
EndDate = null.EndDate
}
}
)
.First(x => x.Id == __id_0)'
I know that is achievable by explicitly defining the expression within a ConvertUsing
, but would like to avoid writing out my whole DTOs if possible.
Upvotes: 1
Views: 968
Reputation: 415
Referring to the documentation linked via @Lucian in their comment.
The solution was to the adjust the AutoMapper config to the following:
CreateMap<Employee, EmployeeDto>()
.ForMember(x => x.ActiveContract, opt => opt.ExplicitExpansion())
And to adjust the query to the following:
var result = await _dbContext.Employees
.ProjectTo<EmployeeDto>(_mapper.ConfigurationProvider,
null,
dest => dest.ActiveContract
)
.FirstAsync(x => x.id == id);
Upvotes: 1