多対多は、NHibernateでカスケードを削除します
-
03-07-2019 - |
質問
システムにできる限り単純化するためのシナリオがあります。アーティファクトのテーブル(それらを呼び出しましょう)があり、アーティファクトには任意の数のセキュリティロールからアクセスでき、セキュリティロールからは任意の数のアーティファクトにアクセスできます。そのため、データベースには3つのテーブルがあります。1つはアーティファクトを記述し、1つはロールを記述し、1つはアーティファクトIDをロールIDにリンクする多対多の関連付けテーブルです。
ドメインに関して、2つのクラスがあります-1つはロール用で、もう1つはアーティファクト用です。アーティファクトクラスには、アクセスできるロールのリストを返すIListプロパティがあります。 (ただし、ロールは、アクセスできるアーティファクトを取得するためのプロパティを提供していません。)
そのため、アーティファクトのnhibernateマッピングには以下が含まれます。
<bag name="AccessRoles" table="ArtefactAccess" order-by="RoleID"
lazy="true" access="field.camelcase-underscore" optimistic-lock="false">
<key column="ArtefactID"/>
<many-to-many class="Role" column="RoleID"/>
</bag>
これはすべて正常に機能し、アーティファクトを削除すると、関連付けテーブルが適切にクリーンアップされ、削除されたアーティファクトとロールの間のすべての参照が削除されます(ロールは正しく削除されません-孤立したくないため削除)。
問題は-ロールを削除して、関連付けテーブルを自動的にクリアする方法です。現在ロールを削除しようとすると、そのロールの関連付けテーブルにまだエントリがあるため、参照制約が取得されます。ロールを正常に削除する唯一の方法は、そのロールにリンクするすべてのアーティファクトを照会し、アーティファクトのロールコレクションからロールを削除し、アーティファクトを更新してからロールを削除することです。簡素化されたシステムで、ロールは他の任意の数のテーブル/オブジェクトに関連付けることができます。
ロールを削除するたびにこの関連付けテーブルをクリアすることをNHibernateに示唆できるようにする必要があります-これは可能ですか?もしそうなら-どうすればいいですか?
ご協力ありがとうございます。
解決
この回答を探していて、Googleでこのスレッドを見つけたので(回答なし)、これに対する解決策を投稿すると思いました。 3つのテーブル:Role、RolesToAccess(ManyToMany)、Access。
次のマッピングを作成します。 アクセス:
<bag name="Roles" table="RolesToAccess" cascade="none" lazy="false">
<key column="AccessId" />
<many-to-many column="AccessId" class="Domain.Compound,Domain" />
</bag>
<bag name="RolesToAccess" cascade="save-update" inverse="true" lazy="false">
<key column="AccessId" on-delete="cascade" />
<one-to-many class="Domain.RolesToAccess,Domain" />
</bag>
役割:
<bag name="Accesses" table="RolesToAccess" cascade="none" lazy="false">
<key column="RoleId" />
<many-to-many column="RoleId" class="Domain.Compound,Domain" />
</bag>
<bag name="RolesToAccess" cascade="save-update" inverse="true" lazy="false">
<key column="RoleId" on-delete="cascade" />
<one-to-many class="Domain.RolesToAccess,Domain" />
</bag>
上記のように、モデルを汚染しないようにRolesToAccessプロパティを保護することができます。
他のヒント
ここで言うこと:
ロールを正常に削除する唯一の方法は、そのロールにリンクするすべてのアーティファクトを照会し、アーティファクトのロールコレクションからロールを削除し、アーティファクトを更新してからロールを削除することです。単純化されていないシステムでは、ロールを他の任意の数のテーブル/オブジェクトに関連付けることができます。
必要ありません。アソシエーションテーブルをマップしたくない(ドメインオブジェクトにしたい)場合でも、最小限のコードで両端で削除を実行できます。
3つのテーブルがあるとしましょう:Role、Artifact、およびArtifactAccess(リンクテーブル)。 マッピングでは、ロールとアーティファクトのドメインオブジェクトのみがあります。両方とも、多対多協会のためのバッグを持っています。
役割:
<bag name="Artifacts" table="[ArtifactAccess]" schema="[Dbo]" lazy="true"
inverse="false" cascade="none" generic="true">
<key column="[ArtifactID]"/>
<many-to-many column="[RoleID]" class="Role" />
</bag>
アーティファクト:
<bag name="Roles" table="[ArtifactAccess]" schema="[Dbo]" lazy="true"
inverse="false" cascade="none" generic="true">
<key column="[RoleID]"/>
<many-to-many column="[ArtifactID]" class="Role" />
</bag>
ご覧のとおり、両端にinverse = falseが指定されています。 NHibernateのドキュメントでは、アソシエーションの一方の端を「逆」端として選択することを推奨していますが、両方を「制御端」として使用することを妨げるものはありません。更新または挿入を実行するとき、これはヒッチなしで両方向から機能します。いずれかの端の削除を実行すると、関連付けテーブルが更新されないため、FK違反エラーが発生します(true)。ただし、削除を実行する前にコレクションをもう一方の端にクリアするだけでこれを解決できます。これは、「this」の使用がある場合、関連付けの「他の」端を調べるよりもはるかに複雑ではありません' 終わり。これが少しわかりにくい場合は、次のコード例をご覧ください。片方しか制御できない場合は、複雑な削除を行う必要があります:
foreach(var artifact in role.Artifacts)
foreach(var role in artifact.Roles)
if(role == roleToDelete)
artifact.Roles.Remove(role)
artifact.Save();
roleToDelete.Delete();
役割を削除するときに行うことは次のようなものです
roleToDelete.Artifacts.Clear(); //removes the association record
roleToDelete.Delete(); // removes the artifact record
これはコードの追加行ですが、この方法では、関連付けのどちらの端が逆の端であるかを決定する必要はありません。また、完全に制御するために関連付けテーブルをマップする必要はありません。
関連付けテーブルのマッピングを作成し、そのテーブルでRole_idが削除しようとしている値であるdeleteを呼び出してから、ロール自体の削除を実行できます。これを行うのはかなり簡単です。
NHibernateはC#クラスのロールにコレクションを持たずにこれを行う方法を提供する必要があると思いますが、この動作はいつでもSQLで設定できます。データベース内のFKのカスケード削除を選択すると、自動で表示されるはずです。NHibのキャッシュに注意してください。
ただし、これを最後のリソースとして使用することを強くお勧めします。
ロールから Artifact
へのマッピングを作成する必要があります。
それを遅延読み込みにして、保護された仮想メンバーにマップして、実際にアクセスされないようにすることができますが、NHibernateがからロールを削除する必要があることを知るために、そこにマッピングする必要がありますArtefactAccess
テーブル