我有一个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”会使分页变得混乱,但是,如果您认为这是最好的方法,我也可以遵循这条路径。
道歉这是一个有点长的啰嗦,我想要包含的信息非常多,以便为您提供最全面的信息。
谢谢,
标记
答案 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,它引导了我一些新的调查线,帮助我最终解决了这个问题!
此致
标记