EF 5.0,在现有数据库工作流上使用代码优先。数据库在 SalesOrderLine 上有您的基本 SalesOrder 和 SalesOrderLine 表以及所需的外键,如下所示;
public class SalesOrder
{
public SalesOrder()
{
this.SalesOrderLines = new List<SalesOrderLine>();
}
public int SalesOrderID { get; set; }
public int CustomerID { get; set; }
public virtual Customer Customer { get; set; }
public virtual ICollection<SalesOrderLine> SalesOrderLines { get; set; }
}
public class SalesOrderLine
{
public SalesOrderLine()
{
}
public int SalesOrderLineID { get; set; }
public int SalesOrderID { get; set; }
public virtual SalesOrder SalesOrder { get; set; }
}
public SalesOrderLineMap()
{
// Primary Key
this.HasKey(t => t.SalesOrderLineID);
// Table & Column Mappings
this.ToTable("SalesOrderLine");
this.Property(t => t.SalesOrderLineID).HasColumnName("SalesOrderLineID");
this.Property(t => t.SalesOrderID).HasColumnName("SalesOrderID");
// Relationships
this.HasRequired(t => t.SalesOrder)
.WithMany(t => t.SalesOrderLines)
.HasForeignKey(d => d.SalesOrderID);
}
现在根据这个页面:http: //msdn.microsoft.com/en-us/data/jj713564
...我们被告知:
以下代码通过将外键设置为 null 来删除关系。请注意,外键属性必须可以为空。
course.DepartmentID = null;
注意:如果引用处于添加状态(在本例中为课程对象),则在调用 SaveChanges 之前,引用导航属性不会与新对象的键值同步。不会发生同步,因为对象上下文不包含添加对象的永久键,直到它们被保存。如果您必须在设置关系后立即使新对象完全同步,请使用以下方法之一。
通过将新对象分配给导航属性。以下代码创建课程和部门之间的关系。如果对象附加到上下文中,则课程也被添加到department.Courses 集合中,并且课程对象上的对应外键属性设置为部门的键属性值。
course.Department =部门;
...听起来不错!
现在我的问题:我有以下代码,但两个断言都失败了 - 为什么?
using (MyContext db = new MyContext ())
{
SalesOrder so = db.SalesOrders.First();
SalesOrderLine sol = db.SalesOrderLines.Create();
sol.SalesOrder = so;
Trace.Assert(sol.SalesOrderID == so.SalesOrderID);
Trace.Assert(so.SalesOrderLines.Contains(sol));
}
两个对象都附加到上下文中——不是吗?在此之前我需要执行 SaveChanges() 吗?如果是这样,这似乎有点愚蠢,当将新对象添加到外键集合时,我需要手动设置对象上的所有引用,这很烦人。
- 更新 -
我应该将格特的答案标记为正确,但我对此不太满意,所以我会等一两天。...原因如下:
以下代码也不起作用:
SalesOrder so = db.SalesOrders.First();
SalesOrderLine sol = db.SalesOrderLines.Create();
db.SalesOrderLines.Add(sol);
sol.SalesOrder = so;
Trace.Assert(so.SalesOrderLines.Contains(sol));
唯一有效的代码是:
SalesOrder so = db.SalesOrders.First();
SalesOrderLine sol = db.SalesOrderLines.Create();
sol.SalesOrder = so;
db.SalesOrderLines.Add(sol);
Trace.Assert(so.SalesOrderLines.Contains(sol));
...换句话说,您必须先设置所有外键关系,然后在连接任何关系和外键字段之前调用 TYPE.Add(newObjectOfTYPE) 。这意味着从 Create 完成到您执行 Add() 为止,对象基本上处于半生不熟的状态。我(错误地)认为,因为我使用了 Create(),并且由于 Create() 返回了一个子类动态对象(而不是使用返回 POCO 对象的“new”),所以关系连线将被处理我。对我来说也很奇怪,您可以在使用new运算符创建的对象上调用 Add() 并且它会起作用,即使该对象不是子类类型......
换句话说,这将起作用:
SalesOrder so = db.SalesOrders.First();
SalesOrderLine sol = new SalesOrderLine();
sol.SalesOrder = so;
db.SalesOrderLines.Add(sol);
Trace.Assert(sol.SalesOrderID == so.SalesOrderID);
Trace.Assert(so.SalesOrderLines.Contains(sol));
...我的意思是,这很酷,但这让我想知道;如果在任何一种情况下都必须添加()对象,如果你想正确地附加它,那么使用“Create()”而不是 new 有什么意义呢?
对我来说最烦人的是以下失败;
SalesOrder so = db.SalesOrders.OrderBy(p => p.SalesOrderID).First();
SalesOrderLine sol = db.SalesOrderLines.Create();
sol.SalesOrder = so;
db.SalesOrderLines.Add(sol);
// NOTE: at this point in time, the SalesOrderId field has indeed been set to the SalesOrderId of the SalesOrder, and the Asserts will pass...
Trace.Assert(sol.SalesOrderID == so.SalesOrderID);
Trace.Assert(so.SalesOrderLines.Contains(sol));
sol.SalesOrder = db.SalesOrders.OrderBy(p => p.SalesOrderID).Skip(5).First();
// NOTE: at this point in time, the SalesOrderId field is ***STILL*** set to the SalesOrderId of the original SO, so the relationships are not being maintained!
// The Exception will be thrown!
if (so.SalesOrderID == sol.SalesOrderID)
throw new Exception("salesorderid not changed");
...这对我来说似乎完全是废话,让我觉得 EntityFramework,即使在第 5 版中,也像是米纸桥上的雷区。为什么上面的代码无法在 SalesOrder 属性的第二次分配上同步 SalesOrderId?我在这里缺少什么重要技巧?