【发布时间】:2018-02-20 01:54:39
【问题描述】:
我有一些复杂的业务定义,我想定义一次并在 Linq to Entities 中重用它们,并在基于它的其他表达式中使用它们。
我的尝试如下,这在我最初传递ConvertOrderDetailsToViewModel 和List 时有效,但我想在IQueryable 上执行此操作,这会导致错误:
LINQ to Entities does not recognize the method 'System.Decimal Invoke(OrderDetail)' method, and this method cannot be translated into a store expression.
有没有办法在本地实现这一点(没有 3rd 方库)? 从this answer 看来,您可以在数据库本身中定义这些函数,然后从 C# 调用它们,但同样,如果可能的话,希望仅在 C# 代码中执行此操作。
还遇到了this answer,它看起来适用于Where 表达式,但是当我尝试为我的select 表达式实现它时,我得到了这个错误,因为我正在尝试将表达式分配给小数。
Cannot implicitly convert type Expression<Func<OrderDetail, decimal>> to 'decimal'
这是表达式函数:
public static Expression<Func<OrderDetail, decimal>> calcQtyNeedOD = (od) => (od.OrderedQuantity - od.PickedQuantity);
调用该函数的视图模型创建方法:
public List<AllocationNeedViewModel> ConvertOrderDetailsToViewModel(IQueryable<OrderDetail> qryShipOrderDtls, List<int> itemIdsToExclude, bool boolGroupViewModelByItemAndRemnant)
{
var qryAllocNeedVMTEST =
from od in qryShipOrderDtls
select new AllocationNeedViewModel()
{
WorkReleaseHeaderId = od.OrderHeader.WorkReleaseHeaderId.Value,
OrderHeaderId = od.OrderHeaderId,
OrderDetailId = od.Id,
ItemId = od.ItemId.Value,
ItemDescription = od.Item.Description,
IsInventoryTracked = od.Item.TrackInventory,
QtyNeed = calcQtyNeedOD(od),
QtyRemain = 0,
QtyAllocated = 0,
IsAllocated = false
}
;
return qryAllocNeedVMTEST.ToList();
}
为了使这更复杂,还有其他属性我也希望有一个可重用的表达式,它也将使用第一个表达式...即
public static readonly Expression<Func<OrderDetail, decimal>> calcQtyRemainOD =
(od) => calcQtyNeedOD.Compile().Invoke(od) - calcQtyAllocatedOD.Compile().Invoke(od);
更新 #1
查看更新 #2...此解决方案不起作用!
虽然到目前为止还没有人能够提供一种本地方式来重用选择表达式跨查询,但我确实找到了一个部分解决方案,可以在内重用它们em> 相同的查询。一旦将表达式投影/分配给实体的属性,您就可以(与 T-SQL 不同)将该属性指定为另一个后续属性表达式的一部分。
示例 - 这显示了每个属性投影的完整表达式。在此示例中,QtyRemain 本质上只是QtyNeed - QtyAllocated。我在QtyRemain 分配中再次指定了这些:
public List<AllocationNeedViewModel> ConvertOrderDetailsToViewModel(IQueryable<OrderDetail> qryShipOrderDtls, List<int> itemIdsToExclude, bool boolGroupViewModelByItemAndRemnant)
{
var qryAllocNeedVM = qryShipOrderDtls
.Select(od => new AllocationNeedViewModel() //Get all Work Order Detail Needs for Work Release
{
QtyNeed = (od.OrderedQuantity - od.PickedQuantity),
QtyAllocated = (od.AllocatedInventories.Count == 0 ? 0 : od.AllocatedInventories.Where(ai => ai.StatusId < _AllocatedInventoryProcessedStatus).Sum(ai => ai.AllocatedQty)),
QtyRemain = (od.OrderedQuantity - od.PickedQuantity) - (od.AllocatedInventories.Count == 0 ? 0 : od.AllocatedInventories.Where(ai => ai.StatusId < _AllocatedInventoryProcessedStatus).Sum(ai => ai.AllocatedQty))
}
);
return qryAllocNeedVM.ToList();
}
相反,您可以简单地使用 QtyRemain 属性分配中已定义的属性,如下所示:
public List<AllocationNeedViewModel> ConvertOrderDetailsToViewModel(IQueryable<OrderDetail> qryShipOrderDtls, List<int> itemIdsToExclude, bool boolGroupViewModelByItemAndRemnant)
{
var qryAllocNeedVM = qryShipOrderDtls
.Select(od => new AllocationNeedViewModel() //Get all Work Order Detail Needs for Work Release
{
QtyNeed = (od.OrderedQuantity - od.PickedQuantity),
QtyAllocated = (od.AllocatedInventories.Count == 0 ? 0 : od.AllocatedInventories.Where(ai => ai.StatusId < _AllocatedInventoryProcessedStatus).Sum(ai => ai.AllocatedQty)),
QtyRemain = this.QtyNeed - this.QtyAllocated
}
);
return qryAllocNeedVM.ToList();
}
虽然这不是我最初问题的完整解决方案,但它是一个部分解决方案,可以为您带来一些所需的好处。
更新 #2
我在更新 #1 上错了。虽然这可以工作和编译并且似乎生成 SQL,但它是不正确的。 this.QtyNeed 的返回值在后面的表达式中使用时总是返回0。 :(
【问题讨论】:
-
看看LINQKit
-
感谢@IvanStoev,但如果可能的话,现在尽量不要使用第三方库。似乎很多这些最终在短时间内被放弃,然后当您将项目升级到下一个 .net 框架时不起作用,然后您不得不修改一堆代码。
-
@ChadRichardson 如果您愿意,您可以编写代码自己做,但您需要做或多或少与该代码库相同的事情。你当然也可以找到其他人的实现。
-
@Servy,明白了。我只是希望在 2017 年有一种本地方式来做到这一点(事情每年都在变化)。如果答案是否定的……那我肯定会看看第三方库路线……或者创建一个数据库视图。
-
另一种选择是使用 UDF 并从您的查询中调用它。这至少可以避免将所有内容都写入视图。
标签: c# entity-framework linq asp.net-mvc-5 linq-to-entities