0

私は C# / LINQ / ASP.NET / MVC 3 / EF を Java / Icefaces / Ibatis ベースから数か月間学んできました (実世界では .NET D を使用しています)。私は .NET Framework の LINQ / Entity Framework を本当に楽しんでいますが、舞台裏で実際に何が起こっているのかを理解するのにいくつか問題があります。

これが私の問題です:

私は AJAX / JSON フィードのjQuery データテーブルを使用しています(ちなみに、無料の Web データテーブル システムが必要な人には強くお勧めします)。私の MVC3 アプリケーションには、テーブルに必要なデータの JSON 結果を返し、並べ替えなどを行うメソッドがあります。すべてがうまくスムーズに機能しています。ただし、これを機能させるために行わなければならなかった「汚い」ハックに懸念があります。

完全なコードは次のとおりです。

//inEntities is the Entity Framework Database Context
//It includes the following entities:
//  Poincon
//  Horaire
//  HoraireDetail
//Poincon, Horaire and HoraireDetail are "decorated" using the Metadata technic which
//adds properties methods and such to the Entity (Like getEmploye which you will see in
//the following snippet)
//
//The Entity Employe is not a database data and therefor not handled by the EF.
//Instead, it is a simple object with properties that applies Lazy Loading to get an
//Employe Name based off of his Employe ID in the Active Directory. An employe object
//can be constructed with his Employe ID which will expose the possibility of getting
//the Employe Name from the AD if needed.

[HttpPost]
public JsonResult List(FormCollection form)
{
    String sEcho;
    int iDisplayStart;
    int iDisplayLength;
    String sSearch;
    int iSortingCols;
    Dictionary<String, String> sorting;

    try
    {
        sEcho = form["sEcho"];
        iDisplayStart = int.Parse(form["iDisplayStart"]);
        iDisplayLength = int.Parse(form["iDisplayLength"]);
        sSearch = form["sSearch"];
        iSortingCols = int.Parse(form["iSortingCols"]);

        sorting = new Dictionary<string,string>();
        for (int i = 0; i < iSortingCols; i++)
            sorting.Add(form["mDataProp_" + form["iSortCol_" + i]].ToUpper(), form["sSortDir_" + i].ToUpper());
    }
    catch
    {
        HttpContext.Response.StatusCode = 500;
        return null;
    }

    var qPoincon = inEntities.Poincons.AsEnumerable();
    var lPoincon = qPoincon.Select(o => new
    {
        o.id,
        emp = o.getEmploye(),
        o.poinconStart,
        o.poinconEnd,
        o.commentaire,
        o.codeExceptions
    }).AsEnumerable();

    //Search
    lPoincon = lPoincon.Where(p => (p.emp.empNoStr.Contains(sSearch) || p.emp.empNom.Contains(sSearch) || (p.commentaire != null && p.commentaire.Contains(sSearch))));

    //Keep count
    int iTotalDisplayRecords = lPoincon.Count();

    //Sorting
    foreach(KeyValuePair<String,String> col in sorting)
    {
        switch (col.Key)
        {
            case "EMPNO":
                if (col.Value == "ASC")
                    lPoincon = lPoincon.OrderBy(h => h.emp.empNo);
                else
                    lPoincon = lPoincon.OrderByDescending(h => h.emp.empNo);
                break;
            case "POINCONSTART":
                if (col.Value == "ASC")
                    lPoincon = lPoincon.OrderBy(h => h.poinconStart);
                else
                    lPoincon = lPoincon.OrderByDescending(h => h.poinconStart);
                break;
            case "POINCONEND":
                if (col.Value == "ASC")
                    lPoincon = lPoincon.OrderBy(h => h.poinconEnd);
                else
                    lPoincon = lPoincon.OrderByDescending(h => h.poinconEnd);
                break;
            case "COMMENTAIRE":
                if (col.Value == "ASC")
                    lPoincon = lPoincon.OrderBy(h => h.commentaire);
                else
                    lPoincon = lPoincon.OrderByDescending(h => h.commentaire);
                break;
        }
    }

    //Paging
    lPoincon = lPoincon.Skip(iDisplayStart).Take(iDisplayLength);

    //Building Response
    var jdt = new
    {
        iTotalDisplayRecords = iTotalDisplayRecords,
        iTotalRecords = inEntities.Poincons.Count(),
        sEcho = sEcho,
        aaData = lPoincon
    };
    return Json(jdt);
}

ご覧のとおり、EF から "Poincons" のリスト全体を取得し、それを Enumerable に変換しています。私の現在の理解では、LINQ クエリを Enumerable に変換すると、EF へのリンクが「強制終了」されます。つまり、LINQ データを最後まで保持して実行するのではなく、その時点でそのリストを取得するために必要な SQL が生成されます。必要なデータのみを返す正確なクエリ。この LINQ クエリを Enumerable に変換した後、LINQ を大幅にフィルタリングしています (データテーブルでページング、並べ替え、検索があるため)。これにより、私のコードが現在行っていることは、「データベースからすべての「Poincons」を取得し、それを Web サーバーのメモリに Enumerable として配置することであると考えるようになります。

私が正しければ、数千のエントリに達すると、パフォーマンスへの影響は非常に大きくなります (これは、本番環境では非常に速く発生します... 従業員が仕事に来るたびに、エントリが 1 つ追加されます。従業員 100 人、〜 300 人)年間労働日、あなたはアイデアを得る)。

このハッキングの理由は、EF が "Poincon" の "getEmploye" メソッドが何であるかを知らないため、実行時に次のような例外をスローするためです。

LINQ to Entities ne reconnaît pas la méthode « PortailNorclair.Models.Employe getEmploye() », et cette dernière ne peut pas être traduite en expression de magasin.

おおよその翻訳 (外国語でグローバリゼーションを維持しながら英語でエラーを表示するように IIS / ASP.NET を構成する方法をコメントで教えてくれる人がいれば、本当に感謝しています。エラー メッセージに関するフランス語の情報が不足している場合があります):

LINQ to Entity does not recognize the method " PortailNorclair.Models.Employe getEmploye()" and the following could not be translated to a SQL expression.

「getEmploye」メソッドはインスタンス化し、Poincon オブジェクトで見つかった従業員 ID を持つ Employee オブジェクトを返します。その Employee オブジェクトには、Active Directory からの従業員名などの情報を「遅延読み込み」するプロパティがあります。

質問は次のとおりです。フィルター処理されていないオブジェクトのリストで .AsEnumerable() を使用することによるパフォーマンスの低下をどのように回避できますか?

どうもありがとう!

4

2 に答える 2

1

「getEmploye」メソッドはインスタンス化し、Poincon オブジェクトで見つかった従業員 ID を持つ Employee オブジェクトを返します。その Employee オブジェクトには、Active Directory からの従業員名などの情報を「遅延読み込み」するプロパティがあります。

従業員名をデータベースに保存する必要があるため、すべての従業員オブジェクトをロードすることなく、Linq クエリを並べ替え、並べ替え、スキップ、および取り込むことができます。

empNoStr、empNom、および empNo がすべてデータベースにある場合、必要なレコードだけを取得し、getEmploye() を呼び出すことができます (Active Directory などから必要なものをロードします)。

于 2012-07-12T12:45:20.193 に答える
0

プログラムが主な作業を実行するクラスがいくつかあります。

データベース行を表す他のクラスがあります。

それらを分離しておくと、データベースで実行する予定のアクションと、ローカルで実行する予定のアクションを分離することもできます。これにより、特定の行が必要な場合にテーブル全体をロードするのを避けるのは簡単になります。

また、ローカルでページングを行っていることがわかりますが、データベースはそれを行うことができ、Web サーバーのメモリを節約できます。

于 2012-07-12T12:05:54.183 に答える