Adam Greenall
Adam Greenall

Reputation: 189

How to avoid a NullReferenceException when using a nested property via projection with Entity Framework Core

I'm having an issue performing a DTO projection with Entity Framework Core 2.2 whereby one class has a property that references another property from a parent class. When I attempt the projection a NullReferenceException is thrown because the parent object is null.

Here is a contrived example of what I'm trying to achieve:

public class Vendor
{
    public ICollection<Item> Items { get; set; }
    public decimal Tax { get; set; }
}

public class Item
{
    public Vendor Vendor { get; set; }
    public string Name { get; set; }
    public decimal ItemPrice { get; set; }
    public decimal TotalPrice => ItemPrice * (1 + Vendor.Tax);
}

public class ItemDto
{
    public string Name { get; set; }
    public decimal TotalPrice { get; set; }
}

private Task<IEnumerable<ItemDto>> Projection()
{
    var results = await _context.Item
        .Select(t => new ItemDto()
        {
            Name = t.Name,
            TotalPrice = t.TotalPrice
        })
        .ToListAsync();

    return results;
}

I am able to get this to work by either:

I've also tried including Vendor in the query, but that doesn't help either. Any ideas on how to solve this would be greatly appreciated!

Upvotes: 0

Views: 577

Answers (2)

Neil
Neil

Reputation: 11889

Your projection is not actually loading all the data it requires:

private Task<IEnumerable<ItemDto>> Projection()
{
  var results = await _context.Item
    .Include(x=>x.Vendor) <--- Make sure you have all the data required
    .Select(t => new ItemDto()
    {
        Name = t.Name,
        TotalPrice = t.TotalPrice
    })
    .ToListAsync();

  return results;
}

Upvotes: -1

Svyatoslav Danyliv
Svyatoslav Danyliv

Reputation: 27396

EF Translator cannot look into property body, so it cannot create appropriate query.

Simpler solution is to do that in the query

private Task<IEnumerable<ItemDto>> Projection()
{
    var results = await _context.Item
        .Select(t => new ItemDto()
        {
            Name = t.Name,
            TotalPrice = t.ItemPrice * (1 + t.Vendor.Tax)
        })
        .ToListAsync();

    return results;
}

For sure it is not reusable way. So maybe you will find this my answer helpful: https://stackoverflow.com/a/66386142/10646316

Upvotes: 2

Related Questions