英文:
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<WebAppRazorUser, ApplicationRole, int>
{
public DbSet<EventImage> 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's
.Where(ei => uniqueEventIds.Contains(ei.EventItemId))
// Get only one image per parent
.GroupBy(img => img.EventItemId, (key, g) => g.OrderBy(img => img.EventImageId).FirstOrDefault())
// Project the image fields without the binary data
.Select(img => 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 => new object()) // Runs without an exception, but obviously doesn't actually give me what I need
.Select(img => new { }) // As above
.Select(img => new { Field1 = "Foo" }) // 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 => uniqueEventIds.Contains(ei.EventItemId))
.GroupBy(img => img.EventItemId, (key, g) => g.OrderBy(img => img.EventImageId).FirstOrDefault()))
.ToList() // Fixes the issue
.Select(img => 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 => img.EventItemId, (key, g) => g
.OrderBy(img => img.EventImageId)
.Select(img => 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 => uniqueEventIds.Contains(ei.EventItemId))
.GroupBy(img => img.EventItemId)
.Select(x => x.FirstOrDefault())
.Select(img => new
{
img.EventImageId,
img.EventItemId,
img.EventImageFileExtension,
}).ToList();
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论