Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Error projecting from a grouped linq query

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.

like image 356
EvilDr Avatar asked Oct 16 '25 10:10

EvilDr


1 Answers

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()
)
like image 167
Ivan Stoev Avatar answered Oct 18 '25 06:10

Ivan Stoev



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!