几个月来我一直在学习 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() 对性能造成影响?
非常感谢!