NHibernate:単一の列を多対1からプリミティブ型にマッピングする
-
06-07-2019 - |
質問
次のマッピングがあります:
<set name="People" lazy="true" table="ProjectPeople">
<key column="ProjectId" />
<composite-element class="PersonRole">
<many-to-one name="Person" column="PersonId" cascade="save-update" not-null="true" />
<many-to-one name="Role" column="RoleId" cascade="save-update" not-null="true" />
</composite-element>
</set>
これで、ドメイン内のロール用に別のクラスを作成したくありません。ロール名のみが必要です。ただし、DBロールでは、別のテーブルRole (Id, Name)
に正規化する必要があります。
次のPersonRoleクラスを使用するようにマッピングするにはどうすればよいですか
public class PersonRole {
public virtual Person Person { get; set; }
public virtual string Role { get; set; }
}
更新:報奨金を追加しました。私だけでなく役に立つ質問のようです。
解決
個人的にはYassirのようなRoleクラスを作成します
ただし、現在の構造を使用する場合は、個人テーブルへの外部キーとロールの説明を含むビューを作成します。
Setビューを変更して、新しいビューを指すようにします 次に、多対1マッピングではなくプロパティになるようにロールマッピングを変更します。
ただし、このアプローチを取ると、ビューを再参照しているため、ロールを更新できなくなると思います。
編集:ロールを更新するには、カスケードファイルが機能するようにマッピングファイルに<sql-insert>,<sql-update> and <sql-delete>
を追加します
他のヒント
それは不可能だからといって、あなたが望む答えを実際に得られないでしょう。 (N)HibernateはObject-Relational-Mappingフレームワークであり、 3種類のマッピング戦略:
- クラス階層ごとのテーブル
- サブクラスごとのテーブル
- 具体的なクラスごとのテーブル
また、formula
やsql-insert
などを使用してこれから逸脱することもできますが、わかっているように、これらは最終的に苦痛を引き起こすだけで、Hibernateコミュニティによって奨励されておらず、悪いですコードの保守性のため。
解決策
実際には、非常に簡単です。 Role
にはクラスを使用しません。 Role型のクラスを expose したくないこと、およびprObject.Role.Name
を常に入力する必要がないことを意味すると思います。ただprObject.Role
、これは文字列を返す必要があります。いくつかのオプションがあります:
- PersonRoleなどで内部クラスを使用します。このクラスは内部クラスまたはプライベートクラスです。メンバーフィールドを設定および更新するプロパティRoleを追加します。
- 内部クラスを使用します。メンバーフィールドを設定および更新するプロパティRoleを追加します。
オプション2を調べましょう:
// mapped to table Role, will not be visible to users of your DAL
// class can't be private, it's on namespace level, it can when it's an inner class
internal class Role
{
// typical mapping, need not be internal/protected when class is internal
// cannot be private, because then virtual is not possible
internal virtual int Id { get; private set; }
internal virtual string Name { get; set; }
}
// the composite element
public class PersonRole
{
// mapped properties public
public virtual Person Person { get; set; }
// mapped properties hidden
internal virtual Role dbRole { get; set; }
// not mapped, but convenience property in your DAL
// for clarity, it is actually better to rename to something like RoleName
public string Role /* need not be virtual, but can be */
{
get
{
return this.dbRole.Name;
}
set
{
this.dbRole.Name = value; /* this works and triggers the cascade */
}
}
}
そして、マッピングは期待どおりに見える可能性があります。結果:クラスごとに1つのテーブルのルールに違反していません(編集:askerが明示的にそのルールに違反することを望んでおり、Hibがそれをサポートしている、それは正しいです)典型的なオブジェクト指向技術を使用して、オブジェクトを変更およびアクセスから隠します。すべてのNH機能(カスケードなど)は引き続き期待どおりに動作します。
(N)Hibernateは、この種の決定に関するものです。明快さ、簡潔さ、保守性を損なうことなく、オブジェクト指向またはORMの規則に違反することなく、データベースに対して十分に考え抜かれた安全な抽象化レイヤーを作成する方法
更新(q。が閉じられた後)
この種の問題に対処する際によく使用する他の優れたアプローチは次のとおりです。
-
通常通りにマッピングを作成し(つまり、テーブルごとに1クラス、あなたはそれが気に入らないが、それは最良の方法です)、拡張メソッドを使用します:
// trivial general example public static string GetFullName(this Person p) { return String.Format("{0} {1}", p.FirstName, p.LastName); } // gettor / settor for role.name public static string GetRoleName(this PersonRole pr) { return pr.Role == null ? "" : pr.Role.Name; } public static SetRoleName(this PersonRole pr, string name) { pr.Role = (pr.Role ?? new Role()); pr.Role.Name = name; }
-
通常はマッピングを作成しますが、
partial class
esを使用します。これにより、<!> quot; decorate <!> quot;あなたの好きなようにあなたのクラス。利点:生成されたテーブルのマッピングを使用する場合、何度でも再生成できます。もちろん、<!> quot; bloat <!> quot;を減らしたいという要望を考慮して、部分クラスは別々のファイルに入れる必要があります。これはおそらく現時点では良いシナリオではありません。public partial class PersonRole { public string Role {...} }
-
おそらく最も単純です:
ToString()
にString.Format
をオーバーロードするだけで、Role.Name
や友人での使用に適していますが、もちろん割り当て可能にはなりません。デフォルトでは、各エンティティークラスまたはPOCOには<=>オーバーロードが必要です。
NHibernateで直接これを行うことは可能ですが、q。私はそれを見る時間がある前に閉じられました(誰にも障害はありません、私はちょうど時間を持っていませんでした)。 Hibernate HBMマッピングを使用して実行する時間が見つかった場合は、アプローチに同意しなくても更新します。最終結果が他のプログラマーにとってあまり明確でなく、全体的にあまり明確でない場合、Hibの高度な概念に取り組むことは良くありません(そのテーブルはどこに行きましたか?そのテーブルにIDao抽象化がないのはなぜですか? NHibernateベストプラクティスおよび S#arp )。ただし、それでも演習は興味深いものです。
<!> quot;ベストプラクティス<!> quot;のコメントを考慮してください。通常の状況では、<!> quot;テーブルごとに1つのクラス<!> quot;だけでなく、IDaoXXXも1つにする必要があります。各テーブルに1つのDaoConcreteXXXと1つのGetDaoXXX。クラス/インターフェース階層を使用して、読み取り専用テーブルと読み取り/書き込みテーブルを区別します。それはテーブルごとに最低4つのクラス/コード行。これは通常自動生成されますが、データレイヤー(dal)に非常に明確なアクセスレイヤー(dao)を提供します。データ層は、可能な限り質素に維持するのが最適です。これらの<!> quot;ベストプラクティス<!> quot;はありません。 <=>を<=>に移動するための拡張メソッドまたは部分メソッドを使用できないようにします。
これらは、一般的なベストプラクティスです。特定の特別な場所や典型的な場所では、常に可能または実行可能である必要はありません。
私がモデルにロールクラスを追加するのであれば、多対1をプリミティブ型にマッピングすることはできないと思います
これは、オブジェクト指向の純粋主義者にとって最大のターンオフです。 確かに目標は、動作するアプリケーションを持つことです。完全なクラス階層の一部のバージョンではありません。それでは、<!> quot; prObject.Role.Name <!> quotをコーディングする必要がある場合はどうでしょうか。 <!> quot; prObject.Role <!> quot;の代わりに。これは、より信頼性の高いプログラムを作成するのにどのように役立ちますか?
アプリケーション設計の純粋主義者の観点からは、あなたが望むことはまったく間違っています。人は複数の役割を持つことができます。通常、役割は複数の人に割り当てることができます。 遅延しないデータモデルが1人あたりのロールが多い場合に、なぜこのようなすべての問題に取り組んで、1人あたりの非現実的な1つのクラス階層を強制するのですか?
本当に<!> quot; 1人につき1つのロールのみがある場合<!> quot;ルールは、基礎となるデータモデルに反映される必要があります。