2015-09-02 162 views
1

我很想看看为什么实体框架在以下上下文中不允许1对1关系;1对1可选与实体框架的关系

ClassA { 
    public int ID; 
    [ForeignKey("ClassB")] 
    public int ClassBID; 
    public ClassB classB; 
} 

ClassB { 
    public int ID; 
    [ForeignKey("ClassA")] 
    public int ClassAID; 
    public ClassA classa; 
} 

即一对一关系,从而我可以导航到linq。

我的背景是我有一辆车。每辆车可以有一个设备(如果没有的话,它是空的)。每个设备将有一个可选车辆。

如果有人能解释他为什么无效(或支持),并解释我将如何解决我的问题,我真的会赞赏它。

在此先感谢。

回答

0

如果我了解设备可以有零个或一个车辆,反之亦然。
在旧的DB模型中,两个表(设备或车辆)中的一个应该有一个引用另一个表的可为空的字段。
要在EF中配置它,您必须使用数据注释或流畅的界面。 这里的模型和上下文的代码

public class ClassA 
{ 
    public int Id { get; set; } 
    public string Description { get; set; } 
    public virtual ClassB ClassB { get; set; } 
} 

public class ClassB 
{ 
    public int Id { get; set; } 
    public string Description { get; set; } 
    public virtual ClassA ClassA { get; set; } 
} 

class Context : DbContext 
{ 
    public Context(DbConnection connection) 
     : base(connection, false) 
    { } 

    public DbSet<ClassA> As { get; set; } 
    public DbSet<ClassB> Bs { get; set; } 

    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
     modelBuilder.Entity<ClassB>().HasOptional(c => c.ClassA).WithOptionalDependent(c => c.ClassB); 
    } 
} 

正如你所想象,有使用该模型的一些限制。即模型(POCO模型)允许您拥有一个classA1,引用一个引用classA2的classB1(类型相同但classA1不同的实例)。 DB和EF没有。这里用一个查询为例倾倒在EF如何在这种情况下,(我觉得很有意思!)

 using (Context context = new Context(connection)) 
     { 
      ClassA classA; 
      ClassB classB; 


      // Very simple behaviour (as expected). You can see the queries after SaveChanges() 
      classA = new ClassA {Description = "B empty"}; 
      context.As.Add(classA); 

      classA = new ClassA { Description = "B full", ClassB = new ClassB(){Description = "ClassB full"}}; 
      context.As.Add(classA); 

      classB = new ClassB { Description = "B empty"}; 
      context.Bs.Add(classB); 

      context.SaveChanges(); 
      /* 
      insert into [ClassAs]([Description]) 
       values (@p0); 

       @p0 = B full 

      insert into [ClassAs]([Description]) 
       values (@p0); 

       @p0 = B empty 

      insert into [ClassBs]([Description], [ClassA_Id]) 
       values (@p0, @p1); 

       @p0 = ClassB full 
       @p1 = 1 

      insert into [ClassBs]([Description], [ClassA_Id]) 
       values (@p0, null); 

       @p0 = B empty 
      */ 




      // Here a new classB references an already referenced classA. But we don't want this!!! 
      // EF works like we want, the classA is detached from the old classB then attached to the 
      // new classB. Below you can see the queries 
      classB = new ClassB { Description = "B full with the wrong A", ClassA = classA}; 
      context.Bs.Add(classB); 
      /* 
      update [ClassBs] 
       set [ClassA_Id] = null 
       where (([Id] = @p0) and ([ClassA_Id] = @p1)) 

       @p0 = 1 
       @p1 = 1 

      insert into [ClassBs]([Description], [ClassA_Id]) 
       values (@p0, @p1); 

       @p0 = B full with the wrong A 
       @p1 = 1 
      */ 

      context.SaveChanges(); 
     } 

现在,最后一步...... 望着数据库的结构这POCO模型

ClassBs(Id, Description, ClassA_Id : ClassAs) 
ClassAs(Id, Description) 

在数据库模型中,我们可以有2个不同的具有相同ClassA实例的ClassB实例(EF不允许我们这样做,但我们可以从SQL实现)。 使用SQL黑客后,你可以运行这个测试

 using (Context context = new Context(connection)) 
     { 
      foreach (var classB in context.Bs.ToList()) 
      { 
       if (classB.ClassA == null) 
        continue; 
       Console.WriteLine("{0} {1} {2}", classB.Id, classB.ClassA.Id, classB.ClassA.ClassB.Id); 
      } 
     } 

这个测试会引发异常

===
“System.InvalidOperationException”类型的未处理的异常出现在EntityFramework.dll

附加信息:发生关系多重性约束冲突:EntityReference中只能有一个相关对象,但该查询返回多个相关对象。这是一个不可恢复的错误。

===

我们可以避免来自SQL的人做到了吗?是的,在ClassA_Id字段上插入一个唯一约束。

+0

非常感谢您的验证。所以在这种情况下,如果不使用流利的API,就无法解决问题了? (我问这个问题的唯一原因是因为我喜欢尝试将类中的所有内容都保存为属性,因为我个人觉得它很容易阅读并遵循:)) –

+0

其实我认为不可能。 – bubi

1

您必须确定一端是非可选的(即允许1到0..1 ... 0..1到0..1不是)。一旦你这样做,EF支持1到0 ..1通过迫使依赖方不拥有它自己的Key,而是定义相关类'Key与它的ForeignKey相同(如果你考虑它,这对于一个应该是1-to的关系是有意义的-1):

class A 
{ 
    public int Id { get; set; } 
    public int? BId { get; set; } 
    public virtual B B { get; set; } 
} 

class B 
{ 
    [Key, ForeignKey("A")] 
    public int Id { get; set; } 
    public virtual A A { get; set; } 
} 

也请注意,在细节BIdint?NULL在SQL对应Nullable<>在C#。