带有嵌套提取逻辑的 EF Core LINQ 投影不起作用

问题描述 投票:0回答:1

我正在尝试将 EF Core LINQ 查询的投影逻辑提取到单独的方法中,以使代码更清晰或为不同的投影重用代码?

有一个类似的关于重用代码的帖子,位于我可以重用代码为使用 EF Core 为子属性选择自定义 DTO 对象吗?,但所选答案使用其他库,并且问题是要使用类似

Select(Model.AsDto) 的调用
,封装了 lambda。

我想使用一个简单的解决方案,并且不想删除 lambda 表达式。我只想提取创建新对象的映射部分,例如

 (danceCoverImage, image) => Mappings.MapToImageItemResponse(danceCoverImage, image))

这适用于一个嵌套级别,但不适用于两个嵌套级别,如下例所示。

我有这个示例投影,它返回带有嵌套封面图像的位置数据。图像 URI、宽度和高度是通过连接从图像集合中提取的。

List<LocationMLResponse> Locations;

var query = dbContext.Location.AsNoTracking()
           .Select(location => new LocationMLResponse(
            location.Id.Value,
            location.Name.ToStringDictionary(),
            location.CoverImages
                .Join(dbContext.Image.AsNoTracking(),
                danceCoverImage => danceCoverImage.ImageId,
                image => image.Id,
                (danceCoverImage, image) => new ImageItemResponse(
                    danceCoverImage.ImageId.Value,
                    danceCoverImage.DisplayOrder,
                    danceCoverImage.Language == null ? null : danceCoverImage.Language.Tag,
                    danceCoverImage.ScreenSize == null ? null : danceCoverImage.ScreenSize.Value,
                    danceCoverImage.FocusPointX,
                    danceCoverImage.FocusPointY,
                    image.Uri.ToString(),
                    image.MetaData.Width,
                    image.MetaData.Height
                )).ToList(),
            location.Address.MapToDto(),
            location.Created,
            location.LastModified,
            location.Version
            ));

Locations = await query.ToListAsync();

我尝试提取映射方法:

var query = dbContext.Location.AsNoTracking()
           .Select(location => MapToLocationResponse.MLResponse(dbContext, location));

internal static class MapToLocationResponse
{
    public static LocationMLResponse MLResponse(VCDbContext dbContext, Location location)
    {
        return new LocationMLResponse(
            location.Id.Value,
            location.Name.ToStringDictionary(),
            location.CoverImages
                .Join(dbContext.Image.AsNoTracking(),
                danceCoverImage => danceCoverImage.ImageId,
                image => image.Id,
                (danceCoverImage, image) => Mappings.MapToImageItemResponse(danceCoverImage, image)).ToList(),
            location.Address.MapToDto(),
            location.Created,
            location.LastModified,
            location.Version
            );
    }
}

internal static partial class Mappings
{
    public static ImageItemResponse MapToImageItemResponse(ImageItem imageItem, Image image)
    {
        return new ImageItemResponse(
            imageItem.ImageId.Value,
            imageItem.DisplayOrder,
            imageItem.Language == null ? null : imageItem.Language.ToString(),
            imageItem.ScreenSize == null ? null : imageItem.ScreenSize.ToString(),
            imageItem.FocusPointX,
            imageItem.FocusPointY,
            image.Uri.ToString(),
            image.MetaData.Width,
            image.MetaData.Height
        );
    }
}

此代码创建以下查询:

SELECT [l].[LocationId], [l].[Created], [l].[CreatedBy], [l].[CreatedInNameOf], [l].[LastModified], [l].[LastModifiedBy], [l].[LastModifiedInNameOf], [l].[Name], [l].[TId], [l].[Version], [l].[Address_City], [l].[Address_CountryId], [l].[Address_CountryName], [l].[Address_DeliveryInstruction], [l].[Address_State], [l].[Address_Street], [l].[Address_StreetAffix], [l].[Address_StreetNumber], [l].[Address_ZipCode], [l0].[TId], [l0].[DisplayOrder], [l0].[FocusPointX], [l0].[FocusPointY], [l0].[ImageId], [l0].[Language], [l0].[LocationId], [l0].[ScreenSize]
FROM [VC].[Location] AS [l]
LEFT JOIN [VC].[LocationCoverImage] AS [l0] ON [l].[LocationId] = [l0].[LocationId]
ORDER BY [l].[LocationId]

缺少图像表的连接和图像字段的选择。

我尝试仅使用一个嵌套进行映射:

 List<LocationMLResponse> Locations;

 var query = dbContext.Location.AsNoTracking()//.AsSplitQuery()
            .Select(location => new LocationMLResponse(
             location.Id.Value,
             location.Name.ToStringDictionary(),
             location.CoverImages
                 .Join(dbContext.Image.AsNoTracking(),
                 danceCoverImage => danceCoverImage.ImageId,
                 image => image.Id,
                 (danceCoverImage, image) => Mappings.MapToImageItemResponse(danceCoverImage, image)).ToList(),
             location.Address.MapToDto(),
             location.Created,
             location.LastModified,
             location.Version
             ));

 Locations = await query.ToListAsync();

这有效。该投影创建与顶部完整投影相同的查询。

(请注意,我在 EF Core GitHub 问题中发布的创建的查询存在一个普遍问题。

这是 EF Core 的总体问题还是仅与连接有关?是否可以使用嵌套调用或者是否需要使用表达式?


EF核心版本:8.0.8
数据库提供程序:Microsoft.EntityFrameworkCore.SqlServer
目标框架:NET 8.0
操作系统:Win 11
Visual Studio 2022 17.11.4

c# entity-framework linq
1个回答
0
投票

(danceCoverImage, image) => Mappings.MapToImageItemResponse(danceCoverImage, image))
和类似的是方法调用表达式。 EF Core 或其他工具可以提取的唯一信息是表达式有两个输入参数,它调用某些方法并返回某种类型的对象。
MapToImageItemResponse
的内容是一个黑盒子。它是经过编译的,无法轻松检查和翻译为 SQL。
制作可重用映射的正确方法是定义一个方法,该方法将准确返回内联 lambda 所拥有的内容 - 投影 linq 表达式。

Expression<Func<CoverImage, ImageItemResponse>> GetImageItemResponseMapping() 
{
    return (danceCoverImage, image) => new ImageItemResponse(
                    danceCoverImage.ImageId.Value,
                    danceCoverImage.DisplayOrder,
                    danceCoverImage.Language == null ? null : danceCoverImage.Language.Tag,
                    danceCoverImage.ScreenSize == null ? null : danceCoverImage.ScreenSize.Value,
                    danceCoverImage.FocusPointX,
                    danceCoverImage.FocusPointY,
                    image.Uri.ToString(),
                    image.MetaData.Width,
                    image.MetaData.Height);
}

然后

location.CoverImages
                 .Join(dbContext.Image.AsNoTracking(),
                 danceCoverImage => danceCoverImage.ImageId,
                 image => image.Id,
                 GetImageItemResponseMapping())

如果做得正确,无论嵌套层数有多少,它都应该可以工作。

© www.soinside.com 2019 - 2024. All rights reserved.