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 に設定して関係を削除します。外部キー プロパティは 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() を実行する必要がありますか? もしそうなら、それは少しばかげているように思えますし、新しいオブジェクトが外部キー コレクションに追加されたときに、オブジェクトのすべての参照を手動で設定する必要があるのはかなり面倒です。
- アップデート -
Gert の回答を正しいものとしてマークする必要がありますが、あまり満足していないので、1 日か 2 日待ちます。...その理由は次のとおりです。
次のコードも機能しません。
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));
...つまり、それはクールなことですが、それは私を不思議に思います。オブジェクトを適切にアタッチしたい場合、どちらの場合もオブジェクトを常に Add() する必要がある場合、new の代わりに「Create()」を使用するポイントは何ですか?
私にとって最も厄介なのは、次のことが失敗することです。
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");
...それは私にはまったくがらくたのように思えます。バージョン 5 でも EntityFramework は、ライス ペーパー橋の地雷原のように感じます。上記のコードで、SalesOrder プロパティの 2 番目の割り当てで SalesOrderId を同期できないのはなぜですか? ここで欠けている重要なトリックは何ですか?