長い投稿を事前にお詫びします。誰かが助けてくれることを願っています。
ASP.NET アプリで NHibernate (2.1.2.4000 から 3.3.1.4000 へ) と Fluent NHibernate (1.1.0.685 から 1.3.0.0 へ) をアップグレードする作業を依頼されました。私はNHibernateの初心者ですが、これに数週間取り組んでいるので、ある程度理解しています。
たとえば、NHibernate の新しいバージョンにはプロキシ ジェネレータが組み込まれていることがわかっているので、以前使用していたプロキシ ジェネレータであった古い NHibnernate.ByteCode.Castle.dll への参照をすべて削除し、参照を取り除きました。 nhibernate.config からそのファイルに追加し、ビルトイン プロキシを使用することを意図しています。
私はさまざまな問題にうまく取り組んできましたが、行き詰まっている問題に遭遇しました。ウェブ上で見つけたものは、実際にそれに正確に対応しているようには見えません. これは驚くべきことです。なぜなら、このようなアップグレードを行った人なら誰でもこの問題を抱えていると思っていたからです。しかし、エンティティ ベース クラスのコーディング方法に問題があるのかもしれません。
2 つの Visual Studio ソリューションがあります。1 つは EntityBase クラスを含む「フレームワーク」ソリューションで、もう 1 つはフレームワーク ソリューションの DLL を使用するメイン アプリケーション ソリューションです。
EntityBase クラスには、プロキシの代わりに「実際の」オブジェクトを返す 2 つのメソッドがあります。これは、ここで説明されている「As」メソッドと非常によく似ています: http://sessionfactory.blogspot.com/2010/08/hacking-lazy-loaded -inheritance.html
私が理解しているように、これらのメソッドは本質的に、NHibernate プロキシ オブジェクトによってラップされた「実際の」(ドメイン) オブジェクトを返しています。私の場合、問題のメソッドは「CastTo」および「AsOfType」と呼ばれます。私の問題を引き起こしているように見えるのは、これらのメソッドの型制約です。
私の EntityBase クラスからの関連コードは次のとおりです。
/// <summary>
/// Represents the base class for a domain entity.
/// </summary>
/// <typeparam name="TIdentity">The type of the identity.</typeparam>
/// <remarks>
/// This class implements all the interfaces that all domain entities must have.
/// </remarks>
[Serializable]
public abstract class EntityBase<TIdentity> : IAuditable, IEntity<TIdentity>, IPersistent
{
/// <summary>
/// Casts to.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public virtual T CastTo<T>() where T : EntityBase<TIdentity>
{
return (T)this;
}
/// <summary>
/// Casts this entity to the type passed in.
/// This is required when trying to cast from a proxy.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public virtual T AsOfType<T>() where T : EntityBase<TIdentity>
{
return this as T;
}
そのため、メイン アプリケーション ソリューションで単体テストを実行すると、次のようなエラーが発生します。
プロキシ インスタンスの作成に失敗しました ---> System.TypeLoadException: アセンブリ 'LocationProxyAssembly、Version=0.0.0.0、Culture=neutral、PublicKeyToken=null' のタイプ 'LocationProxy' のメソッド 'AsOfType' が、より弱いタイプのメソッドを暗黙的にオーバーライドしようとしましたパラメータの制約。
そのため、NHibernate プロキシを作成している Reflection.Emit コードは、AsOfType および CastTo メソッドの型制約について不平を言っているようです。
そこで、これらの制約を少し緩和し、型制約でジェネリックを使用する代わりに、"Entity" を制約 (EntityBase から派生したクラス) として使用してみようと考えました。そこで、次のことを試してみました (はい、2 つのメソッドが本質的に同じことを行っていることはわかっていますが、これらのメソッドへのすべての呼び出しが壊れないように、EntityBase クラスのインターフェイスを保持しようとしています)。
[Serializable]
public abstract class EntityBase<TIdentity> : IAuditable, IEntity<TIdentity>, IPersistent
{
/// <summary>
/// Casts to.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public virtual T CastTo<T>() where T : Entity
{
if (!typeof(T).IsAssignableFrom(GetType()))
{
throw new InvalidCastException();
}
return this as T;
}
/// <summary>
/// Casts this entity to the type passed in.
/// This is required when trying to cast from a proxy.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public virtual T AsOfType<T>() where T : Entity
{
return this as T;
}
したがって、テストは別の方法で失敗します。次のような単体テストがあります。
/// <summary>
/// Tests the type of the get real.
/// </summary>
[TestMethod, TestCategory("Integration")]
public void TestEntityProxyCastToMethod()
{
using (var unitOfWork = UnitOfWork.Begin(Database.MainSolution.Name()))
{
var person = unitOfWork.GetProxy<Person>(new Guid("E196BC94-DFBA-4D6C-B504-03E00F5CA914"));
Assert.IsTrue(person.IsOfType<Employee>());
var employee = person.AsOfType<Employee>();
Assert.IsNotNull(employee);
}
}
単体テストの実行時にスローされる例外は次のとおりです。
テスト メソッド MainSolution.Data.Tests.EntityProxyTests.TestEntityProxyCastTo が例外をスローしました: System.InvalidOperationException: レイト バインド操作は、ContainsGenericParameters が true である型またはメソッドに対して実行できません。
したがって、これは依然として Reflection.Emit エラーのように見えます。これは、NHibernate がプロキシを生成しようとしたときに発生しています。NHibernate が現在プロキシを生成している方法は、EntityBase クラスをコーディングした方法と互換性がないことは明らかです。NHibernate.ByteCode.Castle.dll を使用してプロキシを生成していたときは問題なく動作していましたが、これらのメソッドの型制約に満足していないことは明らかです。
さて、このような投稿 ( Nhibernate: Get real entity class instead of proxied class ) を見てきました。これは、クラスを「プロキシ解除」して、基になる「実際の」オブジェクトを取得する必要があることを示唆しています。しかし、それは(おそらく)これらのメソッドのシグネチャを変更し、「CastTo」および「AsOfType」メソッドなどへのすべての呼び出しを中断することを意味します。私が理解しているように、UnitOfWorkを取得し、そこから現在のセッションを取得する必要があります、次に「Unproxy」を実行して基になるオブジェクトを取得しますが、現在のコードでは、基本的に「this as T」を返すだけで機能します。呼び出し元のコードで現在のセッションを取得し、それを EntityBase のメソッドに渡すことができるのではないかと考えました。
したがって、質問は次のとおりです。a) 何が間違っているのか、b) 3.3 を指定して正しく行うにはどうすればよいですか。c) EntityBase クラスの "CastTo" および "AsOfType" メソッドの既存の署名を保持しながら、NHibernate にプロキシを正しく生成させる方法はありますか?
助けていただければ幸いです。