属性和转换为IQueryable上的sql,其中linq为实体

时间:2017-10-04 08:15:30

标签: c# sql-server entity-framework linq iqueryable

鉴于两个类首先使用EF生成代码,并使用父子关系:

class Parent {
    //...
    public virtual ICollection<Child> Children
}

class Child {
   //...
   public decimal Amount{ get; set; }
   public decimal UnitPrice { get; set; }
}

我想在Parent上创建一个属性Total,类似于

decimal Total => Children.Sum(child => child.UnitPrice * child.Amount)

但是,如果我这样做,然后做

var list = ctx.Parents
                  .Where(p => p.Total > 1000)
                  .Select(p => new {
                       p.Id, 
                       p.Total,
                       Count = p.Children.Count });
foreach(var item in list){
      Console.WriteLine($"Id: {item.} ...");
}

我收到了错误

  

EntityFramework.SqlServer.dll中出现未处理的“System.NotSupportedException”类型异常

     

附加信息:LINQ to Entities不支持指定的类型成员“Total”。仅支持初始化程序,实体成员和实体导航属性。

虽然,在我看来,EF使用SUM(UnitPrice * Amount) as Total生成查询并不难,但我无法让它发挥作用。

我已经尝试过像

这样的静态表达式
  public static Expression<Func<Parent, decimal>> GetTotal()
  {
      return p=> p.Children.Sum(line => line.ItemQty * line.UnitPrice);
  }

虽然只在代码中进行此计算是可以接受的。我想使用此示例来了解有关如何使用IQueryable的更多信息。

A Little Succes

在Parent

上给出以下静态属性
  public static Expression<Func<Parent, decimal?>> Total =>
        p=> (from c in p.Childeren
             select c.UnitPrice * c.ItemQty).Sum();

然后做

var list = ctx.Parents.Select(Parent.Total);

我有一个包含所有总计的列表,我看到EF生成了以下查询

SELECT
    (SELECT
        SUM([Filter1].[A1]) AS [A1]
        FROM ( SELECT
            [Extent2].[UnitPrice] *  CAST( [Extent2].[Amount] AS decimal(19,0)) AS [A1]
            FROM [dbo].[Child] AS [Extent2]
            WHERE [Extent1].[Id] = [Extent2].[ParentId]
        )  AS [Filter1]) AS [C1]
    FROM [dbo].[Parent] AS [Extent1]

因此,它确实能够将Sum()方法转换为SQL。现在我只需要在Where()中使用它。

1 个答案:

答案 0 :(得分:1)

计算属性仅在类中已知,而不在数据库中。在访问这些计算出的属性之前,您需要实例化对象(到类中)。

要让它发挥作用并不难:

var list = ctx.Parents
              .Where(p => p.Total > 1000)
              .ToList() //This instantiates your data into objects
              .Select(p => new {
                   p.Id, 
                   p.Total,
                   Count = p.Children.Count });

这可能需要一个包含声明,具体取决于您是否有延迟加载:

var list = ctx.Parents
              .Include(p => p.Children) //Children will be populated upon instantiation
              .Where(p => p.Total > 1000)
              .ToList() //This instantiates your data into objects
              .Select(p => new {
                   p.Id, 
                   p.Total,
                   Count = p.Children.Count });
  

虽然,在我看来,EF使用SUM(UnitPrice * Amount) as Total生成查询并不难,但我无法让它发挥作用。

还有比你提及的更多的东西。

什么的SUM?不是当前结果(Parents表),您需要在Children表上完成计算的总和(按ParentId分组),这是一个完全不同于你目前使用的数据集。

这就是为什么EF不会做你想做的事情。它试图决定查询的SELECT部分,但您的期望需要更复杂的查询体;将孩子和父母聚集在一起以便对孩子进行计算,然后将这些结果归为一个值的方法。

如果您尝试自己编写SQL查询,那么您会发现 比仅仅SUM(UnitPrice * Amount) as Total更复杂。