この質問は何度も議論/質問されてきましたが、それでもこの問題に対する決定的な答えを見つけることができません。説明が長かったことをお詫び申し上げます。時間をかけて読んで問題を理解していただくことは、私にとって非常に大きな意味があります。
私は、予約制の多階建て商業事業体向けの駐車場管理システムを開発しています。このシステムでは、クライアントがシステムの管理者に電話をかけ、駐車場を利用できるようにしたい日時と期間を知らせます。彼らへ。
誰かがシステムに新しい駐車場を登録するには、最初に登録してフロアを選択する必要があります。したがって、フロアと駐車場の間には1対多の関係があります。1フロアに多くの駐車場があります。これが私のクラスの実装方法です。
フロアクラス
public class Nivel
{
#region Campos
int _IdNivel;
#endregion
#region Propiedades
public int IdNivel
{
get
{
return _IdNivel;
}
private set
{
_IdNivel = value;
}
}
public string NombreNivel { get; set; }
public int CantidadParqueos { get; set; }
public List<Parqueo> Parqueos { get; set; }
#endregion
#region Constructores
public Nivel()
{
Parqueos = new List<Parqueo>();
}
#endregion
}
駐車場クラス
public class Parqueo
{
#region Campos
int _IdParqueo;
int _IdNivel;
#endregion
#region Propiedades
public int IdParqueo
{
get
{
return _IdParqueo;
}
private set
{
_IdParqueo = value;
}
}
public int IdNivel
{
get
{
return _IdNivel;
}
private set
{
_IdNivel = value;
}
}
public string NombreParqueo { get; set; }
public string EstadoParqueo { get; set; }
public Nivel Nivel { get; set; }
#endregion
#region Constructores
public Parqueo()
{
}
public Parqueo(string NombreParqueo, string EstadoParqueo)
{
this.NombreParqueo = NombreParqueo;
this.EstadoParqueo = EstadoParqueo;
}
#endregion
}
今、これはすべてうまく機能していてダンディです。タイトルが示すように、私の主な問題は、特定のフロアの特定の駐車場を削除することと関係があります。私はこれを解決しましたが、私はすでに持っているものよりもエレガントなソリューションが欲しいです。これは、駐車場登録フォームのフォームロードイベントにあるものです。
private void FrmRegistroParqueos_Load(object sender, EventArgs e)
{
this.groupBox3.Left = (this.Parent.Width / 2) - (this.groupBox3.Width / 2);
this.groupBox3.Top = (this.Parent.Height / 2) - (this.groupBox3.Height / 2);
contexto.Niveles.Include("Parqueos").Load();
bindingSourceNiveles.DataSource = contexto.Niveles.Local.ToBindingList<Nivel>();
bindingSourceParqueos.DataSource = bindingSourceNiveles;
bindingSourceParqueos.DataMember = "Parqueos";
this.DgvNiveles.DataSource = bindingSourceNiveles;
this.DgvParqueos.DataSource = bindingSourceParqueos;
if (this.DgvNiveles.Rows.Count > 0)
{
this.DgvNiveles.ClearSelection();
}
}
つまり、これが私が行っていることです。コンテキストと2つのバインディングソースをインスタンス化して、2つのデータグリッドビューでマスター/詳細フォームを実装し、一方のデータグリッドビューで特定のフロアを選択すると、もう一方のフロアにデータが入力されるようにします。その選択されたフロアで利用可能な駐車場を備えたdatagridview。次に、コンテキストでクエリを実行して、データベースで利用可能なすべてのフロアを駐車場を含むローカルメモリにロードして、バインディングソースにデータを入力し、その情報をデータグリッドビューに反映できるようにします。
これは、選択した駐車場を削除するための私のコードです。
private void BtnEliminarParqueo_Click(object sender, EventArgs e)
{
((Nivel)bindingSourceNiveles.Current).CantidadParqueos -= 1;
bindingSourceNiveles.ResetCurrentItem();
Parqueo parqueoEliminado = new Parqueo();
parqueoEliminado = (Parqueo)bindingSourceParqueos.Current;
bindingSourceParqueos.RemoveCurrent();
contexto.Parqueos.Remove(parqueoEliminado);
contexto.SaveChanges();
}
Entity Frameworkに精通している場合は、すでに知っている少しの背景情報。ナビゲーションオブジェクトコレクションの1つから関連する子オブジェクトを削除しても、実際にはそのオブジェクトがデータベースから削除されるわけではありません。親と子の間の関係が削除されるだけです。さて、私のコードでは、フロアと駐車場の間に確立された関係は厳密なものです。特定のフロアに関連付けずに駐車場を追加することはできません。これは、駐車場のテーブルの外部キーがnull不可であることを意味します。したがって、フロアの駐車場コレクションから特定の駐車場を削除してプログラム内の特定の駐車場を削除すると、このルールに違反し、コンテキストでSaveChangesメソッドを呼び出すときに、外部キーにnullを設定しようとしたという例外がコンテキストによってスローされます。
この厄介な動作のため、解決策に到達する前に1つの回避策を試しました。エンティティフレームワークでプログラム内のオブジェクトを介して子オブジェクトを削除できない場合は、コンテキスト自体から子オブジェクトを削除して削除します。変更は私の駐車場のdatagridviewに反映され、ユーザーは駐車場が実際に削除されたことを確認できます。現在、このアプローチは、データベースから子レコードを実際に削除するという意味で「機能」しますが、駐車場datagridviewは、削除された駐車場の位置に何もないことを示すインデックスの範囲外の例外をスローします。だった。これが例外の写真です。ここに写真を挿入できなかったことをお詫びします(十分な評判ポイントがないため、ここで質問するのはこれが初めてです)。
https://www.dropbox.com/s/vank28utf7bpzdg/Exception.png?m
そして、この失敗した回避策のコードは、厄介な例外を私に与えます(ちなみに、メッセージボックスの[OK]ボタンをクリックした後に3回以上起動します):
private void BtnEliminarParqueo_Click(object sender, EventArgs e)
{
((Nivel)bindingSourceNiveles.Current).CantidadParqueos -= 1;
bindingSourceNiveles.ResetCurrentItem();
contexto.Parqueos.Remove((Parqueo)bindingSourceParqueos.Current);
contexto.SaveChanges();
}
それは失敗しましたが、これは私が私のプログラムに実装したかったものであり、私の意見では、物事が機能する方法です。オブジェクトコレクションを使用して現在選択されているオブジェクトを削除し、データベースに反映させるか、データベースから子レコードを削除して、datagridviewsへの変更を反映させます。これが私が望んでいたことです。しかし、それはどれも機能しません。最初の解決策は、外部キーにnull値を含めることができないために機能しません。また、2番目の解決策は、datagridviewがスローする例外のために機能しません。ユーザーが実際にdatagridviewでレコードが削除されたことを確認できるように、datagridviewのレコードを削除し、データベースからも削除できるようにしたいだけです。
それで私は私の現在の解決策にたどり着きました:
1-新しい駐車場をインスタンス化します
2-駐車場datagridviewで現在選択されている駐車場に設定します
3-駐車場のbindingsourceRemoveCurrent()メソッドを介して現在選択されているオブジェクトを削除し、ユーザーが駐車場がdatagridviewから削除されたことを確認できるようにします。この時点で、このオブジェクトの外部キープロパティをnullに設定することにより、コンテキストでリレーションシップが削除されるように設定されています。
4-コンテキストのRemove()メソッドを介してデータベースから削除されたレコードを削除します
5-子レコードがデータベースから削除されるように、コンテキストでSaveChanges()メソッドを呼び出します
基本的に、私が行っているのは、オブジェクトを2回削除することです。1回目は、datagridviewで駐車場が削除されたことをユーザーが確認できるようにするため、もう1回は、データベースから無関係の子オブジェクト(null外部キー)を削除するためです。このようにして、SaveChangesを呼び出したときに、子レコードにnullの外部キーがあるというコンテキストの不満を防ぐことができます。
私の目には、これは問題の醜い解決策であり、これについてあなたが持っているかもしれない他のアプローチについてのフィードバックをお願いします。この長い文章の壁をお読みいただき、誠にありがとうございました。非常に冗長になって申し訳ありませんが、何が起こっているのかを正確に理解する必要があると感じました。
良い一日を。