c# - 在 LINQ to Entities 中重用选择表达式

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

我有一些复杂的业务定义,我想定义一次并在 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.

有没有办法在本地实现这一点(没有第 3 方库)? 看起来像来自 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); 

调用该函数的 View 模型创建方法:

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 . :(

最佳答案

您是否考虑过以下问题?

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 进行投影。

关于c# - 在 LINQ to Entities 中重用选择表达式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46162210/

相关文章:

c# - 接口(interface)从 vb.net 到 c# 的转换

c# - Authorize 和 Request.IsAuthenticated 之间的区别

entity-framework - Entity Framework 和连接字符串

c# - 将 Expression<Func> 选择器绑定(bind)到 linq 查询

c# - 根据不同的值遍历列表的有效方法是什么?

c# - WCF REST 和 QueryString,错误的 UriTemplate?

c# - WPF Gridview 复选框列标题 MVVM

c# - 尝试使用通用存储库更新 EF 中的类时出现 `Attaching an entity of type failed...` 异常

sql-server - Entity Framework 中的存储过程与 ADO.NET

c# - 使用LINQ恰好替换C#中可枚举的一个元素