2009-05-29 4 views
7

これは概念的には私の質問hereに関連しています。しかし、私はNHibernateで遊んできた、と私の質問の真のコアが何であるかを実現しました。.NET ORMでコンストラクタが「正しく」使用されていますか?

古典的なオブジェクト指向設計では、データを適切にカプセル化するために、データメンバ(フィールド)に格納された値をオブジェクトのコンストラクタに渡すのが一般的なパターンです。 ではなくを変更する必要がある値は、アクセサ(読み取り専用プロパティ)のみで公開されます。変更が許可されているものには、アクセサとミューテータの両方があります(読み書き可能なプロパティ)。 適切な O/RMは、これらの規則を遵守し、オブジェクトを作成するときに利用可能なコンストラクタを使用する必要があります。読み書きプロパティ、リフレクション、またはその他のハック(IMHO)メソッドに依存することは間違っているようです。

これには.NET O/RMソリューションがありますか? Praveenさんのポイントに対処するために

EDIT

、私はコンストラクタを選択するための「デフォルト」のアルゴリズムを持っているプロジェクトがあることを知っている - のStructureMapは、例えば、常に最も引数を持つコンストラクタを使用して、ない限り、カスタム属性を持つコンストラクタにマークを付けます。私はこれが状況を処理する効果的な方法であることがわかります。おそらくIoCコンテナに加えて、ORMは私が必要としている解決策を提供するでしょうが、これは本質的に悪いことではありませんが、ORMを使用するための不要な追加手順です。

+0

もう1つ興味深い質問があります。コンストラクタにビジネスロジックが含まれていますか?私。クラスAのインスタンスを作成すると、そのコンストラクタは永続クラスBのインスタンスを作成し、それをプライベートセッターでプロパティに割り当てることができます。 –

+1

@Alex:私の目標は、ドメインオブジェクトに永続性を知らないことです。ドメインオブジェクトからパーシステンスオブジェクトを作成するロジックを持つと、それに違反し、ドメインオブジェクトをパーシスタンスメソッド(別の悪いこと)にしっかりと結合します。 –

答えて

1

Joveでは、私はそれを持っていると思います!

皆様のおかげで、私は自分の質問に答える必要があります。私は、を掘り起こし、OOの原則を熟考し、それをC#言語と.NETフレームワークについて深く考えてみました。

私が思いついた答えは、です。は、私が正しくコンストラクタを使って解決できなかったことを必要とし、最後にコンストラクタ自体に関連していません。それはlazy loadingです!

基本的には、ドメインクラス内で遅延ロードを実装することなく(永続性の無知と柔軟性のための主要なno-no)、ドメインクラスをサブクラス化せずに実行する方法はありません。このサブクラス化は、NHibernateが仮想プロパティを必要とする理由です。

私はまだ間違いなく(少なくとも非コレクションのために...遅延ローディングは、その場所を持っている)親クラスのフィールドを取り込むために、むしろ反射またはいくつかの他の方法よりも、コンストラクタを利用する方が良いだろうと思いますが、私引数のないコンストラクタがどこにあるかを見てください。

+0

あなたがここに来たという結論を本当に理解できません...ドメインオブジェクトについては、ドメインについて何かを表現しているので、デフォルトのコンストラクタを持つことは問題ないと思いますか?オブジェクトが遅延読み込み可能であることを確認しますか?それでも、ドメインロジックではなく、永続性に関連しているようです... – Domenic

+0

私の結論は、 "純粋な"アプローチを実装することを不可能ではないにしても困難にする「機械的」問題があるということです。私はまだそれが本当に好きではありませんが、私の実践的な側面は、物事を完了するためにそれと一緒に行きたいです。 –

2

残念ながら、これは.NETでは何らかの方法でコンストラクタをマーク付けしなくても不可能です。

コンストラクタごとにアセンブリメタデータに格納されるメソッドシグネチャには、コンストラクタの各パラメータの型のみが含まれます。 .NET ORMがどのコンストラクタを使用するかを実際に知る方法はありません。

.ctor() 
.ctor(string, string) 
.ctor(string, string, string) 

ORMは、例えば、あなたのCustomerオブジェクトのためのFirstName、LastNameのとMiddleNameに相当する.ctorパラメータ知るための方法はありません:すべてのORMを見ているが、このようなものです。

このサポートを提供するために、.NET ORMは、各パラメータに対して定義するカスタム属性の読み込みをサポートする必要があります。

パブリックCustomer([プロパティ( "FirstName")]文字列FirstName、[プロパティ( "LastName")]文字列LastName、[プロパティ( "MiddleName")]文字列MiddleName )

この2つの欠点がある:ない方法は、私は考えることができるという(ありません

  1. 、誰かがおそらく私を修正しますが)、これはマッピングファイルに行くことができるということ。
  2. ORMは各プロパティに対して個別の値を取得できる必要があるため、以前と同じマッピングを作成する必要があります。

このように、コンストラクタをマークするすべての作業を行う必要があります。同時に、以前と同じようにクラスを正確にマッピングする必要があります。

+1

私はクラスのマッピングに全く反対しません - 実際には、私はソースコードをマークアップする方が好きです。あなたは本質的な困難に関するいくつかの興味深い点を提起します - ありがとう。 –

+1

"アセンブリメタデータに各コンストラクタ用に格納されたメソッドシグネチャ"には、その型だけでなくパラメータ名も含まれます。 ORMは、マッピングを必要とせずに名前に基づいてパラメータをプロパティに一致させることができます。しかし、使用するctorの問題はまだあります。 – Lucas

+0

@ルーカス:非常に真実 - .NET Reflectorはパラメータの名前を表示します。誰が知っている - 私はちょうど私がここでスクラッチする必要があるかゆみを見つけた。 –

3

ほとんどのORMが実際にこの概念をサポートしていると思いますが、少なくともDataObject.Netはそうしています。このコードは、それが期待どおりに動作:

[HierarchyRoot(typeof(KeyGenerator), "Id")] 
public class Message : Entity 
{ 
    [Field] 
    public int Id { get; private set; } 

    [Field(Length = 100)] 
    public string Text { get; private set; } 

    public Message(string Text) 
    { 
    Text = text; 
    } 
} 

編集:内部トランザクション状態でデータオブジェクトストアデータを、そしてPostSharpによって生成された特殊な実体のコンストラクタを使用します。もちろん、ORMがpocoオブジェクトを使用する場合はそれほど簡単ではありません。

0

これは、変更すべきではない値を考慮するかどうかによって異なります。オートインクリメント、計算カラムなどは、これの良い候補です。

これは確かにです。は、私が書いたORMを使用しており、読み取り専用プロパティの値を設定しようとすると例外がスローされます。

更新:

は覚えているのコンストラクタは、同様に永続データに使用されています。オブジェクトがコンストラクタでPKを受け入れるのは一般的なパターンで、自動的にそのレコードを取得します。

+0

プロパティを作成している場合は、それをO/RMではなくコードジェネレータにします(またはその両方)。 –

+0

それは両方です! ORMがマッピングを動的に生成しなければならないというルールはない。 – RedFilter

+1

あなたのPKが代理キーである場合、コンストラクタでPKを使用すると、私にはコード臭のビットになります。 * business *キーがPKの場合、それはもう少し意味がありますが、その場合は、オブジェクトの構築が間違ったアプローチであることを示唆しています。PKをリポジトリに渡す方が良い方法でしょう。 –

0

通常、ORマッパーがコンストラクタのみを使用することはできません。コンストラクタに渡された値によって初期化されたオブジェクトが、その後、メソッド呼び出しによって状態が変更されたものとします。この場合、オブジェクトは有効な初期状態でなく、したがってコンストラクタによって拒否された状態で永続化される可能性があります。

このようなORマッパーがあるかもしれませんが、それには限界があります。与えられた例で示すように、ORマッパーによるオブジェクトのカプセル化を悪い設計としてバイパスすることは考慮されませんが、状況によっては要件として使用されます。

+1

これは有効な引数ではありません。オブジェクトの永続化状態がコンストラクタに対して有効である場合、オブジェクトの存続期間内の任意のポイントに対して有効である必要があります。存続期間中に有効な状態が変化するオブジェクトは、永続化しない(または部分的にしか存続しない)、またはおそらくリファクタリングを必要とする可能性があります。 –

+0

引数は他の方向に進みます。有効期間は、構築時には有効ではありません。一例として、多分、おそらくプロジェクトのためのライフサイクル管理システムを取る。それらは常に初期状態で作成され、プロジェクトが終了している間は状態が終了、終了、またはアーカイブされた状態になるまで変更されます。状態は常に暗黙的に初期状態なので、状態を引数とするコンストラクターは必要ありません。もちろん、どのような状態でもプロジェクトを永続化して取得できる必要があります。 –

+1

あなたの主張が間違っていると思います。なぜなら、プロジェクトオブジェクトは、オープン、終了、アーカイブ、またはその他の*有効な状態でインスタンス化できない理由はありません。終了するまでプロジェクトをアーカイブできない場合は、まだ完了していない終了日を持つアーカイブ済み状態は無効ですが、新しくインスタンス化されたプロジェクトをアーカイブ済み状態にする必要はありません。 「欲張りな」コンストラクタ選択メソッドは、実際にこれをサポートします(デフォルトでは選択されないか、または引数なしのctorは選択されませんが、それを持つことが理にかなっていれば利用可能になります)。 –

1

なぜこれは間違っていると思いますか? オブジェクトを再構成するときにOR/Mでビジネスロジックを実行しますか? IMHO、いいえ。

DBからオブジェクトをロードすると、OR/Mは何があってもオブジェクトを再構成できるはずです。再考中に値を設定すると、別の値を変更する何らかの種類のロジックがトリガーされるべきではありません(ORMによって値が与えられている必要があります)。

でも、コンストラクタにはオブジェクト作成時に必須のフィールドを「有効」状態にするためのパラメータ。 次に、何らかの計算によって値が与えられたパブリックの読み取り専用プロパティがあり、そのプロパティも永続化する必要があります。
反射がオブジェクトを再構成する「匂い」であると感じる場合、どのようにこの状況に対処しますか?あなたは何とか '読み取り専用'の値を設定できるパブリックメソッドを作成する必要がありますが、これはカプセル化を中断させ、あなたはそれをやっていません。

+1

計算によって作成された、オブジェクトの永続状態の一部ではない、どのような種類のパブリックの読み取り専用プロパティが存在しますか?計算ではどのデータが使用されますか?オブジェクトからのデータであれば、簡単に再構成できます。オブジェクトの外部のデータであれば、データまたは読み取り専用フィールドのいずれかが間違った場所にあると主張します。 –

+0

@Harper:「オブジェクトからのデータであれば、簡単に再構成できます」という便利な読み取り専用プロパティーで!それはDBの計算された列に似ています – Lucas

+0

またはあなたのオブジェクトにあなたが持っている何らかの 'ステータス'フラグはどうですか? ユーザーがプロパティを変更するたびに設定するフラグ 'PropertyChanged'がありますが、オブジェクトがDBからロードされたときにこのフラグが設定されないようにしたいとします。 –

0

Skeet氏はしばらく前に「ビルダー」パターンを提案しました。私はそれをPOCOクラス内のパブリッククラスにしました。 POCOと同じプロパティを持ちますが、すべての読み書きが可能です。 POCOは必要に応じて読み取り専用ですが、PRIVATE SETとなります。このようにして、POCOがインスタンス化され、コンストラクタのためのファンキーな引数がないときに、ビルダーはプロパティを設定できます。そこには「ポプキン不変性」の種類があります。

+0

これはDRYの原則に違反していると思われます。私は個人的に反復的な作業に反対しています。特に実際には正しいアプローチではないと思っています。 –

+0

私は理解します。コンストラクタパラメータのアプローチについての私のバグは、それが私には適切なものを列挙するためのもう一つの場所であるということです。コンストラクタparamsはそれらをリストします。コンストラクタはプライベートフィールドを設定してそれらをリストします。フィールドそのもの...パルメータを変更する必要がありますか? brhrr – n8wrl

+0

ええと... C++のメソッド(イニシャライザーリスト)でさえも*もっと*良くありません。しかし、コンストラクタのパラメータのアプローチは、私が戻って教えてきたOOの原則(OK、10年前 - 私はそれほど古くはありませんでした! –

1

私は、コンストラクタがその生涯の始めにオブジェクトを作成するために必要な以前のポスターの1つに同意します。コンストラクタを使用して、現在アーカイブされている状態にある既存のオブジェクトを水和することは、せいぜい逆に直感的です。

重要なORMがすべて現時点で欠落しているように見えるのは、データベースや他のデータストアに格納されているように、最後の既知の状態からドメインオブジェクトを再構成することです本質的に建設または改造作業。どちらのコンストラクタもプロパティセッターも使用しないでください。むしろ、この種の操作に最も密接に対応するフレームワークのメカニズムはシリアライゼーションです!オブジェクトの状態を保存し、後でISerializableインターフェイス、標準シリアル化コンストラクタなどを使用してオブジェクトを復元するためのパターンは既に認識されています。オブジェクトをデータベースに保存することは、基本的にストリームに保存することと変わりありません。実際には、直列化中に使用されるStreamingContextStates列挙値の1つがPersistence!です。 IMHO、これは永続化メカニズムを設計する際の標準的なアプローチでなければなりません。残念ながら、私はボックスの外でこれをサポートするORMについて知らない。

シリアル化のために設計されたオブジェクトは引き続きPOCOであることにも注意してください。永続性の無知は違反されていない。重要なポイントは、ドメインオブジェクトは、永続化および復元に必要なデータと、そのデータを復元する順序(重要な場合があります)を最もよく知る必要があることです。オブジェクトには、リレーショナルデータベース、フラットファイル、バイナリブロブなど、格納される特定のメカニズムがあります。

+1

ISerializableが存在するずっと前から、コンストラクタを使用してオブジェクトを逆シリアル化するのは一般的でした(逆説的ではありません)。コンストラクタはオブジェクトのインスタンスを生成します。コンストラクタ呼び出しと*ドメインエンティティの生存時間には固有の結びつきはありません。オブジェクトはそうですが、オブジェクトはドメインエンティティの*表現*であり、エンティティ自体ではありません。 –

関連する問題