Pergunta

Estou criando um sistema de administração de torneios ASP.NET MVC4 que, como classe contendo Tournament com uma coleção de Round objetos.Sou novo no EF Code First, mas entendi que o EF é deveria inicializar minha coleção de objetos com classes de proxy observadas na instanciação, desde que eu os tenha declarado como virtuais.Por que eles são nulos quando tento adicionar elementos a eles do controlador ASP.NET no código abaixo?

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

atualizar

Reinicializar o dataContext após salvar a entidade resolve meu problema.Mas não da maneira certa.A questão original permanece.

Alterado CreateTournament-método

[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 });
}
Foi útil?

Solução

Só funcionaria como você espera se você fosse anexando o criado dbTournament ao contexto.Somente nesse caso faz sentido criar a coleção e prepará-la para carregamento lento.Mas você é adicionando o dbTournament como uma nova entidade e neste caso não pode haver nenhum dependente Rounds no banco de dados que poderia se referir ao novo torneio, portanto, uma consulta de carregamento lento não retornaria um resultado de qualquer maneira e a EF não cria a coleção para carregamento lento em primeiro lugar.

Você poderia aplicar truques como anexar o dbTournament primeiro e depois adicionando-o ao contexto.Mas isso só geraria consultas desnecessárias ao banco de dados, acionadas por carregamento lento, sem resultado e é bastante hackeado.Na minha opinião, a solução mais simples e razoável é a inicialização da coleção padrão em um construtor padrão...

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

...ou pelo menos um guarda em seu InitializeDefaults método:

public void InitializeDefaults()
{
    var round = new Round("Round 1");
    if (Rounds == null)
        Rounds = new HashSet<Round>();
    Rounds.Add(round);
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top