Specific Entity Framework Code First Many to 2 Mod

2019-07-27 13:00发布

问题:

I'm a bit of a loss here.

Basically I have these two Models:

public class Player
{
    public int PlayerId { get; set; }
    public string Name { get; set; }

    public virtual ICollection<Game> Games { get; set; }
}

public class Game
{
    public int GameId { get; set; }

    public virtual Player PlayerBlack { get; set; }
    public virtual Player PlayerWhite { get; set; }
}

Now the Database Schema, EF Code First creates for me, is not correct because the Game table gets 3 Foreign Keys (Playerblack, PlayerWhite and Player) instead of 2.

So how can I tie these Models together so that EF understands that the Players Games are found by either looking at the Black or White Player.

Basically each time I call myPlayer.Games EF has to look into PlayerBlack AND PlayerWhite Foreign Keys.

Is it even possible?

回答1:

I believe it's not possible. You cannot have an association with a single endpoint on one side and two endpoints on the other side of the relation.

Possible workarounds:

  • Use two collections in the Player class:

    public virtual ICollection<Game> GamesAsBlackPlayer { get; set; }
    public virtual ICollection<Game> GamesAsWhitePlayer { get; set; }
    

    Depending on your context you could perhaps merge these collections together to a readonly collection Games which is not mapped to the database.

(Edit: Proposed second workaround was crap, deleted now.)

Edit2: Another workarund could be to extend you class model by an additional class PlayerInGame:

public class Player
{
    public int PlayerId { get; set; }
    public string Name { get; set; }

    public virtual ICollection<PlayerInGame> PlayerInGames { get; set; }

    // optional helper property
    [NotMapped]
    public IEnumerable<Game> Games
    {
        get
        {
            return PlayerInGames.Select(g => g.Game);
        }
    }
}

public class Game
{
    public int GameId { get; set; }
    public virtual ICollection<PlayerInGame> PlayersInGame { get; set; }

    [NotMapped]
    public Player PlayerBlack
    {
        get
        {
            return PlayersInGame.Single(p => p.WhiteOrBlack == "B").Player;
        }
    }

    [NotMapped]
    public Player PlayerWhite
    {
        get
        {
            return PlayersInGame.Single(p => p.WhiteOrBlack == "W").Player;
        }
    }
}

public class PlayerInGame
{
    public int PlayerInGameId { get; set; }

    public virtual Game Game { get; set; }
    public virtual Player Player { get; set; }
    public string WhiteOrBlack { get; set; }
}

As you can see by the Single method in the PlayerBlack and PlayerWhite properties in the Game class you have to make sure in your business logic to create the proper PlayerInGame entities so that your PlayersInGame collection always has two elements with Black or White flag respectively.



回答2:

I would tackle the problem like this:

public abstract class Player
{
    public int ID { get; set; }
}

public class WhitePlayer : Player
{
}

public class BlackPlayer : Player
{
}

public class Game
{
    public int ID { get; set; }

    public virtual WhitePlayer WhitePlayer { get; set; }
    public virtual BlackPlayer BlackPlayer { get; set; }
}

public class GamePlayerContext : DbContext
{
    public DbSet<Game> Games { get; set; }
    public DbSet<Player> Players { get; set; }
}