7

長くなって申し訳ありませんが、少なくとも私はすべての情報を理解して助けてくれると思いますか?

熱心な読み込みを使用してデータベースからデータを読み込みたいと考えています。

データは 5 つのテーブルに設定され、m:n 関係の 2 つのレベルが設定されます。したがって、データを含む 3 つのテーブルがあります (階層の上から下に並べられています)。

CREATE TABLE [dbo].[relations](
    [relation_id] [bigint] NOT NULL
)

CREATE TABLE [dbo].[ways](
    [way_id] [bigint] NOT NULL
)

CREATE TABLE [dbo].[nodes](
    [node_id] [bigint] NOT NULL,
    [latitude] [int] NOT NULL,
    [longitude] [int] NOT NULL
)

最初の 2 つは、実際には独自の ID のみで構成されています (ここでは関係のない他のデータをフックするため)。

これら 3 つのデータ テーブルの間には、並べ替えのヒントを含む 2 つの m:n テーブルがあります。

CREATE TABLE [dbo].[relations_ways](
    [relation_id] [bigint] NOT NULL,
    [way_id] [bigint] NOT NULL,
    [sequence_id] [smallint] NOT NULL
)

CREATE TABLE [dbo].[ways_nodes](
    [way_id] [bigint] NOT NULL,
    [node_id] [bigint] NOT NULL,
    [sequence_id] [smallint] NOT NULL
)

これは基本的に、OpenStreetMap データ構造の一部です。Entity Framework にこのデータベースからオブジェクトを作成させ、テーブルとまったく同じようにクラスを設定しました。m:n テーブルは実際にはクラスとして存在します。(EFでは、明示的な中間クラスがなくてもオブジェクトのm:n関係を構築できることを理解しています-この方法でオブジェクトモデルを変更しようとする必要がありますか?)




やりたいこと:私のエントリ ポイントは、関係の 1 つの項目です。

最初に中間の m:n 関係を熱心にロードし、次にループでそれを反復処理し、最も低い関係を熱心にロードするのが最善だと思います。私は次の方法でそれをやろうとします

IQueryable<relation> query = context.relations;
query = query.Where( ... ); // filters down to exactly one
query = query.Include(r => r.relation_members);
relation rel = query.SingleOrDefault();

これにより、リレーションとすべての 1:n 情報が、データベースへの 1 回のトリップでロードされます。しかし、中間データテーブルの「方法」ではなく、1:n テーブルのみをロードすることに気付きました。

次のように行を変更しても、これは変わりません。

query = query.Include(r => r.relation_members.Select(rm => rm.way));

ここでミドルレベルをロードできないようですね。

まったく機能しないのは、ノードレベルのデータを熱心にロードすることです。私は次のことを試しました:

foreach (relation_member rm in rel.relation_members) {
    IQueryable<way_node> query = rm.way.way_nodes.AsQueryable();
    query = query.Include(wn => wn.node);
    query.Load();
}

これは機能し、中間レベルのウェイと way_node のすべての 1:n 情報を反復ごとに 1 つのステートメントで積極的にロードしますが、ノードからの情報 (緯度/経度)はロードしません。これらの値のいずれかにアクセスすると、データベースへの別のトリップをトリガーして、1 つのノード オブジェクトをロードします。

1 リレーションをロードしたいので、この最後のトリップは致命的です -> 300 ウェイ、各ウェイ -> 2000 ノード。結局、私はサーバーに 1 + 300 + 300*2000 を打っています... 改善の余地があると思います。

しかし、どのように?有効な構文で記述されたこの最後のステートメントと熱心な読み込みを取得できません。興味がない。1 つのリレーションから始めて、1 回のトリップでオブジェクト グラフ全体をロードする方法はありますか?

4

3 に答える 3

7

グラフ全体を 1 回の往復でロードすると、次のようになります。

IQueryable<relation> query = context.relations;
query = query.Where( ... ); // filters down to exactly one
query = query.Include(r => r.relation_members
    .Select(rm => rm.way.way_nodes
        .Select(wn => wn.node)));
relation rel = query.SingleOrDefault();

Includeただし、これまではうまくいかなかったと言っている...Select(rm => rm.way)ので、これがうまくいく可能性は低いです。(そして、それが機能する場合、生成された SQL の複雑さと、このクエリが返すデータとエンティティの量のために、パフォーマンスはおかしくない可能性があります。)

さらに調査する必要がある最初のことは、.Include(r => r.relation_members.Select(rm => rm.way))正しいように見えるのになぜ機能しないのかということです。モデルとデータベースへのマッピングは正しいですか?

明示的な読み込みを介してノードを取得するループは、次のようになります。

foreach (relation_member rm in rel.relation_members) {
    context.Entry(rm).Reference(r => r.way).Query()
        .Include(w => w.way_nodes.Select(wn => wn.node))
        .Load();
}
于 2014-01-11T18:33:21.447 に答える
1

Include()ソート/グループ化/結合が関係している場合、何らかの理由で無視されることがあります。

ほとんどの場合、Include() をSelect()匿名の中間オブジェクトに書き換えることができます。

前:

context.Invoices
  .Include(invoice => invoice .Positions)
  .ToList();

後:

context.Invoices
  .Select(invoice  => new {invoice, invoice.Positions})
  .AsEnumerable()
  .Select(x => x.invoice)
  .ToList();

このようにして、クエリが Include() 情報を失うことはありません。

于 2015-09-25T08:10:56.540 に答える