我很想知道为什么实体框架在以下情境中不允许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 :(得分:1)
您必须确定一端是非可选的(即,允许1到0..1 ... 0..1到0..1不是)。一旦你这样做,EF通过强制依赖方不拥有它自己的Key
来支持1到0..1,而是定义依赖类' Key
与其ForeignKey
相同(如果您考虑它,对于应该是1比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; }
}
另请注意,SQL中BId
为int?
:NULL
的详细信息对应于C#中的Nullable<>
。
答案 1 :(得分: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)
在DB模型中,我们可以有2个不同的ClassB实例,它们具有相同的ClassA实例(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);
}
}
此测试引发异常
===
EntityFramework.dll中出现未处理的“System.InvalidOperationException”类型异常
附加信息:发生了关系多重性约束违规:EntityReference只能有一个相关对象,但查询返回了多个相关对象。这是一个不可恢复的错误。
===
我们可以避免来自SQL的人这样做吗?是的,在ClassA_Id字段上插入唯一约束。