3

I'm creating an ASP.NET MVC4 tournament administration system which as a containing class Tournament with a collection of Round objects. I'm new to EF Code First, but I've understood that EF is supposed to initialize my collection objects with observed proxy classes upon instantiation, as long as I've declared them as virtual. Why are they null when I try to add elements to them from the ASP.NET controller in the code below?

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

update

Reinitializing the dataContext after saving the entity, solves my problem. But in not the right way. Original question stands.

Altered CreateTournament-method

[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 });
}
Community
  • 1
  • 1
Nilzor
  • 18,082
  • 22
  • 100
  • 167
  • Take a look at: http://stackoverflow.com/questions/4069563/why-is-my-entity-framework-code-first-proxy-collection-null-and-why-cant-i-set – Marthijn Jan 10 '14 at 16:28

1 Answers1

4

It would only work as you expect if you were attaching the created dbTournament to the context. Only in that case creating the collection and preparing it for lazy loading makes sense. But you are adding the dbTournament as a new entity and in this case there can't be any dependent Rounds in the database that could refer to the new tournament, hence a lazy loading query wouldn't return a result anyway and EF doesn't create the collection for lazy loading in the first place.

You could apply tricks like attaching the dbTournament first and after that adding it to the context. But that would only yield unnecessary database queries triggered by lazy loading with no result and is rather hacky. In my opinion the simplest and most reasonable solution is either the standard collection initialization in a default constructor...

public Tournament()
{
    Rounds = new HashSet<Round>();
}

...or at least a guard in your InitializeDefaults method:

public void InitializeDefaults()
{
    var round = new Round("Round 1");
    if (Rounds == null)
        Rounds = new HashSet<Round>();
    Rounds.Add(round);
}
Community
  • 1
  • 1
Slauma
  • 175,098
  • 59
  • 401
  • 420
  • 2
    If I initialize the `HashSet` in the constructor, will that not hinder a *change tracking proxy* when reading at later point, as quoted from Rowan Miller in this SO-answer? http://stackoverflow.com/a/4069727/507339 . I'm going for the Guard in `InitializeDefaults` to be on the safe side. As a side note I feel this part of Entity Framework is poorly documented (or I have not found it) – Nilzor Jan 11 '14 at 11:15