-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Closed
Description
Hey guys,
I've run into an issue with query projection when subqueries are included.
I've spent some time debugging and seeing if I could get it fixed up, I've got a pull request coming up with a fix that definitely works for my scenario and most probably other ones as well.
The code isn't the most elegant, but I'm not exactly at home in your source so I tried to keep my additions small and easily readable. If you've got a better way of fixing this problem, I'll gladly change the source (or feel free to change it yourself and fix up the pull request before merging).
More details in the reproduction repo:
https://github.com/Erythnul/AutomapperIssue
Source/destination types
//Source
public class Order
{
public Guid Id { get; set; }
public Guid CustomerId { get; set; }
public int? MostImportantOrderLine { get; set; }
public ICollection<OrderLine> OrderLines { get; set; } = null!;
}
public class OrderLine
{
public Guid Id { get; set; }
public Guid OrderId { get; set; }
public int OrderLineNumber { get; set; }
public string Description { get; set; } = null!;
public Order Order { get; set; } = null!;
}
//Destination
public class OrderModel
{
public OrderSubModel? OrderSubModel { get; set; }
public OrderLineModel? MostImportantOrderLine { get; set; }
}
public class OrderSubModel
{
public Guid? OrderId { get; set; }
}
public class OrderLineModel
{
public Guid OrderLineId { get; set; }
public string Description { get; set; } = string.Empty;
}
Mapping configuration
new MapperConfiguration(cfg =>
{
cfg.CreateMap<Entities.Order, Dtos.OrderModel>()
.ForMember(dst => dst.OrderSubModel, opt => opt.MapFrom(src => src.MostImportantOrderLine != null ? src : null))
.ForMember(dst => dst.MostImportantOrderLine, opt =>
{
opt.MapFrom(src => src.OrderLines.FirstOrDefault(x => x.OrderLineNumber == src.MostImportantOrderLine));
});
cfg.CreateMap<Entities.Order, Dtos.OrderSubModel>()
.ForMember(dst => dst.OrderId, opt => opt.MapFrom(src => src.Id));
cfg.CreateMap<Entities.OrderLine, Dtos.OrderLineModel>()
.ForMember(dst => dst.OrderLineId, opt => opt.MapFrom(src => src.Id))
.ForMember(dst => dst.Description, opt => opt.MapFrom(src => src.Description));
});
Version: 10.1.1
Expected behavior
//projectedQuery.Expression
queryable.Select(
dtoOrder => new Object_1548421275___MostImportantOrderLine_MostImportantOrderLine_MostImportantOrderLine
{
__MostImportantOrderLine = dtoOrder.OrderLines.FirstOrDefault(x => ((int?)x.OrderLineNumber) == dtoOrder.MostImportantOrderLine),
MostImportantOrderLine = dtoOrder.MostImportantOrderLine,
Id = dtoOrder.Id
}).Select(
dtoLet => new Dtos.OrderModel
{
MostImportantOrderLine = (dtoLet.__MostImportantOrderLine == null)
? null
: new Dtos.OrderLineModel
{
Description = dtoLet.__MostImportantOrderLine.Description,
OrderLineId = dtoLet.__MostImportantOrderLine.Id
},
OrderSubModel = (((dtoLet.MostImportantOrderLine != null) ? dtoLet : null) == null)
? null
: new Dtos.OrderSubModel
{
OrderId = (Guid?)((dtoLet.MostImportantOrderLine != null) ? dtoLet : null).Id
}
});
Actual behavior
//projectedQuery.Expression
queryable.Select(
dtoOrder => new Object_1548421275___MostImportantOrderLine_MostImportantOrderLine_MostImportantOrderLine
{
__MostImportantOrderLine = dtoOrder.OrderLines.FirstOrDefault(x => ((int?)x.OrderLineNumber) == dtoOrder.MostImportantOrderLine),
MostImportantOrderLine = dtoOrder.MostImportantOrderLine
//, Id = dtoOrder.Id
// Doesn't collect OrderId into dtoLet, is also not added as possible field to Proxy Object above
}).Select(
dtoLet => new Dtos.OrderModel
{
MostImportantOrderLine = (dtoLet.__MostImportantOrderLine == null)
? null
: new Dtos.OrderLineModel
{
Description = dtoLet.__MostImportantOrderLine.Description,
OrderLineId = dtoLet.__MostImportantOrderLine.Id
},
OrderSubModel = (((dtoLet.MostImportantOrderLine != null) ? dtoOrder : null) == null) //Doesn't compile, should be dtoLet
? null
: new Dtos.OrderSubModel
{
OrderId = (Guid?)((dtoLet.MostImportantOrderLine != null) ? dtoOrder : null).Id //Doesn't compile, should be dtoLet
}
});
Steps to reproduce
https://github.com/Erythnul/AutomapperIssue
var orders = new[]
{
new Order
{
Id = Guid.Parse("00000000-0000-0000-0000-000000000001"),
MostImportantOrderLine = 1,
OrderLines = new List<OrderLine>
{
new OrderLine
{
Id = Guid.Parse("00000000-0000-0000-0000-000000000101"),
OrderId = Guid.Parse("00000000-0000-0000-0000-000000000001"),
OrderLineNumber = 1,
Description = "ChessSet"
}
}
}
}.AsQueryable();
var projection = orders.ProjectTo<OrderModel>(Configuration);
var orderModel = projection.First(); //throws