ValueInjecterを使用して、EntityFrameworkPOCOのディープクローンを同様のDTOクラスに作成する際に問題が発生しました。
複数の関連エンティティ/ナビゲーションプロパティを持つ子エンティティを持つ複雑なPOCOオブジェクトからやや単純なDTOに注入している場合、ValueInjecterはまだ複数のプロパティ値に触れており、データベースからこのデータの遅延読み込みが発生しているようです。
ValueInjecterは、指定されたターゲットに値を注入する準備をするときに、特定のソースオブジェクトのすべてのプロパティの値を取得すると思います。
私の実際のプロジェクトはかなり複雑ですが、例として、NerdDinnerの例を取り上げて、はるかに単純な方法で問題を再現しました。(NerdDinnerは、EF4を使用した最初の開発コードの例です(ScottGu NerdDinnerの例)。
したがって、2つのモデルクラスがあります。
public class Dinner
{
public int DinnerId { get; set; }
public string Title { get; set; }
public DateTime EventDate { get; set; }
public string Address { get; set; }
public string HostedBy { get; set; }
public virtual ICollection<RSVP> Rsvps { get; set; }
}
と
public class RSVP
{
public int RsvpID { get; set; }
public int DinnerID { get; set; }
public string AttendeeEmail { get; set; }
public virtual Dinner Dinner { get; set; }
}
DTOクラスも作成しました。
public class DinnerDTO
{
public int DinnerId { get; set; }
public string Title { get; set; }
public DateTime EventDate { get; set; }
public string Address { get; set; }
public string HostedBy { get; set; }
}
にRsvpsコレクションがないことに注意Dinner
してくださいDinnerDTO
。
また重要なのは、オブジェクトのディープクローン作成にCloneInjection規則を使用していることです。このコードは、ここSOだけでなく、他の多くのサイトでも、ディープクローンインジェクションを実行するためのアプローチとして提案されています。このコードはここにあります:CloneInjectionコード
ここで、発生する遅延読み込みを強調するために、Id=1のディナーに10,000のRSVPを挿入しました。
次に、次のコードを実行します。
var dinner = nerdDinners.Dinners.Where(x => x.DinnerId == 1).FirstOrDefault();
DinnerDTO dinnerDTO = new DinnerDTO();
dinnerDTO.InjectFrom<CloneInjection>(dinner);
の行にブレークポイントを設定し、InjectFrom
それをステップオーバーすると、10,000 RSVPを遅延ロードするため、かなりの遅延が発生します。Match
とメソッドの両方でCloneInjectionコードにブレークポイントを設定した場合、ロードラグSetValue
が解決されるまで、どちらもヒットしません。これは、プロパティの遅延負荷を発生させているのはValueInjecterの内部にある必要があることを示していRSVPs
ます。
さて、上記のコードをこれに変更すると:(Include
クエリにを追加する)
var dinner = nerdDinners.Dinners.Where(x => x.DinnerId == 1).Include("RSVPs").FirstOrDefault();
DinnerDTO dinnerDTO = new DinnerDTO();
dinnerDTO.InjectFrom<CloneInjection>(dinner);
この変更により、RSVPのリストの「EagerLoad」が強制されます。予想どおり、ラグはクエリと一致し、ラインはInjectFrom
ラグなしで過ぎ去ります。
StackOverflowに関する漠然と関連するいくつかの投稿を読みましたが、データコンテキストでLazyLoadingを無効にしてから有効にすることをお勧めするものもあります。私はそれを試しました、そしてそれがうまくいく間、それはかなり汚れたように感じました。
私はこの投稿(遅延読み込みや熱心な読み込みをトリガーせずにNHibernate POCOをDTOにコピーする)と関連コードを読みました。彼のアプローチは、プロパティが初期化されていないプロキシであるかどうかを判断し、何らかの方法でそれらを取り除くためにいくつかのNHibernateメソッドを使用しているようです。EF4で似たようなものを見つけることができませんでした。
私を本当につまずかせるのは、Rsvpsコレクションが私のDTOオブジェクトにも含まれておらず、その値にも興味がないということです。これは私には正しくないようです。ValueInjecterコードは、ターゲットオブジェクトが気にしない可能性のあるプロパティの値を問い合わせるべきではないと思います。
ValueInjecterでこの動作をオーバーライドできる方法はありますか?どういうわけか、 ?のSetValue
メソッドのように、値が必要であることが絶対に確実になるまで、プロパティ値の評価を延期します。ConventionInjection
そうすれば、少なくとも、私のDTOが望まないプロパティを評価することはありません。
私が考えることができる最善の解決策は、ValueInjecterまたはカスタム規則を使用して、アンロードされた遅延読み込みプロパティを何らかの方法で検出できるようにすることです。評価する代わりに、ターゲットでそのプロパティをnullに設定します。しかし、それは不可能だと思います。
私が使用すべきEFを介したより良いアプローチはありますか?データベース内のすべてを熱心にロードしたくありません。
私は完全にオフになっていて、問題はValueInjecterにはまったくありませんか?
*編集* 私は解決策を見つけてこの質問に答えました、私がそれを間違っているだけなのか、それとももっと良いアプローチがあるのか、私はまだ興味があります。