6

C# プロジェクトに NHibernate を使用しているため、いくつかのモデル クラスがあります。

次の例を想定してみましょう。

using System;

namespace TestProject.Model
{
    public class Room
    {
        public virtual int Id { get; set; }
        public virtual string UniqueID { get; set; }
        public virtual int RoomID { get; set; }
        public virtual float Area { get; set; }

    }
}

これらのオブジェクトを NHibernate でマッピングすることは、今のところ問題なく機能しています。ここで、新しい Room オブジェクトを生成し、それをデータベースに保存したいと考えています。各メンバーを別々に設定することを避けるために、新しいコンストラクターをモデル クラスに追加します。仮想メンバーの下に次のように記述します。

public RoomProperty()
{

}


public RoomProperty(int pRoomId, int pArea)
{
        UniqueID = Guid.NewGuid().ToString();
        RoomID = pRoomId;
        Area = pArea;
}

FxCop でコードを分析すると、次のことがわかります。

"ConstructorShouldNotCallVirtualMethodsRule"
This rule warns the developer if any virtual methods are called in the constructor of a non-sealed type. The problem is that if a derived class overrides the method then that method will be called before the derived constructor has had a chance to run. This makes the code quite fragile. 

このページでは、これが間違っている理由についても説明しており、私もそれを理解しています。しかし、私は問題を解決する方法がわかりません。

すべてのコンストラクターを消去して次のメソッドを追加すると...

public void SetRoomPropertyData(int pRoomId, int pArea)
        {
            UniqueID = Guid.NewGuid().ToString();
            RoomID = pRoomId;
            Area = pArea;

        }

.... 標準コンストラクターを呼び出した後にデータを設定するには、NHibernate が初期化に失敗するため、アプリケーションを開始できません。それは言います:

NHibernate.InvalidProxyTypeException: The following types may not be used as proxies:
VITRIcadHelper.Model.RoomProperty: method SetRoomPropertyData should be 'public/protected virtual' or 'protected internal virtual'

しかし、このメソッドを仮想に設定すると、コンストラクターで仮想メンバーを設定したときと同じ間違いになります。これらの間違い (違反) を回避するにはどうすればよいですか?

4

3 に答える 3

5

問題は仮想セットにあります。基本クラスコンストラクターの仮想プロパティに値を渡すと、基本セットの代わりにオーバーライドされたセットが使用されます。オーバーライドされたセットが派生クラスのデータに依存している場合、派生クラスのコンストラクターがまだ実行されていないため、問題が発生します。

サブクラスがオーバーライドされたセットの状態のデータを使用しないことが絶対に確実な場合は、基本クラスコンストラクターで仮想プロパティを初期化できます。ドキュメントに適切な警告を追加することを検討してください。

可能であれば、プロパティごとにバッキングフィールドを作成し、それらを基本クラスのコンストラクターで使用してみてください。

プロパティの初期化を派生クラスに延期することもできます。これを実現するには、派生クラスのコンストラクターで呼び出す基本クラスに初期化メソッドを作成します。

于 2013-02-20T14:45:09.490 に答える
1

次のいずれかが機能することを期待しています。

  1. プロパティを非仮想にします (NHibernate がサポートしている限り優先されます)。
  2. 自動実装されたプロパティから明示的なバッキング フィールドを持つプロパティに変更し、プロパティを設定する代わりにコンストラクターでフィールドを設定します。
  3. Create最初にオブジェクトを構築し、構築されたオブジェクトを返す前にプロパティに値を設定する静的メソッドを作成します。

編集:コメントから、オプション#3が明確ではなかったことがわかります。

public class Room
{
    public virtual int Id { get; set; }
    public virtual string UniqueID { get; set; }
    public virtual int RoomID { get; set; }
    public virtual float Area { get; set; }

    public static Room Create(int roomId, int area)
    {
        Room room = new Room();
        room.UniqueID = Guid.NewGuid().ToString();
        room.RoomID = roomId;
        room.Area = area;
        return room;
    }
}
于 2013-02-20T14:45:33.050 に答える