.NET动态LINQ OrderBy连接和嵌套对象

时间:2014-11-25 00:41:08

标签: .net linq entity-framework c#-4.0 dynamic

我有这样的功能 -

public void ReadTable(string sortField, string sortOrder, string RegID)
{
    sortOrder = sortOrder == "desc" ? "descending" : "";

    var db = new MyEntities();


    var results = (from CA in db.tblCalifornia
                       join BN in db.tblBeach on CA.BeachID equals BN.BeachID into ps
                       from BN in ps.DefaultIfEmpty()
                   where CA.RegID == regID && CA.tblLife.duration != null
                   orderby sortField + " " + sortOrder
                   select new { CA, BN }).ToList();
 }

我想使用tblCalifornia中名为“city”的字段或tblLife中的“duration”字段进行动态排序。

我将该函数称为ReadTable(“city”,“asc”,“120”)或ReadTable(“CA.city”,“asc”,“120”)。这两种方法都不行,虽然编译没问题,但不会抛出运行时异常。

如何让它发挥作用?

谢谢,

1 个答案:

答案 0 :(得分:1)

你正在混合使用动态sql的Linq-To-Entities - 它不会起作用。运行时正在从linq查询构建sql查询,并且它不了解如何将orderby子句中的原始字符串转换为动态SQL。

这会让你走近:

var q = (from CA in db.tblCalifornia
           join BN in db.tblBeach on CA.BeachID equals BN.BeachhID into ps
           from BN in ps.DefaultIfEmpty()
           where CA.RegID == regID && CA.tblLife.duration != null
           select new { CA, BN }
        );
if (order == "desc") {
     q = q.OrderByDescending(m => m.CA.city);
}
else {
     q = q.OrderBy(m => m.CA.city); 
}
var results = q.ToList();

OrderBy和OrderByDescending是Linq-To-Entities兼容的方法,因此SQL生成的顺序可以正常工作。注意;但是,我明确地将它们使用的表达式设置为CA.city。 不幸的是,我不认为只用EF就可以动态设置你想要的种类。

如果你真的想这样做 - 请查看LinqKit http://www.albahari.com/nutshell/linqkit.aspx

它的" AsExpandable()"方法将允许您将表达式传入您的方法,然后您可以编译并插入到您的linq查询中。

所以你的新方法最终将被调用:

ReadTable("asc", m => m.CA.City, "120");

您必须为CA和BN创建一个显式的包含类,然后使用表达式,如下所示

public class Container {
    public tblCalifornia CA { get; set; }
    public tblBeach BN { get; set; }
}
public void ReadTable(string sortOrder, Expression<Func<Container,string>> fnSortExpr, string regID) {

    var sort = fnSortExpr.Compile();
    var q = (from CA in db.tblCalifornia.AsExpandable() 
           join BN in db.tblBeach on CA.BeachID equals BN.BeachhID into ps
           from BN in ps.DefaultIfEmpty()
           where CA.RegID == regID && CA.tblLife.duration != null
           select new Container { BN = BN, CA = CA }
        );
    if (order == "desc") {
       q = q.OrderByDescending(sort);
    }
    else {
       q = q.OrderBy(sort); 
    }
    var results = q.ToList();
    ...
}

AsExpandable()创建一个围绕IQueryable的包装器,它允许运行时将表达式转换为LINQ-To-Entities可以理解的内容。首先必须在表达式上调用.Compile(),然后在新的可扩展IQueryable中使用它。我假设您的sortBy字段是来自浏览器的字符串,在这种情况下,您必须在调用此方法之前将支持的字符串映射到表达式。

我还没有测试过上面的代码,但希望它能让你走上正轨。你要做的事情绝不是一件容易的事,所以要对自己保持耐心。

祝你好运。