EF 代码首先从数据库 0..1 到多关系
Posted
技术标签:
【中文标题】EF 代码首先从数据库 0..1 到多关系【英文标题】:EF code first from database 0..1 to many relationship 【发布时间】:2015-07-09 09:13:05 【问题描述】:我正在尝试从现有数据库生成实体框架代码优先模型(不更改数据库架构)。该数据库过去曾用于生成 edmx 模型,我正在尝试使用 Fluent Api 或数据注释来实现等效模型。
我无法重现的关系是 0..1 到许多使用连接表(不是可为空的外键)。
所以它看起来像这样:
TableA
ID (PrimaryKey)
TableB (0 or 1)
JoinTable
TableA_FK (PrimaryKey, ForeignKey),
TableB_FK (ForeignKey)
TableB
ID (PrimaryKey)
TableAs (Many)
这是否可以在代码优先样式中实现,还是我必须生成一个 edmx 模型才能在 EF 中使用此数据库而不更改其架构?
非常感谢, 菲尔
【问题讨论】:
您几乎可以使用Entity Splitting 来完成此操作,但我认为 TableA 中的每一行都需要连接表中的一行 数据库模型是什么样的?如果JoinTable
有一个PK/FK 引用tableA,则关联只能是* (A) 到0..1 (B)。
我搞错了吗?基本上,A 可能有一个 B(尽管很多没有)B 可能有很多 A,尽管大多数都没有。
【参考方案1】:
这是一个不使用 JoinTable 类的示例。连接表是通过fluent api配置的。
class DataContext : DbContext
public DataContext(string connectionString)
: base(connectionString)
public DbSet<TableA> TableA get; set;
public DbSet<TableB> TableB get; set;
protected override void OnModelCreating(DbModelBuilder modelBuilder)
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<TableA>().ToTable("TableA");
modelBuilder.Entity<TableB>().ToTable("TableB");
modelBuilder.Entity<TableB>()
.HasMany(x => x.TableAs)
.WithMany()
.Map(m =>
m.ToTable("JoinTable");
m.MapLeftKey("TableA_FK");
m.MapRightKey("TableB_FK");
);
class TableA
public int ID get; set;
public TableB TableB get; set;
class TableB
public int ID get; set;
public ICollection<TableA> TableAs get; set;
这将生成以下迁移脚本,它看起来像您拥有的架构。
public override void Up()
CreateTable(
"dbo.TableA",
c => new
ID = c.Int(nullable: false, identity: true),
TableB_ID = c.Int(),
)
.PrimaryKey(t => t.ID)
.ForeignKey("dbo.TableB", t => t.TableB_ID)
.Index(t => t.TableB_ID);
CreateTable(
"dbo.TableB",
c => new
ID = c.Int(nullable: false, identity: true),
)
.PrimaryKey(t => t.ID);
CreateTable(
"dbo.JoinTable",
c => new
TableA_FK = c.Int(nullable: false),
TableB_FK = c.Int(nullable: false),
)
.PrimaryKey(t => new t.TableA_FK, t.TableB_FK )
.ForeignKey("dbo.TableB", t => t.TableA_FK, cascadeDelete: true)
.ForeignKey("dbo.TableA", t => t.TableB_FK, cascadeDelete: true)
.Index(t => t.TableA_FK)
.Index(t => t.TableB_FK);
【讨论】:
您最终会得到两个独立的关系:TableA 和 TableB 之间的 1:1,以及使用连接表的相同表之间的 many:many 是的,但是如果它需要匹配架构可以避免吗?据我了解,这与问题中描述的架构相匹配。 这是我所见过的最接近我想要实现的答案。查看代码让我印象深刻的一个问题是,主键在迁移中的连接表上不太正确,但这可能不是问题,因为数据库已经存在。 使用这种方法加载数据也有问题。我可能在设置中犯了错误,但我得到了模型中找不到指定的表“[JoinTable]”。确保已正确指定表名。【参考方案2】:如果我理解正确,以下仅使用数据注释的代码应该可以创建您的模型。
public class TableA
public int ID get; set;
public JoinTable JoinTable get; set;
public class TableB
public int ID get; set;
public List<JoinTable> JoinTables get; set;
public class JoinTable
[Key, ForeignKey("TableA")]
public int TableA_FK get; set;
[ForeignKey("TableB")]
public int TableB_FK get; set;
public TableA TableA get; set;
public TableB TableB get; set;
有趣的是,EF 不会执行返回原始的往返,如果您从该代码创建的数据库模型生成代码优先模型,那么 EF 会简化模型并删除连接表并创建可为空的外键。
让我知道这是否有效。
【讨论】:
这似乎接近我想要的。如果 TableA 可以直接引用其 TableB 而无需通过连接表,那将是一个完整的答案。这发生在数据库优先的 edmx 模型方法中 - 连接表隐藏在模型中。 @PhilWithington 是的,我知道你的意思。我不确定如何实现这一目标。您可能有另一个NotMapped
属性取消引用 JoinTable
属性。
好建议。不过,未来可能会出现往返的问题。感谢您的想法。【参考方案3】:
我可能是错的,但我相信你在这里遗漏了一些概念......
如果 JoinTable 除了外键之外没有任何列,为什么还要使用它?这没有意义... IHMO TableA 中的可为空的外键是正确的方法。
当您使用 Code-First 时,这意味着您数据库中的所有内容都将由 CODE 表示。没有理由在您的数据库中有一个表,但在您的代码中没有......
EDMX 处理这种关系,因为它使用“关联”https://msdn.microsoft.com/en-us/data/jj713299#Overview
...回到代码优先,您可以像这样表示您的数据库:
public class JoinTable
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int TableA_FK get; set;
public int TableB_FK get; set;
//a future property here
public virtual TableA TableA get; set;
public virtual TableB TableB get; set;
public partial class TableA
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int TableAId get; set;
[Required]
[StringLength(50)]
public string Name get; set;
public virtual JoinTable JoinTable get; set;
public partial class TableB
public TableB()
JoinTable = new HashSet<JoinTable>();
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int TableBId get; set;
[Required]
[StringLength(50)]
public string Name get; set;
public virtual ICollection<JoinTable> JoinTable get; set;
public partial class Model1 : DbContext
public Model1()
: base("name=Model1")
public virtual DbSet<JoinTable> JoinTable get; set;
public virtual DbSet<TableA> TableA get; set;
public virtual DbSet<TableB> TableB get; set;
protected override void OnModelCreating(DbModelBuilder modelBuilder)
modelBuilder.Entity<TableA>()
.HasOptional(e => e.JoinTable)
.WithRequired(e => e.TableA);
modelBuilder.Entity<TableB>()
.HasMany(e => e.JoinTable)
.WithRequired(e => e.TableB)
.HasForeignKey(e => e.TableB_FK)
.WillCascadeOnDelete(false);
【讨论】:
我同意可空外键更实用,尤其是在首先使用 EF 代码时。然而,就关系数据库而言,无论哪种方式都可以争论,例如***.com/questions/1723808/…。在我的情况下,虽然我需要先从现有数据库中对代码进行逆向工程。您的解决方案并不像 edmx 方法那样表示模型。 我从未说过我的解决方案像 edmx 那样代表模型。我说的是,在我看来,edmx 方法在使用代码优先时没有意义,因为没有理由在你的数据库中有一个表而不是在你的代码中 多对多关系会在数据库中创建表,但不会在模型中创建? @PhilWithington:仅针对特定情况(以及实体拆分) @PhilWithington 是的,会的,但我说的是你的问题的情况,对不起以上是关于EF 代码首先从数据库 0..1 到多关系的主要内容,如果未能解决你的问题,请参考以下文章
Fluent API 首先在 EF 代码中实现零或一到零或一的关系