如何使用 ProjectTo 在 Automapper 中有条件地 MapFrom
How to Conditionally MapFrom in Automapper using ProjectTo
我想使用来自 Automapper 10.1.1 的投影为 EFCore 5 创建一个查询,该查询将有条件地包含关联。
如果我将其写在 select 语句中,我将执行以下操作:
_dbContext.Employees
.Select(x => new EmployeeDto()
{
Id = x.Id,
Contract = includeContract ? new ContractDto()
{
Id = x.Contract.Id
} : null
})
.FirstAsync();
我是这样尝试的:
型号
//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 配置文件
public class EmployeeProfile : Profile
{
public EmployeeProfile()
{
bool includeContract = false;
CreateMap<Employee, EmployeeDto>()
.ForMember(x => x.ActiveContract, opt => opt.MapFrom(x => includeContract ? x.ActiveContract : null));
}
}
而不是 null
,我尝试了 default(Contract)
和 (Contract)null
它们产生相同的结果。
我还有一个合同映射,只是一个简单的 <Contract, ContractDto>
.
查询
bool includeContract = someCondition;
var result = await _dbContext.Employees
.ProjectTo<EmployeeDto>(_mapper.ConfigurationProvider, new { includeContract })
.FirstAsync(x => x.id == id);
我的预期结果是,除非 includeContract
设置为 true,否则我将返回一个 EmployeeDto
合同为空。
但是,如果 includeContract
为假,则会抛出以下错误:
System.InvalidOperationException: Expression 'NULL' in SQL tree does not have type mapping assigned
生成的查询表达式:
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)'
我知道这可以通过在 ConvertUsing
中显式定义表达式来实现,但我希望尽可能避免写出我的整个 DTO。
在他们的评论中引用通过@Lucian 链接的文档。
解决方案是将 AutoMapper 配置调整为以下内容:
CreateMap<Employee, EmployeeDto>()
.ForMember(x => x.ActiveContract, opt => opt.ExplicitExpansion())
并将查询调整为以下内容:
var result = await _dbContext.Employees
.ProjectTo<EmployeeDto>(_mapper.ConfigurationProvider,
null,
dest => dest.ActiveContract
)
.FirstAsync(x => x.id == id);
我想使用来自 Automapper 10.1.1 的投影为 EFCore 5 创建一个查询,该查询将有条件地包含关联。 如果我将其写在 select 语句中,我将执行以下操作:
_dbContext.Employees
.Select(x => new EmployeeDto()
{
Id = x.Id,
Contract = includeContract ? new ContractDto()
{
Id = x.Contract.Id
} : null
})
.FirstAsync();
我是这样尝试的:
型号
//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 配置文件
public class EmployeeProfile : Profile
{
public EmployeeProfile()
{
bool includeContract = false;
CreateMap<Employee, EmployeeDto>()
.ForMember(x => x.ActiveContract, opt => opt.MapFrom(x => includeContract ? x.ActiveContract : null));
}
}
而不是 null
,我尝试了 default(Contract)
和 (Contract)null
它们产生相同的结果。
我还有一个合同映射,只是一个简单的 <Contract, ContractDto>
.
查询
bool includeContract = someCondition;
var result = await _dbContext.Employees
.ProjectTo<EmployeeDto>(_mapper.ConfigurationProvider, new { includeContract })
.FirstAsync(x => x.id == id);
我的预期结果是,除非 includeContract
设置为 true,否则我将返回一个 EmployeeDto
合同为空。
但是,如果 includeContract
为假,则会抛出以下错误:
System.InvalidOperationException: Expression 'NULL' in SQL tree does not have type mapping assigned
生成的查询表达式:
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)'
我知道这可以通过在 ConvertUsing
中显式定义表达式来实现,但我希望尽可能避免写出我的整个 DTO。
在他们的评论中引用通过@Lucian 链接的文档。
解决方案是将 AutoMapper 配置调整为以下内容:
CreateMap<Employee, EmployeeDto>()
.ForMember(x => x.ActiveContract, opt => opt.ExplicitExpansion())
并将查询调整为以下内容:
var result = await _dbContext.Employees
.ProjectTo<EmployeeDto>(_mapper.ConfigurationProvider,
null,
dest => dest.ActiveContract
)
.FirstAsync(x => x.id == id);