2

IQueryableを公開する単純なリポジトリを使用して、Webフォーム用の新しいASP.NET4.5モデルバインディングをテストしていました。リポジトリはEF5、データベースファーストアプローチを使用しています。DTOを使用するためにEF自動生成エンティティを投影しています。

すべてが正常に機能し、それがポイントです。ある種の例外が発生することを期待していました...

これはコードです:

リポジトリ

    public IQueryable<JobDto> GetJobs()
    {
        var ctx = this.contextResolver.GetCurrentContext<pubsEntities>();

        return ctx.jobs.Select(x => new JobDto
            {
                Description = x.job_desc,
                ID = x.job_id,
                Maximum = x.max_lvl,
                Minimum = x.min_lvl
            });
    }

ご覧のとおり、EFエンティティをカスタムDTOに投影しており、プロパティはまったく異なります。

ASPXコードの背後にある

    public IQueryable<JobDto> gv_GetData()
    {
        return this.jobsRepository.GetJobs();
    }

ASPX

    <asp:GridView runat="server" ID="gv" AllowPaging="true" AllowSorting="true"
        DataKeyNames="ID"
        AutoGenerateColumns="true"
        SelectMethod="gv_GetData"
        ItemType="QueryRepository.JobDto, QueryRepository">
        <Columns>
            <asp:BoundField DataField="Description" HeaderText="My custom description" SortExpression="Description" />
        </Columns>
    </asp:GridView>

これは、すばらしいリポジトリを使用するときに、魅力、ページング、および箱から出してObjectDataSourceソートするように機能します(現在、ページングやソートなどを単純化するためにを使用する必要はありません)

私の質問は:

リポジトリが返さIQueryable<JobDto>れ、DTOのプロパティの名前がEFエンティティ(:という名前の別のエンティティjob)のプロパティと同じでない場合。

DTOエンティティで定義されたプロパティ名で構成されているので、 EFがGridView正しくソートできるのはどうしてですか?GridView私の知る限り、LINQを使用した動的ソートは、文字列を使用して順序基準を設定して行われます。どういうわけかLINQtoEntities IQueryable-DTOプロパティを、EFエンティティによって公開されているプロパティに自動マッピングしています。

誰かがこの貧しい魂を助けることができます=(舞台裏で何が起こっているのかを理解するために??

クエリがデータベースで正しく実行されていることを確認するためだけにSQLプロファイルを実行しました。

SELECT TOP (10) 
[Project1].[C1] AS [C1], 
[Project1].[job_desc] AS [job_desc], 
[Project1].[job_id] AS [job_id], 
[Project1].[max_lvl] AS [max_lvl], 
[Project1].[min_lvl] AS [min_lvl]
FROM ( SELECT [Project1].[job_id] AS [job_id], [Project1].[job_desc] AS [job_desc], [Project1].[min_lvl] AS [min_lvl], [Project1].[max_lvl] AS [max_lvl], [Project1].[C1] AS [C1], row_number() OVER (ORDER BY [Project1].[job_desc] DESC) AS [row_number]
    FROM ( SELECT 
        [Extent1].[job_id] AS [job_id], 
        [Extent1].[job_desc] AS [job_desc], 
        [Extent1].[min_lvl] AS [min_lvl], 
        [Extent1].[max_lvl] AS [max_lvl], 
        1 AS [C1]
        FROM [dbo].[jobs] AS [Extent1]
    )  AS [Project1]
)  AS [Project1]
WHERE [Project1].[row_number] > 0
ORDER BY [Project1].[job_desc] DESC

これらの行(ASPX)に特に注意してください。

<asp:BoundField DataField="Description" SortExpression="Description" />

そして結果のSQL

ORDER BY [Project1].[job_desc] DESC
4

3 に答える 3

5

このコードで:

return ctx.jobs.Select(x => new JobDto
    {
        Description = x.job_desc,
        ID = x.job_id,
        Maximum = x.max_lvl,
        Minimum = x.min_lvl
    });

Select呼び出しているオーバーロードは、EFによって発行される実際のSQLに影響を与える新しいものを実際に作成していますIQueryable。実際にはまだデータオブジェクトをDTOに投影していませんが、EFに、クエリの実行後にDTOに投影される結果セットのクエリを作成するために使用できる式を指定しています。Select呼び出されているオーバーロードでExpression<Func<Job, JobDto>>は、だけでなく、が渡されていることに注意してくださいFunc<Job, JobDto>。EFは式を分析するため、可能な場合はSQLへの高度な変換を行うことができます。

によってOrderBy式が追加されると、SQLに変換できる新しい式で式がGridView変更されるだけです。IQueryable

編集

Selectあなたがあなたの文脈の特性を呼び出すときIQueryable<Job> jobs、EFはあなたを見てExpression<Job, JobDto>決定することができます:

  1. JobDto投影から必要な列
  2. テーブルJobDtoからプロジェクションがどのように入力されるか。Job

BCLの多くのExpressionクラスを見ると、これをどのように実行できるかがわかります。コンパイラは、に遭遇するとx => new JobDto { Description = x.job_desc, ... }、次のような複雑な式ツリーを作成します(私はこれを大幅に単純化しています)。

LambdaExpression<Func<Job, JobDto>>
    MemberInitExpression
        NewExpression
        Bindings
            MemberAssignment
                Member = Description property
                Expression = MemberExpression representing access to the Job property
            MemberAssignment...
            MemberAssignment...
            MemberAssignment...
            etc.

このツリーに、EFが式をウォークスルーして内部マッピングを生成し、同等のSQLコマンドを生成するのに十分な情報がどのように含まれているかを確認できます。基本的に、.NET式をSQL式に投影しています。すべてが1:1のマッピングを持っているわけではありませんが、あなたの場合、マッピングがいかに簡単であるかがわかります。

Job type          -> Extent1 alias   -> dbo.jobs table
JobDto projection -> Project1 alias  -> subquery

他にも予測がありますが、気付くでしょう。行番号プロパティと、値を含む不思議なプロパティが導入されています1。それが何に使われているのかわかりません。

次に、が分析さOrderByれる追加の拡張です。Expression

于 2012-10-11T23:27:32.357 に答える
1

IQueryableはソートが適用されるまで何もクエリを実行していない可能性があるため、EFがクエリの生成方法を知っている可能性は十分にあると思います。これを実行すると、同じSQL出力が得られると思います。

var ctx = this.contextResolver.GetCurrentContext<pubsEntities>();

var jobs = ctx.jobs.Select(x => new JobDto
    {
        Description = x.job_desc,
        ID = x.job_id,
        Maximum = x.max_lvl,
        Minimum = x.min_lvl
    }).OrderBy(x => x.Description).ToArray();

編集:後世のために私の答えを維持しますが、ジェイコブのものははるかによく書かれています。

于 2012-10-11T23:26:35.903 に答える
1

この質問に対する答えは、IQueryable演算子がどのように機能するかにあります。LINQクエリは遅延モードで実行されるため、GetJobsメソッドが呼び出されても結果は取得されません。

ASP.NET GridViewでの並べ替えを有効にするには、プロパティSortExpressionを列に設定します。列をクリックしてデータを並べ替えると、動的ラムダ式が作成され、GridViewのSelectMethodから取得した結果に対してのOredrBy演算子が呼び出されます。ソートを適用すると、次のパターンのステートメントが呼び出されると言えます。

gv_GetData().OrderBy(j => j.Description);

演算子の実行は、GetJobs()メソッドによって返される型によって異なります。さらにレベルを下げると、GridViewに返されるデータは次の結果であると言えます。

this.jobsRepository.GetJobs().OrderBy(j => j.Description);

anothetレベルを下げると、評価される最終的なLINQクエリは次のようになります。

ctx.jobs.Select(x => new JobDto
        {
            Description = x.job_desc,
            ID = x.job_id,
            Maximum = x.max_lvl,
            Minimum = x.min_lvl
        }).OrderBy(j => j.Description);

SQLクエリは、すべての式を解析した後に形成されます。ここでは、解析する2つの式があります。1つはSelect演算子に渡され、もう1つはOrderBy演算子に渡されます。両方の式を解析した後に取得されたデータは、SQLクエリを形成するために使用されます。

SelectMethodとしてIEnumerableを返すメソッドを使用すると、例外が発生します。なぜなら、IEnumerable演算子は、動的に形成できないFuncを受け入れるからです。

于 2012-10-15T16:42:49.033 に答える