Linq,通过IOrderedEnumerable命令IQueryable

时间:2012-01-21 17:55:56

标签: linq entity-framework telerik telerik-grid

我有一个MVC telerik网格,我已经设置了自定义绑定到IQueryable以便于计算列的排序。网格在此属性上排序时的默认排序行为是:

data = data.OrderBy(product => product.oneMthCost_IntOnly);

“data”是IQueryable,product.oneMthCost_IntOnly不是从数据库返回的,它是一个计算属性,在“SearchViewModel”上为此属性调用“get”访问器时计算:

public class SearchViewModel
{
    public int ID  { get; set; }
    public string lend_name  { get; set; }
    public decimal? pDes_rate  { get; set; }
    public string pDes_details  { get; set; }
    public int? pDes_totTerm  { get; set; }
    public decimal? pDes_APR  { get; set; }
    public string pDes_revDesc  { get; set; }
    public string pMax_desc  { get; set; }
    public DateTime? dDipNeeded { get; set; }
    public DateTime? dAppNeeded { get; set; }

    public decimal oneMthCost_IntOnly
    {
        get { return ProductOperations.CalculateSingleYearInterestCost(SourcingModel.search.LoanAmt, (decimal)pDes_rate); }
    }
}

为了解释如何返回SearchViewModel(“data”),它基于实体数据模型,该模型使用以下延迟Linq查询作为网格投射到SearchViewModel的基础。

    //Return the required products
    var model = from p in Product.Products
                where p.archive == false && ((Prod_ID == 0) || (p.ID == Prod_ID))
                select new SearchViewModel
                    {
                        ID = p.ID,
                        lend_name = p.Lender.lend_name,
                        pDes_rate = p.pDes_rate,
                        pDes_details = p.pDes_details,
                        pDes_totTerm = p.pDes_totTerm,
                        pDes_APR = p.pDes_APR,
                        pDes_revDesc = p.pDes_revDesc,
                        pMax_desc = p.pMax_desc,
                        dDipNeeded = p.dDipNeeded,
                        dAppNeeded = p.dAppNeeded
                    };

因此,使用网格默认行为,如下所示:

data = data.OrderBy(product => product.CalculatedProp);

在此列排序时抛出此错误:

  

不支持指定的类型成员'oneMthCost_IntOnly'   LINQ to Entities。仅初始化程序,实体成员和实体   支持导航属性。

嗯,这确实有意义,表达式树在使用'get'访问器获取它之前不知道该值是什么。所以,我完全放弃了这样一个事实:我将需要实现整个对象集,对每一行进行计算,然后对其进行排序IQueryable(遗憾的是业务逻辑太复杂,表达式树无法转动计算到SQL,所以需要一个C#方法)。所以我这样做:

var calcdata = data.ToList().OrderBy(p => p.oneMthCost_IntOnly);

它实现了所有数据,进行了所有计算,并将其分类到IOrderedEnumerable计算数据中......这是擦除:

如何将“calcdata”加入“data”以通过IOrderedEnumerable“calcdata”键对IQueryable数据进行排序?将网格重新绑定到“calcdata”会使分页变得混乱,但是,如果您认为这是最好的方法,我也可以遵循这条路径。

道歉这是一个有点长的啰嗦,我想要包含的信息非常多,以便为您提供最全面的信息。

谢谢,

标记

2 个答案:

答案 0 :(得分:1)

好的 - 这是一个非常高的水平,并简要回答了你的完整问题。

我们在订购非常复杂的数据集方面遇到了问题。像你一样,我们试图实现它们,而且性能非常糟糕。我们最终做的是在数据库中创建一个View,其中包含我们需要排序的列。我们在实体模型中包含了这个视图,然后它就像一个表(一些警告说实话)。

一旦我们在EF模型中获得了View,我们就可以在不实现的情况下使用order。您的视图还可以包含您需要的连接。

这种方法在很大程度上取决于你对数据库的控制,我不是每个开发人员都能理解。我们发现这种通用方法非常成功,如果你能够以这种方式构造View,就可以获得它们的主键。

如果您无法将View放入数据库,那么您可以使用QueryViews之类的实体框架结构来达到类似的效果。然而,它们有自己的约束 - 例如无法再自动更新模型,可以将除QueryView之外的任何内容加入到它们中。我相信它会起作用,但你需要弄清楚并直接修改EF模型xml。

观点注意事项

我们不得不做一些摆弄将存储过程映射到视图。因为它是只读的我们真的不想打扰插入,但是当删除依赖实体时,我从视图中删除了删除。 TBH - 我们此时攻击它并包括一个影响一行和这项工作的虚拟过程。必须有一个比这更好的答案。

好的 - 当我开始写作时,不如我想象的那么简短。

修改

如果您想使用join关键字加入两个集合。一个来自数据库,另一个来自c#,那么他们都必须要IEnumerables - 即通过ToList()实现的EF查询和适当集合中的c#对象,即通用List。

然后语法可能是

var joinedCollections = from efItem in efCollection
                     join charpItem in charpCollection on csharpItem.CommonId equals efItem.CommonID
                     select select new {csharp.field1, efItem.field1};

当然这假设两个集合之间有一个共同的密钥。效率可能也很糟糕。

答案 1 :(得分:1)

我在下面是正确的轨道:

calcdata = data.ToList().OrderByDescending(p => p.oneMthCost_IntOnly);

当以这种方式排序时,这会对数据的所有行进行实质化(我知道,这不是非常高效,但它是能够对数据库不知道的数据进行此排序的唯一方法)。我遇到的麻烦是上面创建一个IOrderedEnumerable,在分页时,如下所示:

        //Then paging
        if (command.PageSize > 0)
        {
            calcdata = calcdata.Skip((command.Page - 1) * command.PageSize);
        }

        calcdata = calcdata.Take(command.PageSize);

这会向后发出一个IEnumerable(我假设它会发出一个IOrderedEnumerable),所以试图回转到这种类型然后打破了分页。以上工作原理,calcdata是IEnumerable,而不是IOrderedEnumerable。 IQueryable不需要连接(为什么在需要所有信息时返回数据库?),所以我们重新绑定到IEnumerable。

感谢你输入Crab Bucket,它引导了我一些新的调查线,帮助我最终解决了这个问题!

此致

标记