【问题标题】:EF code first from database 0..1 to many relationshipEF 代码首先从数据库 0..1 到多关系
【发布时间】: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,尽管大多数都没有。

标签: entity-framework code-first


【解决方案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 代码时。然而,就关系数据库而言,无论哪种方式都可以争论,例如stackoverflow.com/questions/1723808/…。在我的情况下,虽然我需要先从现有数据库中对代码进行逆向工程。您的解决方案并不像 edmx 方法那样表示模型。
  • 我从未说过我的解决方案像 edmx 那样代表模型。我说的是,在我看来,edmx 方法在使用代码优先时没有意义,因为没有理由在你的数据库中有一个表而不是在你的代码中
  • 多对多关系会在数据库中创建表,但不会在模型中创建?
  • @PhilWithington:仅针对特定情况(以及实体拆分)
  • @PhilWithington 是的,会的,但我说的是你的问题的情况,对不起
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-01
  • 2018-09-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多