我正在运行EF语句,需要计算自付额。经过长时间的尝试,我似乎无法在.Select()
语句中添加自定义函数。相反,我尝试在.Select()
语句之后添加值。
这里的问题是,在我的CalculateDeductibles()
中,我似乎无法向item.Deductibles
添加任何值。
GetDeductibles(item.RequestId)
是一个相当繁重的功能,它会执行几个额外的查询,因此我试图防止将我的IQueryable
转换为IList
对象。
所以实际上有两个问题:
GetDeductibles()
语句中直接使用.Select()
函数吗?.Select()
之后,我可以以某种方式(关注性能)增加价值吗?
代码:
public IQueryable<ReinsuranceSlip> GetReinsuranceSlipsOverview(int userId, int companyId, string owner, string ownerCompany)
{
IQueryable<ReinsuranceSlip> model = null;
model = _context.Request
.Where(w => w.RequestGroup.ProgramData.MCContactId == userId)
.Select(x => new ReinsuranceSlip()
{
Id = x.Id,
RequestId = x.Id,
LocalPolicyNumber = x.LocalPolicyNumber,
BusinessLine = x.RequestGroup.ProgramData.BusinessLine.DisplayName,
BusinessLineId = x.RequestGroup.ProgramData.BusinessLine.Id,
ParentBroker = x.RequestGroup.ProgramData.Broker.Name,
LocalBroker = x.Broker.Name,
InceptionDate = x.InceptionDate,
RenewDate = x.RenewDate,
//Deductibles = CalculateDeductibles(x)
});
CalculateDeductibles(model);
return model;
}
private void CalculateDeductibles(IQueryable<ReinsuranceSlip> model)
{
//model.ForEach(m => m.Deductibles = GetDeductibles(m.RequestId));
foreach (var item in model)
{
item.Deductibles = GetDeductibles(item.RequestId);
}
}
答案 0 :(得分:1)
已更新和很抱歉,此答案的第一个版本。我不太明白。
答案1:IQueryable用来创建一个完整的SQL语句以在SQL Server中调用。因此,如果您想使用IQueryable,则您的方法需要生成语句并将其返回。您的GetDetuctibles方法获取请求ID参数,但您的可查询模型对象尚未从数据库收集任何数据,并且它不知道x.Id值。更何况,您的GetCarearDetuctiples获得了一个参数,因此使用该参数会生成一个可查询的对象,并在进行一些计算后返回十进制。我的意思是是的,您可以在select语句中使用您的方法,但这确实很复杂。您可以使用AsExpendable()LINQ方法并重写您的方法的返回类型Expression或Iqueryable。
有关详细信息,请检查。
Entity Navigation Property IQueryable cannot be translated into a store expression
以及http://www.albahari.com/nutshell/predicatebuilder.aspx
您还应该查看本文以了解IQueryable接口:https://samueleresca.net/2015/03/the-difference-between-iqueryable-and-ienumerable/
答案2:您可以使用IEnumerable接口代替IQueryable接口来实现此目的。在这种情况下将很容易使用。您可以按时进行性能测试和改进方法。
但是,如果我是您,我会考虑使用存储过程来提高性能。
答案 1 :(得分:0)
您必须了解IEnumerable
和IQueryable
之间的区别。
IEnumerable
对象包含所有要枚举的对象,该对象按此对象表示的顺序枚举。您可以要求第一个元素,一旦找到它,就可以重复要求下一个元素,直到没有下一个元素为止。
IQueryable
的工作方式有所不同。 IQueryable
包含一个Expression
和一个Provider
。 Expression
是应选择哪些数据的一般描述。 Provider
知道谁必须执行查询(通常是数据库),并且知道如何将Expression
转换成Provider
可以理解的格式。
LINQ函数有两种类型:返回IQueryable<TResult>
的函数和返回TResult
的函数。第一种类型的函数不执行查询,它们只会更改表达式。他们使用延迟执行。第二组的函数将执行查询。
当必须执行查询时,Provider
采用Expression
并尝试将其转换为执行查询的进程所理解的格式。如果此过程是关系数据库管理系统,则通常为SQL。
此翻译是您无法添加自己的功能的原因:Expression
必须可翻译为SQL,并且您的函数唯一能做的就是调用将更改{{1} }转换为可以转换为SQL的内容。
实际上,即使实体框架也不支持所有LINQ功能。有一个list of Supported and Unsupported LINQ methods
我可以直接在查询中获得GetDeductibles吗?
不,您不能,除非您可以如此简单地使其仅使用受支持的LINQ方法来更改Expression
。您必须以扩展功能的格式编写它。参见extension methods demystified
您的GetDeductibles应该以{{1}}作为输入,并返回Expression
作为输出:
IQueryable<TSource>
如果确实需要调用其他本地函数,请考虑在调用本地函数之前先调用IQueryable<TResult>
。 static class QueryableExtensions
{
public static IQueryable<TResult> ToDeductibles<TSource, TResult, ...>(
this IQueryable<TSource> source,
... other input parameters, keySelectors, resultSelectors, etc)
{
IQueryable<TResult> result = source... // use only supported LINQ methods
return result;
}
}
上方的优势在于,像实体框架中的智能AsEnumerable
提供程序一样,它不会提取所有项,而是会提取每页 项。因此,如果只需要几个数据,就不会将所有数据都传输到本地进程。在调用AsEnumerable之前,请确保丢弃所有不再需要的数据,从而限制了传输的数据量。
在执行完.Select()
之后,我能以某种方式添加值吗?LINQ旨在查询数据,而不是对其进行更改。在更改数据之前,必须先将其具体化后再进行更改。如果是数据库查询,这意味着您拥有归档数据的副本,而不是原始副本。因此,如果进行更改,则将更改副本,而不是原始副本。
使用实体框架时,您必须获取要更新/删除的每个项目。确保您没有选择值,而是选择原始项目。
否:
ToList
但是:
IQueryable
现在,您的DbContext的ChangeTracker中具有原始的School。如果您更改SchoolToUpdate,然后调用SaveChanges,则会将您的SchoolToUpdate与原始School进行比较,以检查是否必须更新School。
如果需要,可以通过将新学校直接附加到ChangeTracker或调用存储过程来绕过此机制。