加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

c# – Entity Framework 6,急切加载嵌套关系返回空集合

发布时间:2020-12-15 23:22:29 所属栏目:百科 来源:网络整理
导读:我有一个看起来像这样的域模型(剥离了一些不重要的属性) public class User{ [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public virtual int UserId { get; set; } private ICollectionRecipe _recipes; public virtual ICollectionRecip
我有一个看起来像这样的域模型(剥离了一些不重要的属性)

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; }
}

简而言之,用户与Recipe有一对多的关系,而Recipe与Ingredient之间存在一对多的关系.我正在使用代码第一种方法,在受保护的覆盖中使用此映射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);

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

public void Add(User entity)
{
    using (var context = new UserContext())
    {
        context.Users.Add(entity);
        context.SaveChanges();
    }
}

和取:

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;
    }
}

我有集成测试,它创建一个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();

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

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

解决方法

这个:

get { return _recipes ?? new List<Recipe>(); }

应该读:

get { return _recipes ?? (_recipes = new List<Recipe>()); }

成分相同.

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

你看到Recipes已满,因为你在这里设置它:User user = new User {Recipes = recipes / *加上一些省略的属性* /};所以它存储在支持字段中.

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

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

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

var user = context.Users
        .Include(u => u.Recipes)
        .Include(u => u.Recipes.Select(x => x.Ingredients))
        .FirstOrDefault(u => u.UserId == id);

只需:

var user = context.Users
        .Include(u => u.Recipes.Select(x => x.Ingredients))
        .FirstOrDefault(u => u.UserId == id);

它会加载两个级别

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读