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 到多关系的主要内容,如果未能解决你的问题,请参考以下文章

MVC 5 - 如何首先使用 EF 代码更新关系表

如何首先在 EF 代码中建立模型和枚举之间的多对多关系?

Fluent API 首先在 EF 代码中实现零或一到零或一的关系

动手学深度学习第一课:从上手到多类分类-NDArray

如何在 Access 中添加具有其他多数据(1 到多)关系的记录

添加1到多关系android Room Database?