EF代码首先是一对多,反向一对一的关系

时间:2014-12-18 10:26:44

标签: entity-framework ef-code-first

我正在尝试使用代码创建一对多和反向一对一的关系。这是我想做的事情

1)两个类之间的一对多,它按预期工作。

    public class X
    {
        [Key]
        public int XId { get; set; }
        public ICollection<Y> Y { get; set; }

    }

    public class Y
    {
        [Key]
        public int YId { get; set; }
        public int XId { get; set; }
        public X X { get; set; }
    }

    public class DataContext : DbContext
    {
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Y>()
                .HasRequired(y => y.X)
                .WithMany(x => x.Y)
                .HasForeignKey(y => y.XId);
        }
    }

现在我要做的是在Y和X之间创建Reverse One-to-One可选关系,这样X将包含Y的外键......怎么可能?这是我想要做的,它会引发一些多重错误

       public class X
        {
            [Key]
            public int XId { get; set; }
            public ICollection<Y> Y { get; set; }
            public int YId {get; set; }
            [ForiegnKey("YId")]
            public Y YOptional { get; set; }
        }

        public class Y
        {
            [Key]
            public int YId { get; set; }
            public int XId { get; set; }
            public X X { get; set; }
            public X XOptional {get; set; }
        }

        public class DataContext : DbContext
        {
            protected override void OnModelCreating(DbModelBuilder modelBuilder)
            {
                modelBuilder.Entity<Y>()
                    .HasRequired(y => y.X)
                    .WithMany(x => x.Y)
                    .HasForeignKey(y => y.XId);

                modelBuilder.Entity<X>()
                    .HasOptional(x => x.YOptional)
                    .WithOptionalDependent(y=> y.XOptional);
            }
        }

2 个答案:

答案 0 :(得分:5)

您不能在两个实体之间建立关系,这两个实体的定义与两端不同。所以你不能从一个方向做1:*而从另一个方向做1:1。

让我猜一下,你真的不希望它从依赖端1:1。从那一端起,它总是只指向一件事。

在映射中,与生活不同,除非你有多对多,否则一个孩子只有一个父母。

但是,您可以创建一个0..1:* relationaship(零或一对多)。如果父母可以有一个或多个孩子(例如&#34;很多&#34;),但孩子可以在没有父母的情况下存在,但它永远不会有多于一个父母(例如&#34;零或一个&#34; )。

这是使您的类产生[零或一个]到多个关系的最简单方法。请注意,我在类Y中使外键成为可以为null的int。通过此设置,EF约定将生成一个映射,允许子节点在没有父节点的情况下存在。

public class X
  {
    [Key]
    public int XId { get; set; }
    public ICollection<Y> Y { get; set; }

  }

  public class Y
  {
    [Key]
    public int YId { get; set; }
    public int? XId { get; set; }
    public X X { get; set; }
  }

  public class DataContext : DbContext
  {
    public DbSet<X> XSet { get; set; }
    public DbSet<Y> YSet { get; set; }
   }

以下是从上述类和上下文派生的可视化模型的屏幕截图。 我认为,如果我猜测你可能只是以不同的方式感知它是正确的,那么这就达到了你所寻求的行为。

enter image description here

答案 1 :(得分:3)

使用您在评论中提到的实际类名:

映射可能包含多个User的{​​{1}}不是问题。但是,当您想要在SingleUser之间映射1:1关联时,您必须选择两者中的哪一个是“原则”实体。两个表中都不能有外键列,因为一个实体将始终插入另一个实体之前。接下来插入“依赖”实体,它引用主体的主键值。

因此,如果Single是主要实体,则可以使用类似于此的类模型:

User

映射的两个选项:

选项1:public class User { public User() { this.Singles = new HashSet<Single>(); } public int UserId { get; set; } public string Name { get; set; } public Single Single { get; set; } public virtual ICollection<Single> Singles { get; set; } } public class Single { public int SingleId { get; set; } public string Name { get; set; } public virtual User User { get; set; } public int SuperUserId { get; set; } public User SuperUser { get; set; } } 作为主要

User

在数据模型中,protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<User>().HasMany(u => u.Singles) .WithRequired(s => s.SuperUser).HasForeignKey(s => s.SuperUserId); modelBuilder.Entity<User>().HasOptional(s => s.Single) .WithOptionalPrincipal(s => s.User).Map(m => m.MapKey("UserId")); } 现在有两个外键,SingleUserId。这是在SuperUserId User中创建SingleUser.Single的方法:

User.Singles

EF将首先插入var superUser = new User { Name = "superUser1" }; var single = new Single { Name = "single" }; superUser.Singles.Add(single); db.Users.Add(superUser); superUser.Single = single; db.SaveChanges(); ,然后User将两个外键都等于Single的主键。

选项2:User原则

您还可以将Single作为1:1关联中的主体实体:

Single

现在modelBuilder.Entity<User>().HasOptional(s => s.Single) .WithOptionalDependent(s => s.User).Map(m => m.MapKey("SingleId")); Single)中只有一个外键,SuperUserIdUser)中只有外键。如果你执行相同的代码,现在EF将抛出

  

无法确定相关操作的有效排序。

这是因为存在鸡与蛋问题:必须在创建依赖SingleId之前创建Single,但必须在User之前创建User {1}}可以添加到其Single集合中。通过稍后分配Singles

可以解决
Single

你想把它包装在var superUser = new User { Name = "superUser1" }; var single = new Single { Name = "single" }; superUser.Singles.Add(single); db.Users.Add(superUser); db.SaveChanges(); superUser.Single = single; db.SaveChanges(); 中,所以我认为这个选项不太可行。

注意
如您所见,在1:1映射中,外键无法映射到类模型中的属性。 TransactionScopeHasForeignKey之后的流畅API中没有WithOptionalDependent。此外,此关联只能通过流畅的API进行映射。在数据注释中,没有属性来指示关联的主要结束。