I have a domain model that looks like this (stripped of some unimportant properties)
public class User
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public virtual int UserId { get; set; }
private ICollection<Recipe> _recipes;
public virtual ICollection<Recipe> Recipes
{
get { return _recipes ?? new List<Recipe>(); }
set { _recipes = value; }
}
}
public class Recipe
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int RecipeId { get; set; }
private ICollection<Ingredient> _ingredients;
public virtual ICollection<Ingredient> Ingredients
{
get { return _ingredients ?? new List<Ingredient>(); }
set { _ingredients = value; }
}
public User User { get; set; }
}
public class Ingredient
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int IngredientId { get; set; }
public Recipe Recipe { get; set; }
}
So in short, User has one-to-many relationship to Recipe, which in turn has one-to-many relationship to Ingredient. I'm using code first approach, with this mapping in protected override void OnModelCreating(DbModelBuilder modelBuilder) :
modelBuilder.Entity<User>()
.HasMany(u => u.Recipes)
.WithRequired(u => u.User)
.WillCascadeOnDelete(true);
modelBuilder.Entity<Recipe>()
.HasMany(u => u.Ingredients)
.WithRequired(u => u.Recipe)
.WillCascadeOnDelete(true);
and I'm using repository, which has this code to store data into MySQL database:
public void Add(User entity)
{
using (var context = new UserContext())
{
context.Users.Add(entity);
context.SaveChanges();
}
}
and fetching:
public User GetById(int id)
{
using (var context = new UserContext())
{
var user = context.Users
.Include(u => u.Recipes)
.Include(u => u.Recipes.Select(x => x.Ingredients))
.FirstOrDefault(u => u.UserId == id);
return user;
}
}
I have integration tests, which create a User object, List<Recipe> with two recipes where each has List<Ingredient> with 2 items. They are different for each recipe, so 4 ingredients total. When I add it to the repository, I can see it in the database with correct PKs and FKs.
However when retrieving from database, returned User object has recipes, but neither of those recipes has the ingredients in them. Due the the way I set up my getters, when I try to access, them, I receive an empty collection.
e.g. doing this:
/* create recipes collection and seed it */
User user = new User {Recipes = recipes /* plus some omitted properites*/};
_repository.Add(user);
var returnedUser = _repository.GetById(user.UserId);
var userIngredients = user.Recipes.First(u => u.RecipeName == "Test").Ingredients.ToList();
var returnedIngredients = returnedUser.Recipes.First(u => u.RecipeName == "Test").Ingredients.ToList();
returnedIngredients is empty, while userIngredients has two elements. This then fails assertion and results in failed integration test.
Can someone tell me how to properly do eager loading on nested one-to-many?
.Include(u => u.Recipes), you don't need it. To include a collection and then a collection one level down use.Include(e => e.Level1Collection.Select(l1 => l1.Level2Collection))(your second include).get { return _recipes ?? new List<Recipe>(); }