C#EF / LINQ hack修复打击性能?其他固定方式?

时间:2012-07-12 11:53:58

标签: c# json asp.net-mvc-3 linq entity-framework

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

这是我的问题:

我正在使用AJAX / JSON fed jQuery datatable(我强烈建议任何需要免费网络数据表系统的人)。我在我的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数据保留到最后并执行percise查询,只返回您需要的数据。在将此LINQ查询转换为Enumerable之后,我对LINQ进行了大量过滤(因为在数据表中存在分页,排序和搜索)。这让我想到我的代码目前正在做的是“从数据库中抓取所有”Poincons“并将其作为Enumerable放入Web服务器的内存中,使用Enumerable进行工作,然后将结果序列化为JSON字符串,发送给客户。

如果我是正确的,那么当您点击几千个条目时,性能就会非常沉重(一旦投入生产就会很快发生......每当一个员工上班时,它将增加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”方法实例并返回一个Employe对象,该对象具有在Poincon对象中找到的employees id。 Employe对象具有“延迟加载”信息的属性,例如来自Active Directory的雇员名称。

所以问题是:如何在未经过滤的对象列表中使用.AsEnumerable()来避免性能损失?

非常感谢!

2 个答案:

答案 0 :(得分:1)

  

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

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

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

答案 1 :(得分:0)

您的计划有一些课程可以完成其主要工作。

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

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

我看到你也在本地进行Paging,而数据库可以做到这一点,并为你的web服务器节省一些内存。