无法将计算的值添加到IQueryable

时间:2018-11-27 08:58:30

标签: c# entity-framework linq iqueryable

我正在运行EF语句,需要计算自付额。经过长时间的尝试,我似乎无法在.Select()语句中添加自定义函数。相反,我尝试在.Select()语句之后添加值。

这里的问题是,在我的CalculateDeductibles()中,我似乎无法向item.Deductibles添加任何值。

GetDeductibles(item.RequestId)是一个相当繁重的功能,它会执行几个额外的查询,因此我试图防止将我的IQueryable转换为IList对象。

所以实际上有两个问题:

  1. 我可以在我的GetDeductibles()语句中直接使用.Select()函数吗?
  2. 在完成.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);
    }
}

2 个答案:

答案 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)

您必须了解IEnumerableIQueryable之间的区别。

IEnumerable对象包含所有要枚举的对象,该对象按此对象表示的顺序枚举。您可以要求第一个元素,一旦找到它,就可以重复要求下一个元素,直到没有下一个元素为止。

IQueryable的工作方式有所不同。 IQueryable包含一个Expression和一个ProviderExpression是应选择哪些数据的一般描述。 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或调用存储过程来绕过此机制。