Error projecting from a grouped linq query

huangapple go评论65阅读模式
英文:

Error projecting from a grouped linq query

问题

这是您提供的代码翻译:

我有一个数据库表,用于存储关于图像的以下数据:

public class EventImage
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int EventImageId { get; set; }

    public string EventImageFileExtension { get; set; }

    // 基于注释提供,但未用于减少带宽浪费
    public virtual byte[]? EventImageFullSizeBytes { get; set; }

    // 基于注释提供,但未用于减少带宽浪费
    public virtual byte[]? EventImageThumbnailBytes { get; set; }

    public int EventItemId { get; set; }
}

然后使用典型的DbSet进行查询

public class WebAppContext : IdentityDbContext<WebAppRazorUser, ApplicationRole, int>
{
    public DbSet<EventImage> EventImages { get; set; }
}

我的目标是检索每个(父级)事件项目的一个图像,但不包括二进制数据。使用Stack Overflow 上的不同答案,我组合了以下内容:

var eventImages = context.EventImages.AsNoTracking()
                    // 过滤到特定的父 Id
                    .Where(ei => uniqueEventIds.Contains(ei.EventItemId))
                    // 获取每个父项目的一个图像
                    .GroupBy(img => img.EventItemId, (key, g) => g.OrderBy(img => img.EventImageId).FirstOrDefault()) 
                    // 以不包括二进制数据的方式投影图像字段
                    .Select(img => new 
                                {
                                img.EventImageId,
                                img.EventItemId,
                                img.EventImageFileExtension,
                                })
                                .ToList();

然而,它引发了我不理解的异常:

> KeyNotFoundException: 给定的键 'EmptyProjectionMember' 在字典中不存在。 
>
> System.Collections.Generic.Dictionary<TKey, TValue>.get_Item(TKey key)

如果我删除整个投影(`.Select(...)`)部分,查询的第一部分确实返回了两个带有所有字段填充的图像,所以我认为那部分是正确的。不过,我不确定错误是与什么相关的。

更新
--

更换某些元素会产生奇怪的结果。

    .Select(img => new object()) // 无异常运行,但显然不会给我需要的内容

    .Select(img => new { }) // 与上面相同

    .Select(img => new { Field1 = "Foo" }) // 这会导致相同的异常

更新2
--

如果我从初始搜索创建一个列表,然后从中进行投影,似乎可以工作:

```csharp
var eventImages = (context.EventImages.AsNoTracking()
                    .Where(ei => uniqueEventIds.Contains(ei.EventItemId))
                    .GroupBy(img => img.EventItemId, (key, g) => g.OrderBy(img => img.EventImageId).FirstOrDefault()))
                    .ToList() // 修复了问题
                    .Select(img => new 
                                {
                                img.EventImageId,
                                img.EventItemId,
                                img.EventImageFileExtension,
                                });

查看底层类,我认为异常是由于属性虚拟而引起的,但仍然希望得到权威答案。

英文:

I have a database table that stores the following data regarding images:

public class EventImage
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int EventImageId { get; set; }
public string EventImageFileExtension { get; set; }
// Provided based on comments, but not used to reduce wasted bandwidth
public virtual byte[]? EventImageFullSizeBytes { get; set; }
// Provided based on comments, but not used to reduce wasted bandwidth
public virtual byte[]? EventImageThumbnailBytes { get; set; }
public int EventItemId { get; set; }
}

A typical DbSet is then used for querying:

public class WebAppContext : IdentityDbContext&lt;WebAppRazorUser, ApplicationRole, int&gt;
{
public DbSet&lt;EventImage&gt; EventImages { get; set; }
}

My goal is to retrieve one image per (parent) event item but without the binary data being included. Using different answers on SO I've pieced together the following:

var eventImages = context.EventImages.AsNoTracking()
// Filter to specific parent Id&#39;s
.Where(ei =&gt; uniqueEventIds.Contains(ei.EventItemId))
// Get only one image per parent
.GroupBy(img =&gt; img.EventItemId, (key, g) =&gt; g.OrderBy(img =&gt; img.EventImageId).FirstOrDefault()) 
// Project the image fields without the binary data
.Select(img =&gt; new 
{
img.EventImageId,
img.EventItemId,
img.EventImageFileExtension,
})
.ToList();

However its throwing an exception that I do not understand:

> KeyNotFoundException: The given key 'EmptyProjectionMember' was not present in the dictionary.
>
> System.Collections.Generic.Dictionary<TKey, TValue>.get_Item(TKey key)

If I remove the entire projection (.Select(...)) aspect, the first part of the query does return two images with all the fields populated, so I think that part is correct. I'm not sure what the error is relating to though.

Update

Swapping out certain elements provides strange results.

.Select(img =&gt; new object()) // Runs without an exception, but obviously doesn&#39;t actually give me what I need
.Select(img =&gt; new { }) // As above
.Select(img =&gt; new { Field1 = &quot;Foo&quot; }) // This fails with the same exception

Update 2

If I create a list from the initial search and then project from that, it appears to work:

var eventImages = (context.EventImages.AsNoTracking()
.Where(ei =&gt; uniqueEventIds.Contains(ei.EventItemId))
.GroupBy(img =&gt; img.EventItemId, (key, g) =&gt; g.OrderBy(img =&gt; img.EventImageId).FirstOrDefault()))
.ToList() // Fixes the issue
.Select(img =&gt; new 
{
img.EventImageId,
img.EventItemId,
img.EventImageFileExtension,
});

Looking at the underlying class I believe the exception was caused by the property being virtual but would still appreciate an authoritative answer please.

答案1

得分: 2

EF Core 6.0 / 7.0 查询翻译缺陷。但我们应该宽容,因为翻译所有 LINQ 查询表达式树的变化是非常复杂的事情(如果您尝试自己做的话),所以像这样的错误/缺陷会发生。

解决方法是在 FirstOrDefault 调用之前将投影移到 GroupBy 结果选择器内,例如:

.GroupBy(img => img.EventItemId, (key, g) => g
    .OrderBy(img => img.EventImageId)
    .Select(img => new 
    {
        img.EventImageId,
        img.EventItemId,
        img.EventImageFileExtension,
    })
    .FirstOrDefault()
)
英文:

EF Core 6.0 / 7.0 query translation defect. But we should be tolerant, since translation of all LINQ query expression tree variations is a very complicated thing (if you try to do that yourself), so bugs/defects like this happen.

The workaround is to move the projection before the FirstOrDefault call (inside the GroupBy result selector), e.g.

.GroupBy(img =&gt; img.EventItemId, (key, g) =&gt; g
    .OrderBy(img =&gt; img.EventImageId)
    .Select(img =&gt; new 
    {
        img.EventImageId,
        img.EventItemId,
        img.EventImageFileExtension,
    })
    .FirstOrDefault()
)

答案2

得分: 1

也许你的 .net 版本过旧。根据你的代码,我在 .net7 和 efcore 7 上尝试过,它能正常工作。

尝试这个看看它是否有效:

           var eventImages = context.EventImages.AsNoTracking()
           .Where(ei => uniqueEventIds.Contains(ei.EventItemId))
           .GroupBy(img => img.EventItemId) 
           .Select(x => x.FirstOrDefault())
           .Select(img => new 
               {
                   img.EventImageId,
                   img.EventItemId,
                   img.EventImageFileExtension,
               }).ToList();
英文:

Maybe your .net is old version. For your code I tried on .net7 efcore 7 it works fine

Try this to see if it works

           var eventImages = context.EventImages.AsNoTracking()
           .Where(ei =&gt; uniqueEventIds.Contains(ei.EventItemId))
           .GroupBy(img =&gt; img.EventItemId) 
           .Select(x =&gt; x.FirstOrDefault())
           .Select(img =&gt; new 
               {
                   img.EventImageId,
                   img.EventItemId,
                   img.EventImageFileExtension,
               }).ToList();

huangapple
  • 本文由 发表于 2023年5月14日 20:48:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/76247570.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定