儿童收藏中的总和

时间:2017-01-16 13:56:37

标签: mysql entity-framework-6

当我对空子集合中的值求和时,将MySQL与EF6一起使用会抛出异常,因为bug #80127中的MySQL不支持DefaultIfEmpty。

public class Foo
{
  public int Id { get; set; }
  public decimal Total { get; set; }
  public virtual IList<Bar> Bars { get; set; }
}

public class Bar
{
  public int Id { get; set; }
  public int FooId { get; set; }
  public virtual Foo Foo { get; set; }
  public decimal Received { get; set; }
}

使用DefaultIfEmpty的推荐方法会抛出一个带有无效的异常,其中包含了clausule&#39; Project1&#39;。&#39; Id&#39;。这是MySQL的一个老bug。

var result = db.Foo.Select(f => new {
  Total = f.Total,
  Received = f.Bars.Select(b => b.Received).DefaultIfEmpty().Sum()
});

我使用内联如果工作正常但会生成一个非常难看的SQL,其中包含大量内部查询和重复的select语句。

var result = db.Foo.Select(f => new {
  Total = f.Total,
  Received = f.Bars.Any() ? f.Bars.Sum(b => b.Received) : 0
});

有没有更好的方法来避免使用DefaultIfEmpty?

1 个答案:

答案 0 :(得分:2)

我通常喜欢的DefaultIfEmpty的替代方法是使用强制转换操作符将非可空类型提升为可空,这对MySQL连接器起作用(甚至)。

然后解决方案取决于您的接收器类属性类型。

最好的情况是,如果您可以收到可以为空的结果,在这种情况下查询很简单:

var result = db.Foo.Select(f => new {
  Total = f.Total,
  Received = f.Bars.Sum(b => (decimal?)b.Received)
});

如果它必须是非可空类型,则可以使用空合并运算符

var result = db.Foo.Select(f => new {
  Total = f.Total,
  Received = f.Bars.Sum(b => (decimal?)b.Received) ?? 0
});

但是生成的SQL查询很丑陋且效率低下。

在这种情况下你能做的最好的事情就是使用(一个非常烦人的)双重选择技巧:

var result = db.Foo.Select(f => new {
    f.Total,
    Received = f.Bars.Sum(b => (decimal?)b.Received)
})
.Select(r => new {
    r.Total,
    Received = r.Received ?? 0    
};
带有let子句的

或(更好)查询语法:

var result =
    from f in db.Foos
    let received = f.Bars.Sum(b => (decimal?)b.Received)
    select new { f.Total, Received = received ?? 0 };

使用MySQL Connector / Net 6.9.8

测试最新的EF6.1.3