![](/img/trans.png)
[英]Entity Framework Core Eager Loading Then Include on a collection
[英]Entity Framework 6, eager loading nested relationship returns empty collection
我有一個看起來像這樣的域模型(剝離了一些不重要的屬性)
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; }
}
簡而言之, User
與Recipe
一對多的關系,而Recipe
與Ingredient
之間存在一對多的關系。 我正在使用代碼第一種方法,在protected override void OnModelCreating(DbModelBuilder modelBuilder)
使用此映射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);
我正在使用存儲庫,它具有此代碼將數據存儲到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 /* 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
至於急切的加載,你不需要這個:
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);
它會加載兩個級別
幾點想法:
public class Ingredient
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int IngredientId { get; set; }
public virtual Recipe Recipe { get; set; } // <-- make this virtual
}
...和...
public class Recipe
{
public Recipe()
{
// set up a default collection during construction
Ingredients = new List<Ingredient>();
// could also be Ingredients = new Collection<Ingredient>();
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int RecipeId { get; set; }
// private ICollection<Ingredient> _ingredients; <-- don't back
public virtual ICollection<Ingredient> Ingredients
{
get; // <-- should never return null because of constructor
protected set; // and because externals cannot set to null
}
public virtual User User { get; set; } // <-- make this virtual too
}
也可能值得向依賴實體添加一些外鍵屬性(例如,成分中的RecipeId)並更新modelBuilder定義。 添加fk屬性之后會發生以下情況:
modelBuilder.Entity<Recipe>()
.HasMany(u => u.Ingredients)
.WithRequired(u => u.Recipe)
.HasForeignKey(u => u.RecipeId) // <-- change is here
.WillCascadeOnDelete(true);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.