LINQtoSQLで、主キーの変更をする
LINQtoSQLでは、主キーのフィールドを変更してSubmitChanges()しようとすると例外が発生してしまい、変更することができません。
代わりに、元々のレコードは削除して、代わりに、主キー以外の全てのフィールドをコピーして(DBの値でなく、画面で主キー以外のいろいろなフィールドも編集した結果からコピー)主キーだけを新しいものにしたレコードを追加する、という組み合わせで、一応、見かけ上は主キーを変更したのと同等の変更をすることができます。(厳密にはすでに書いている通り、旧レコードの削除+新レコードの追加ですが)。
これを行うコード例です。Dbは、LINQtoSQLで作成されるDataContextのインスタンスが入っており、同一アセンブリ内のBellという名前空間に、作りたいエンティティのクラスが定義されていて、その主キーは「Id」という名前の列であり、画面に呼び出して編集対象にしてるテーブル名がTargetTableNameに入ってる、という前提のコードです(前提多くてすみません)。
もともと、複数のコード系マスタテーブルを編集するために作ったもので、Idという名前のフィールドが存在し、それが主キーになっているテーブルならどれでも対応できるよう、途中でdynamicで受けています。また、複数ある対象の中からどれを編集対象とするか、そのテーブル名をTargetTableNameに入れて呼び出すものとなっています。
なお、Id列は、「Id」という名称ですが、SQL server側の列のプロパティとしては「IDである」は「いいえ」になってる必要があります^^ 「はい」で自動で番号振られる状態だと、手動で値を設定することはできませんので。
public void Save() { Action doAfterDelete = () => { }; var changeSet = Db.GetChangeSet(); for (var i = changeSet.Updates.Count-1; i >=0; i--) { var oldEntity = changeSet.Updates[i]; var table = Db.GetTable(oldEntity.GetType()); dynamic beforeEdit = table.GetOriginalEntityState(oldEntity); dynamic afterEdit = oldEntity; //主キーが「Id」という名前の列の場合。 int beforeId = beforeEdit.Id; int afterId = afterEdit.Id; if (beforeId == afterId) continue; //ID変更の処理。LINQtoSQLでは変更はできないので、旧レコードの削除と新レコードの追加に組み替える。 //元のレコードの削除 table.DeleteOnSubmit(oldEntity); //新しいレコードの作製 var assembly = Assembly.GetExecutingAssembly(); dynamic newEntity = assembly.CreateInstance( "Bell." + TargetTableName, false, BindingFlags.CreateInstance, null, null, null, null ); //作ったレコードの全フィールドに画面で編集した値を設定 //(IDも画面で新しいものに編集されている前提) foreach (var info in oldEntity.GetType().GetProperties()) { info.SetValue(newEntity, info.GetValue(oldEntity, null)); } //SubmitChangeで旧レコードを削除した後でないと新レコード追加できないのでデリゲートに追加するだけ doAfterDelete+=()=> { table.InsertOnSubmit(newEntity); }; } //まずここで主キー変更以外の変更の更新(主キー変更する旧レコードの削除含む) Db.SubmitChanges(); //主キー変更したレコードのコピーはここで追加される。 doAfterDelete(); Db.SubmitChanges(); }
forループを逆順で回してるのは特に意味はないです^^
元々、更新処理を、元レコードの削除+コピーの追加に置き換えるわけだから、ChangesetのUpdatesからは該当レコードを除去した方がいいんじゃないかと思い、そのために逆順にしてコード書き始めたと思うのですが、そもそもそんなことする必要もなく無事動作したというオチ。