Почему Entity Framework не инициализирует мои навигационные коллекции?
-
21-12-2019 - |
Вопрос
Я создаю систему администрирования турниров ASP.NET MVC4, которая в качестве содержащего класса Tournament
с коллекцией Round
объекты.Сначала я новичок в коде EF, но я понял, что EF - это предполагается инициализировать объекты моей коллекции с наблюдаемыми прокси-классами при создании экземпляра, если я объявил их виртуальными.Почему они равны нулю, когда я пытаюсь добавить к ним элементы из контроллера ASP.NET в приведенном ниже коде?
public class Tournament
{
public int Id { get; set; }
[Required]
public String Name { get; set; }
public virtual ICollection<Contestant> Contestants { get; set; }
public virtual ICollection<Round> Rounds { get; set; }
public void InitializeDefaults()
{
var round = new Round("Round 1");
Rounds.Add(round); // Generates NullReferenceException when called from controller
}
}
public class Round
{
public long Id { get; set; }
public int MaxContestants { get; set; }
public String Title { get; set; }
public Round() { }
public Round(string title) : this()
{
Title = title;
}
}
public class MainController {
// (...)
[HttpPost]
public ActionResult CreateTournament(Tournament tournament)
{
var db = new DataContext();
var dbTournament = db.Tournaments.Create();
dbTournament.Name = tournament.Name;
db.Tournaments.Add(dbTournament);
dbTournament.InitializeDefaults();
db.SaveChanges();
return RedirectToRoute(new { action = "Main", tournamentId = dbTournament.Id });
}
}
public class DataContext : DbContext
{
public IDbSet<Tournament> Tournaments { get; set; }
public IDbSet<Judge> Judges { get; set; }
public IDbSet<Contestant> Contestants { get; set; }
}
обновление
Повторная инициализация DataContext после сохранения объекта решает мою проблему.Но не в том смысле, в каком нужно.Первоначальный вопрос остается в силе.
Измененный CreateTournament
-способ
[HttpPost]
public ActionResult CreateTournament(Tournament tournament)
{
var db = App.ServiceLocator.GetDataBase();
db.Tournaments.Add(tournament);
db.SaveChanges();
db.Dispose();
// Reinitializing the datacontext
db = App.ServiceLocator.GetDataBase();
var dbTournament = db.GetTournament(tournament.Id);
dbTournament.InitializeDefaults();
db.SaveChanges();
return RedirectToRoute(new { action = "Main", tournamentId = dbTournament.Id });
}
Решение
Это сработало бы только так, как вы ожидаете если бы ты был присоединение созданный dbTournament
к контексту.Только в этом случае создание коллекции и подготовка ее к отложенной загрузке имеет смысл.Но ты такой добавление то dbTournament
как новая сущность, и в этом случае не может быть никаких зависимых Round
s в базе данных, которые могли бы ссылаться на новый турнир, следовательно, запрос отложенной загрузки в любом случае не вернул бы результат, и EF в первую очередь не создает коллекцию для отложенной загрузки.
Вы мог бы применять хитрости например, прикрепление dbTournament
сначала и после этого добавляем его в контекст.Но это привело бы только к ненужным запросам к базе данных, вызванным отложенной загрузкой, без результата и довольно халтурно.На мой взгляд, самым простым и разумным решением является либо стандартная инициализация коллекции в конструкторе по умолчанию...
public Tournament()
{
Rounds = new HashSet<Round>();
}
...или, по крайней мере, охранник в вашем InitializeDefaults
метод:
public void InitializeDefaults()
{
var round = new Round("Round 1");
if (Rounds == null)
Rounds = new HashSet<Round>();
Rounds.Add(round);
}