在LINQ to Entities

时间:2017-09-11 18:30:42

标签: c# entity-framework linq asp.net-mvc-5 linq-to-entities

我有一些复杂的业务定义,我想定义一次并在Linq to Entities中重用它们,并在其他基于它的表达式中使用它们。

我的尝试如下,这在我最初传递ConvertOrderDetailsToViewModel一个List时有效,但我想在IQueryable上执行此操作,这会导致错误:< / p>

LINQ to Entities does not recognize the method 'System.Decimal Invoke(OrderDetail)' method, and this method cannot be translated into a store expression.

有没有办法实现这一目标(没有第三方库)? 从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

我在UPDATE#1上错了。虽然这可以工作并编译并且似乎生成SQL,但这是不正确的。在后续表达式中使用时返回的this.QtyNeed值始终为0。 :(

1 个答案:

答案 0 :(得分:-1)

您是否考虑过以下事项?:

public static Func<OrderDetail, decimal> calcQtyNeedOD = (od) => (od.OrderedQuantity - od.PickedQuantity);

public List<AllocationNeedViewModel> ConvertOrderDetailsToViewModel(IQueryable<OrderDetail> qryShipOrderDtls, List<int> itemIdsToExclude, bool boolGroupViewModelByItemAndRemnant)
{
    return qryShipOrderDtls
        .ToArray()
        .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
            }).ToList();
}

原始查询试图对实际数据库执行Func(它不会理解你的表达式)。由于您没有过滤查询(没有where子句),因此返回整个集合,以便您可以使用本地Func进行投影。