3

現在のプロジェクトでは、WebAPIEntityFrameworkモデル ファーストを使用しています。調査の結果、LazyLoading関連エンティティをロードするために使用する必要があるようです。

サービスで使用するとパフォーマンスとシリアル化の問題が発生する可能性があることを多くのブログで読んだLazyLoadingので、可能であれば避けたいと思います。

POCOオブジェクトを作成せずにこれを達成する方法について何か提案はありますか?

4

1 に答える 1

1

あなたの質問に対する簡単な答えは - use.Include()です。

例として、別の顧客を参照Customerする関連付けられたオブジェクトを持つオブジェクトがあるとします。アプリケーションで、 のリストと、それぞれを紹介した関連人物ReferredByを返す必要があります。CustomerCustomer

WebAPI メソッドは次のようになります。

[HttpGet]
public IQueryable<Customer> Customers() {
    return db.Customers.OrderBy(c => c.LastName).Top(10);
}

シリアライザーがこれを把握すると、さまざまなエラーが発生する可能性があります。詳細については、この記事を参照してください。ただし、本質的には、次の 2 つのことから生じます。

  1. ProxyCreation / LazyLoading - これは、「要求した場合にのみ、オブジェクト グラフに関連付けられたオブジェクトをオンデマンドでロードする」という EF の言葉です。
  2. サイクルのシリアル化 - つまり -Aを参照しB、 をB参照するAので、一方をシリアル化するたびに、もう一方をその子として再度シリアル化します。これにより、無限ループが作成されます。

すべての詳細やその他の問題については説明しません。これについては、ある程度詳しく説明した記事を既に提供しています。代わりに、アプリケーションで問題を解決する方法を次に示します。

  1. シリアライザーとして JSON.net を使用します。Visual Studio でプロジェクトの既定のシリアライザーとして設定する方法については、このリンクを参照してください (まだ設定されていない場合)。
  2. Global.asax構成の一部としてロードされるファイルまたはファイルの 1 つで、.cs次の設定を使用します。

    config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = 
        Newtonsoft.Json.ReferenceLoopHandling.Ignore;
    

    これは、JSON.netに参照ループをシリアライズしないように指示します。

  3. およびその各プロパティで を に設定MergeOptionします。私のアプリケーションでは、次のファイルを作成するファイルを編集してこれを行います。DataContextCollectionMergeOption.NoTracking.ttDataContext

    まず、コンストラクターを作成する行を見つけて、次のように変更します。

    MergeOption _defaultMergeOption = MergeOption.AppendOnly;
    
    public <#=code.Escape(container)#>() : this("name=<#=container.Name#>") { }
    public <#=code.Escape(container)#>(String connectionString) : base(connectionString) {
    <# if (!loader.IsLazyLoadingEnabled(container)) { #>
        this.Configuration.LazyLoadingEnabled = false;
    <# } #>
        this.Configuration.ProxyCreationEnabled = false;
        _defaultMergeOption = MergeOption.NoTracking;
    }
    

    次で始まる行を見つけます。

    <#  foreach (var entitySet in container.BaseEntitySets.OfType<EntitySet>()) { #>
    

    次の数行を次のように編集します。

    <#  foreach (var entitySet in container.BaseEntitySets.OfType<EntitySet>()) { #>
    <#= Accessibility.ForReadOnlyProperty(entitySet)#> ObjectQuery<<#=typeMapper.GetTypeName(entitySet.ElementType)#>> <#=code.Escape(entitySet)#> {
        get {
            var set = (((IObjectContextAdapter)this).ObjectContext).CreateObjectSet<<#=typeMapper.GetTypeName(entitySet.ElementType)#>>();
            set.MergeOption = _defaultMergeOption;
    
            return set;
        }
    }
    <#  }
    

    DataContextこれが何をするかというと、すべてのコレクションを にデフォルト設定しMergeOption.NoTracking、 と を自動的に無効にProxyCreationするコンストラクタを提供することですLazyLoading。プル元のインスタンスを作成するときはDataContext、次のように簡単に言うことができます。

    var db = new MyDataContext("ConnectionStringGoesHere");
    

上記を考えると、新しい WebAPI メソッドは次のように単純になります。

[HttpGet]
public IQueryable<Customer> Customers() {
    return db.Customers.Include("ReferredBy")
             .OrderBy(c => c.LastName).Top(10);
}

.Include()最初の SQL ステートメントの一部として子レコードをロードし (データベースへの合計 1 つのヒット)、シリアライザーは後方参照を無視するため、次のような JSON を生成できます。

[{
    Id: 1,
    FirstName: 'Frank',
    LastName: 'Abba',
    ReferredBy: {
        Id: 4,
        FirstName: 'Bob',
        LastName: 'Jones',
        ReferredBy: null
    }
 }, {
    Id: 4,
    FirstName: 'Bob',
    LastName: 'Jones',
    ReferredBy: null
 }
}]
于 2013-04-24T20:49:23.277 に答える