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.
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
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.
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()
)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With