实体框架6,急切加载嵌套关系返回空集合

isk*_*nar 3 .net c# entity-framework

我有一个看起来像这样的域模型(剥离了一些不重要的属性)

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; }
}
Run Code Online (Sandbox Code Playgroud)

简而言之,User与一对多的关系Recipe,与之有一对多的关系Ingredient.我正在使用代码优先方法,这个映射在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);
Run Code Online (Sandbox Code Playgroud)

我正在使用存储库,它具有此代码将数据存储到MySQL数据库中:

public void Add(User entity)
{
    using (var context = new UserContext())
    {
        context.Users.Add(entity);
        context.SaveChanges();
    }
}
Run Code Online (Sandbox Code Playgroud)

和取:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

我有集成测试,它创建一个User对象,List<Recipe>有两个食谱,每个食谱有List<Ingredient>2个项目.每种食谱都有所不同,总共有4种成分.当我将它添加到存储库时,我可以在数据库中看到它具有正确的PK和FK.

但是,从数据库中检索时,返回的User对象有配方,但这些配方都没有配方.由于我设置我的getter的方式,当我尝试访问它们时,我收到一个空集合.

例如这样做:

/* 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();
Run Code Online (Sandbox Code Playgroud)

returnedIngredients是空的,虽然userIngredients有两个元素.然后这会失败断言并导致集成测试失败.

有人能告诉我如何在嵌套的一对多上正确地进行加载吗?

Jcl*_*Jcl 6

这个:

get { return _recipes ?? new List<Recipe>(); } 
Run Code Online (Sandbox Code Playgroud)

应该读:

get { return _recipes ?? (_recipes = new List<Recipe>()); } 
Run Code Online (Sandbox Code Playgroud)

成分相同.

否则,每次都会返回一个新的空列表(因为它没有存储在任何地方).这仅在整个列表设置在某一点(从数据库读取并覆盖)时才有效,但如果您是按代码创建实体则不会,因为它们将被添加到未存储在支持字段中的列表中.

您看到它Recipes已满,因为您在此处设置它:User user = new User {Recipes = recipes /* plus some omitted properites*/};,因此它存储在支持字段中.

但是当你添加它们时,成分不是:

var recipe = new Recipe();
recipe.Ingredients.Add(myIngredient); // <-- this will be stored in a 
                                      //     new List<Ingredient>, 
                                      //     but not on the 
                                      //     _ingredients backing field
                                      //     since the get accesor doesn't 
                                      //     set it
recipe.Ingredients.Add(myIngredient); // <-- this will be stored in a 
                                      //     new List<Ingredient>, not in 
                                      //     the expected created one
Run Code Online (Sandbox Code Playgroud)

至于急切的加载,你不需要这个:

 var user = context.Users
        .Include(u => u.Recipes)
        .Include(u => u.Recipes.Select(x => x.Ingredients))
        .FirstOrDefault(u => u.UserId == id);
Run Code Online (Sandbox Code Playgroud)

只需:

 var user = context.Users
        .Include(u => u.Recipes.Select(x => x.Ingredients))
        .FirstOrDefault(u => u.UserId == id);
Run Code Online (Sandbox Code Playgroud)

它会加载两个级别

  • 这是不正确的.在查询嵌套集合时,必须使用SELECT语法,据我所知 (2认同)