Below is the model.
class Product
{
public int Id { get; set; }
public ICollection<Categorization> Categorization { get; set; }
}
class Categorization
{
public int ProductId { get; set; }
public Product Product { get; set; }
public int CategoryId { get; set; }
public Category Category { get; set; }
}
class Category
{
public int Id { get; set; }
public ICollection<Categorization> Categorization { get; set; }
}
I am trying to list down all the Products and their Category as well. API Code as below
foreach (var product in _context.Products.ToList()){
var categories = product.Categorization.Select(c => c.Category);
...
}
When we call the Api, I get this error:
System.ArgumentNullException: Value cannot be null.
Parameter name: source
at System.Linq.Enumerable.Select[TSource,TResult](IEnumerable1 source, Func
2 selector)
product.Categorization
is null because it's not loaded. If you're expecting lazy loading to handle the load you must make the product.Categorization
property virtual (along with the properties you wish to lazy load in the Categorization
class, Category
in this case)
Edit: in your example you should eager load the data using Include
and ThenInclude
as you know you need the data:
_context.Products
.Include( p => p.Categorization )
.ThenInclude( c => c.Category )
.ToList()
For querying all products and their categories, you could try Include
and ThenInclude
like below:
public async Task<IActionResult> GetAllArticleAsync()
{
var articles = await _context.Articles
.Include(a => a.ArticleTags)
.ThenInclude(at => at.Tag)
.Select(a => new
{
Id = a.Id,
ArticleName = a.ArticleName,
Tags = a.ArticleTags.Select(at => at.Tag).ToList()
})
.ToListAsync();
return Ok(articles);
}
For this way, you need to configure opt.SerializerSettings.ReferenceLoopHandling
in Startup.cs
like below:
services.AddMvc()
.AddJsonOptions(opt => opt.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
For a complete demo, refer EFCorePro