EF 代码优先外键关系
EF Code First Foreign Key Relationship
我有以下情况:
- 我有几个账号和几个游戏table。
- 每个游戏table都有一个创作者。
- 每个帐户都可以放在一个 table 上(但不是必须的)。
我的模型 类 看起来像这样:
public class GameTable
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id
{
get;
set;
}
[Required]
public string Name { get; set; }
[Required]
public string Description { get; set; }
public Guid CreatorId { get; set; }
[ForeignKey(nameof(CreatorId))]
public Account Creator { get; set; }
}
public class Account
{
public Account()
{
Elo = 1000;
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
[Required]
[MaxLength(15)]
[MinLength(4)]
public string Name { get; set; }
[Required]
[MaxLength(32)]
[MinLength(32)]
public string PasswordHash { get; set; }
public int Elo { get; set; }
public bool IsAdmin { get; set; }
public Guid? CurrentTableId { get; set; }
[ForeignKey(nameof(CurrentTableId))]
public virtual GameTable CurrentTable { get; set; }
}
我的上下文目前是这样的:
[Export(typeof(MyDbContext)), PartCreationPolicy(CreationPolicy.Shared)]
[DbConfigurationType(typeof(MySqlEFConfiguration))]
public class MyDbContext : DbContext
{
public MyDbContext() : base("DbContextCon")
{
Database.SetInitializer<PiksenDbContext>(new CreateDatabaseIfNotExists<PiksenDbContext>());
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
public virtual DbSet<Account> Accounts { get; set; }
public virtual DbSet<SecurityToken> SecurityTokens { get; set; }
public virtual DbSet<GameTable> GameTables { get; set; }
}
不幸的是,它不适用于像这样设置的两个外键。以下消息让我现在有些头疼:
System.InvalidOperationException: 'Unable to determine the principal
end of an association between the types 'namespace.GameTable' and
'namespace.Account'. The principal end of this association must be
explicitly configured using either the relationship fluent API or data
annotations.'
然而,当我注释掉其中一个 FK-Relationships 时,一切都很好。这两者之间似乎存在某种冲突。
我试图获得的结果是 Account
中的一个可空外键 CurrentTableId
和 GameTable
中的一个不可空外键 CreatorId
.
你想创建一个自引用 GameTable table。
为此,您需要将 Collection 属性 添加到 Account table like..
public virtual ICollection<GameTable> Children { get; set; }
整个代码块看起来像
public class GameTable
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id
{
get;
set;
}
[Required]
public string Name { get; set; }
[Required]
public string Description { get; set; }
public Guid CreatorId { get; set; }
[ForeignKey(nameof(CreatorId))]
public Account Creator { get; set; }
}
public class Account
{
public Account()
{
Elo = 1000;
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
[Required]
[MaxLength(15)]
[MinLength(4)]
public string Name { get; set; }
[Required]
[MaxLength(32)]
[MinLength(32)]
public string PasswordHash { get; set; }
public int Elo { get; set; }
public bool IsAdmin { get; set; }
public Guid? CurrentTableId { get; set; }
[ForeignKey(nameof(CurrentTableId))]
public virtual GameTable CurrentTable { get; set; }
public virtual ICollection<GameTable> Children { get; set; }
}
希望能解决您的问题。
在 FK 关系的每一侧,您可以(通常应该)具有导航属性。并且当存在多个FK关系时,可以使用InversePropertyAttribute来指定一对Navigation Properties代表哪个FK。
EG
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ef62test
{
class Program
{
public class GameTable
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id
{
get;
set;
}
[Required]
public string Name { get; set; }
[Required]
public string Description { get; set; }
public Guid CreatorId { get; set; }
[ForeignKey(nameof(CreatorId))]
public Account Creator { get; set; }
[InverseProperty(nameof(Account.CurrentTable))]
public ICollection<Account> CurrentPlayers { get; } = new HashSet<Account>();
}
public class Account
{
public Account()
{
Elo = 1000;
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
[Required]
[MaxLength(15)]
[MinLength(4)]
public string Name { get; set; }
[Required]
[MaxLength(32)]
[MinLength(32)]
public string PasswordHash { get; set; }
public int Elo { get; set; }
public bool IsAdmin { get; set; }
public Guid? CurrentTableId { get; set; }
[ForeignKey(nameof(CurrentTableId))]
public virtual GameTable CurrentTable { get; set; }
[InverseProperty(nameof(GameTable.Creator))]
public ICollection<GameTable> CreatedTables { get; } = new HashSet<GameTable>();
}
public class MyDbContext : DbContext
{
public MyDbContext() : base("DbContextCon")
{
Database.SetInitializer<MyDbContext>(new CreateDatabaseIfNotExists<MyDbContext>());
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
public virtual DbSet<Account> Accounts { get; set; }
//public virtual DbSet<SecurityToken> SecurityTokens { get; set; }
public virtual DbSet<GameTable> GameTables { get; set; }
}
static void Main(string[] args)
{
Database.SetInitializer(new DropCreateDatabaseAlways<MyDbContext>());
using (var db = new MyDbContext())
{
db.Database.CreateIfNotExists();
}
Console.WriteLine("Hit any key to exit.");
Console.ReadKey();
}
}
}
我有以下情况:
- 我有几个账号和几个游戏table。
- 每个游戏table都有一个创作者。
- 每个帐户都可以放在一个 table 上(但不是必须的)。
我的模型 类 看起来像这样:
public class GameTable
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id
{
get;
set;
}
[Required]
public string Name { get; set; }
[Required]
public string Description { get; set; }
public Guid CreatorId { get; set; }
[ForeignKey(nameof(CreatorId))]
public Account Creator { get; set; }
}
public class Account
{
public Account()
{
Elo = 1000;
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
[Required]
[MaxLength(15)]
[MinLength(4)]
public string Name { get; set; }
[Required]
[MaxLength(32)]
[MinLength(32)]
public string PasswordHash { get; set; }
public int Elo { get; set; }
public bool IsAdmin { get; set; }
public Guid? CurrentTableId { get; set; }
[ForeignKey(nameof(CurrentTableId))]
public virtual GameTable CurrentTable { get; set; }
}
我的上下文目前是这样的:
[Export(typeof(MyDbContext)), PartCreationPolicy(CreationPolicy.Shared)]
[DbConfigurationType(typeof(MySqlEFConfiguration))]
public class MyDbContext : DbContext
{
public MyDbContext() : base("DbContextCon")
{
Database.SetInitializer<PiksenDbContext>(new CreateDatabaseIfNotExists<PiksenDbContext>());
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
public virtual DbSet<Account> Accounts { get; set; }
public virtual DbSet<SecurityToken> SecurityTokens { get; set; }
public virtual DbSet<GameTable> GameTables { get; set; }
}
不幸的是,它不适用于像这样设置的两个外键。以下消息让我现在有些头疼:
System.InvalidOperationException: 'Unable to determine the principal end of an association between the types 'namespace.GameTable' and 'namespace.Account'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.'
然而,当我注释掉其中一个 FK-Relationships 时,一切都很好。这两者之间似乎存在某种冲突。
我试图获得的结果是 Account
中的一个可空外键 CurrentTableId
和 GameTable
中的一个不可空外键 CreatorId
.
你想创建一个自引用 GameTable table。 为此,您需要将 Collection 属性 添加到 Account table like..
public virtual ICollection<GameTable> Children { get; set; }
整个代码块看起来像
public class GameTable
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id
{
get;
set;
}
[Required]
public string Name { get; set; }
[Required]
public string Description { get; set; }
public Guid CreatorId { get; set; }
[ForeignKey(nameof(CreatorId))]
public Account Creator { get; set; }
}
public class Account
{
public Account()
{
Elo = 1000;
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
[Required]
[MaxLength(15)]
[MinLength(4)]
public string Name { get; set; }
[Required]
[MaxLength(32)]
[MinLength(32)]
public string PasswordHash { get; set; }
public int Elo { get; set; }
public bool IsAdmin { get; set; }
public Guid? CurrentTableId { get; set; }
[ForeignKey(nameof(CurrentTableId))]
public virtual GameTable CurrentTable { get; set; }
public virtual ICollection<GameTable> Children { get; set; }
}
希望能解决您的问题。
在 FK 关系的每一侧,您可以(通常应该)具有导航属性。并且当存在多个FK关系时,可以使用InversePropertyAttribute来指定一对Navigation Properties代表哪个FK。
EG
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ef62test
{
class Program
{
public class GameTable
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id
{
get;
set;
}
[Required]
public string Name { get; set; }
[Required]
public string Description { get; set; }
public Guid CreatorId { get; set; }
[ForeignKey(nameof(CreatorId))]
public Account Creator { get; set; }
[InverseProperty(nameof(Account.CurrentTable))]
public ICollection<Account> CurrentPlayers { get; } = new HashSet<Account>();
}
public class Account
{
public Account()
{
Elo = 1000;
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
[Required]
[MaxLength(15)]
[MinLength(4)]
public string Name { get; set; }
[Required]
[MaxLength(32)]
[MinLength(32)]
public string PasswordHash { get; set; }
public int Elo { get; set; }
public bool IsAdmin { get; set; }
public Guid? CurrentTableId { get; set; }
[ForeignKey(nameof(CurrentTableId))]
public virtual GameTable CurrentTable { get; set; }
[InverseProperty(nameof(GameTable.Creator))]
public ICollection<GameTable> CreatedTables { get; } = new HashSet<GameTable>();
}
public class MyDbContext : DbContext
{
public MyDbContext() : base("DbContextCon")
{
Database.SetInitializer<MyDbContext>(new CreateDatabaseIfNotExists<MyDbContext>());
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
public virtual DbSet<Account> Accounts { get; set; }
//public virtual DbSet<SecurityToken> SecurityTokens { get; set; }
public virtual DbSet<GameTable> GameTables { get; set; }
}
static void Main(string[] args)
{
Database.SetInitializer(new DropCreateDatabaseAlways<MyDbContext>());
using (var db = new MyDbContext())
{
db.Database.CreateIfNotExists();
}
Console.WriteLine("Hit any key to exit.");
Console.ReadKey();
}
}
}