简体   繁体   中英

Entity Framework - list of items is not loading

I'm trying to save games in database with EntityFramework Code-First.

There are fragments of my classes:

/// <summary>
/// Game class
/// </summary>
public class Game : ObservableObject
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int GameId { get; set; }

    /// <summary>
    /// List of teams
    /// <see cref="Models.Teams"/>
    /// </summary>
    public Teams Teams
    {
        get
        {
            return teams;
        }
        set
        {
            teams = value;
            OnPropertyChanged("Teams");
        }
    }
}


/// <summary>
/// Teams class
/// </summary>
public class Teams : ObservableObject, IEnumerable<Team>
{
    public int GameId { get; set; }

    public Game Game { get; set;}

    List<Team> teamsList = new List<Team>();

    /// <summary>
    /// List of teams
    /// </summary>
    public List<Team> TeamsList
    {
        get
        {
            return teamsList;
        }
        set
        {
            teamsList = value;
            OnPropertyChanged("TeamsList");
        }
    }
}

/// <summary>
/// Team (group of players)
/// </summary>
public class Team : ObservableObject
{


    #region Properties & fields

    List<Player> players = null;
    static int NumberOfTeams = 0;

    /// <summary>
    /// List of players in team
    /// </summary>
    public List<Player> Players
    {
        get
        {
            return players;
        }
        set
        {
            players = value;

            OnPropertyChanged("NumberOfPlayers");
            OnPropertyChanged("IsEmpty");
            OnPropertyChanged("IsNotDefault");
            OnPropertyChanged("Players");
        }
    }
}

/// <summary>
///Defines player
/// </summary>
public class Player : Models.Person
{

    /// <summary>
    /// Identifies player in game. Also lets to recover player's statistics from last month
    /// </summary>
    public int PlayerId { get; set; }
}

And the Entity Framework part:

 public class GameContext : DbContext
{
    public GameContext() : base("Db")
    {
        this.Configuration.LazyLoadingEnabled = false;
        this.Configuration.ProxyCreationEnabled = true;
    }

    public DbSet<Models.Game> Games { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        //Configure domain classes using modelBuilder here

        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<Models.Devices>().HasKey(d => d.GameId);
        modelBuilder.Entity<Models.Players>().HasKey(p => p.TeamId);
        modelBuilder.Entity<Models.Teams>().HasKey(t => t.GameId);
        modelBuilder.Entity<Models.Scenario>().HasKey(s => s.GameId);

        modelBuilder.Entity<Models.Game>().HasOptional<Models.Teams>(g => g.Teams).WithRequired(t => t.Game);
        modelBuilder.Entity<Models.Game>().HasOptional<Models.Scenario>(g => g.Scenario).WithRequired(s => s.Game);
        modelBuilder.Entity<Models.Game>().HasOptional<Models.Devices>(g => g.Devices).WithRequired(d => d.Game);

    }
}
 public static class GameService
{
    public static List<Models.Game> GetGames()
    {
        using (var gctx = new Contexts.GameContext())
        {
            return gctx.Games.ToList();
        }
    }

    public static void InsertGame(Models.Game game)
    {
        using (var gctx = new Contexts.GameContext())
        {
            gctx.Games.Add(game);
            gctx.SaveChanges();
        }
    }
}

I'm trying to use it all like this:

List<Models.Game> games = Services.GameService.GetGames().ToList();

        foreach(var game in games)
        {
            Console.WriteLine("Game " + game.GameId);

            foreach (var team in game.Teams)
            {
                Console.Write("\t");
                Console.WriteLine("Team" + team.Number);
                foreach(var player in team.Players)
                {
                    Console.Write("\t\t");
                    Console.WriteLine("Player" + player.PlayerId);
                }
            }
        }

When I run this code i see my games, teams for that games, but i can't reach players for each team. So my question is: what I'm doing wrong here?

OK, I resolved my problem. There were few reasons which caused the problem.

  1. Need to use Include for Eager Loading. Method looks like that:

     public static List<Models.Game> GetGames() { using (var gctx = new Contexts.DatabaseContext()) { return gctx.Games.Include(g => g.Teams).Include(g => g.Teams.TeamsList.Select(t => t.Players)).ToList(); } } 
  2. I used Fluent API to determine relationships (overriding DatabaseContext.OnModelCreating):

     protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<Models.Game>().HasOptional(g => g.Teams).WithOptionalPrincipal(t => t.Game); // 1 -> 0..1 modelBuilder.Entity<Models.Game>().HasOptional(g => g.Devices).WithOptionalPrincipal(d => d.Game); // 1 -> 0..1 modelBuilder.Entity<Models.Game>().HasOptional(g => g.Scenario).WithOptionalPrincipal(s => s.Game); // 1-> 0..1 modelBuilder.Entity<Models.Teams>().HasMany(ts => ts.TeamsList).WithRequired(t => t.Teams); // 1 -> many modelBuilder.Entity<Models.Team>().HasMany(t => t.Players).WithRequired(p => p.Team); // 1 -> many base.OnModelCreating(modelBuilder); } 
  3. Only in Game class navigation properties are NOT virtual.

Thanks for any help in this case. You're wonderful, guys!

You should add the virtual keyword to your navigation properties, that is, the properties that will be automatically filled by EF. Specially if you are using lazy loading. If a property is not declared as virtual then EF will not be able to replace it by a proxy, and so the lazy loading will not work. You will not get exceptions, but the data will not be loaded. Try that with the Players list property:

public virtual List<Player> Players 

Also, your model is a bit strange. The keys in the entities are supposed to be identifiers for the entity itself, something like this:

modelBuilder.Entity<Game>().HasKey(g => g.GameId);
modelBuilder.Entity<Team>().HasKey(t => t.TeamId);
modelBuilder.Entity<Player>().HasKey(p => p.PlayerId);

That is because they will be the primary keys for those entities. You are using them instead more like foreign keys.

Entity Framework lazy loads objects.

That is, it doesn't actually load each Player object in the Team object within the Game object. Rather it returns a proxy to it.

Try returning an IQueryable in the GetGames() method:

public static class GameService
{
    public static IQueryable<Models.Game> GetGames()
    {
        using (var gctx = new Contexts.GameContext())
        {
            return gctx.Games.Select(....);
        }
    }
}

I'm also migrating from ASPNET MVC 5 to .NET Core 6

  1. Search your whole project for : DbContext , it will find your data context class.

  2. Notice that this class found also has some methods that return DBSet . These are the Models that represent tables in the database and you can access those directly.

  3. Import your data context class as a private property of your controllers like:

    private readonly SampleDbContext _context;

  4. Access your models (DBSets) with chained methods. Query example to return single user:

    _context.Users.FirstOrDefault(u => u.Name == input.Name);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM