0

几个月来我一直在学习 C#/LINQ/ASP.NET/MVC 3/EF,现在来自 Java/Icefaces/Ibatis 基础(现实世界使用 .NET D;)。我真的很喜欢 .NET Framework 中的 LINQ / Entity Framework,但我在了解幕后真正发生的事情时遇到了一些问题。

这是我的问题:

我正在使用 AJAX / JSON 馈送的jQuery数据表(我强烈推荐给任何需要免费网络数据表系统的人)。我的 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 的链接,或者换句话说,将生成获取该列表所需的 SQL,而不是将 LINQ 数据保留到最后并执行只返回您需要的数据的精确查询。将此 LINQ 查询转换为 Enumerable 后,我对 LINQ 进行了大量过滤(因为在数据表中存在分页、排序、搜索)。这让我想到我的代码目前正在做的是“从数据库中获取所有“Poincons”并将其作为 Enumerable 放入 Web 服务器的内存中,

如果我是对的,那么当您点击几千个条目时,性能受到的影响非常大(一旦在生产中发生这种情况会非常快......每次员工上班时,它都会添加 1 个条目。100 名员工,~300一年的工作日,你懂的)。

这种 hack 的原因是 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 的 Employe 对象。该 Employe 对象具有“延迟加载”信息的属性,例如来自 Active Directory 的员工姓名。

所以问题是:如何避免在未过滤的对象列表上使用 .AsEnumerable() 对性能造成影响?

非常感谢!

4

2 回答 2

1

“getEmploye”方法实例化并返回具有在 Poincon 对象中找到的员工 ID 的 Employe 对象。该 Employe 对象具有“延迟加载”信息的属性,例如来自 Active Directory 的员工姓名。

您应该将员工姓名存储在数据库中,这样您就可以对 Linq 查询进行排序、排序、跳过和接收,而无需加载每个员工对象。

如果 empNoStr、empNom 和 empNo 都在数据库中,您可以只检索您想要的记录,并为每个记录调用 getEmploye()(从活动目录或任何地方加载您需要的任何其他内容)。

于 2012-07-12T12:45:20.193 回答
0

您的程序在某些类上执行其主要工作。

还有其他代表数据库行的类。

如果您将它们分开,您还可以将您打算在数据库中发生的操作与您打算在本地执行的操作分开。这使得在需要特定行时避免加载整个表变得微不足道。

我看到您也在本地进行分页,而数据库可以这样做并为您的网络服务器节省一些内存。

于 2012-07-12T12:05:54.183 回答