こんにちは、TSoftです。
.NET Core 1.0で開発を進めていたプログラムを、2016年11月16日にリリースされた.NET Core 1.1とEF Core 1.1にアップデートしたところ、タイトルの様な部分で動かなくなってしまいました…。
ググっても情報が出ないので、慣れないブログ記事でまとめてみたいと思います。
用語等に誤りがありましたら、ぜひコメントでご指摘ください。
環境
Windows 10
Visual Studio 2015
.NET Core SDK 1.0.0-preview2-1-003177
Microsoft.AspNetCore.Mvc 1.1.0
Microsoft.EntityFrameworkCore 1.1.0
SQL Server 2014 Express LocalDB
Microsoft.EntityFrameworkCore.SqlServer 1.1.0
と
CentOS 7
PostgreSQL 9.2.15
Npgsql.EntityFrameworkCore.PostgreSQL 1.1.0
※この環境ではスタックトレースの行番号が誤って表示されたので注意
内容
下記のコードのように、1対多で親側のクラスに子クラスがListとして存在するとします。
//DbContextは省略
public class ParentItem
{
public int Id { get; set; }
public string Value1 { get; set; }
public List<SubItem> SubItems { get; set; }
}
public class SubItem
{
public int Id { get; set; }
public int ParentItemId { get; set; }
[ForeignKey("ParentItemId")]
public ParentItem ParentItem { get; set; }
public string Value1 { get; set; }
}
Where句やCount句を利用してデータベースからParentItemを選択するとき、条件式に子要素のListに対してCountメソッドとFirstメソッドを呼びます。
await _context.ParentItems
.Include(parent => parent.SubItems)
.Where(parent => parent.SubItems.Count() == 0 || parent.SubItems.First().Value1 == "foobar")
.ToListAsync()
すると、下記のような例外が発生します。
ArgumentException: Incorrect number of arguments supplied for call to method 'System.Threading.Tasks.Task`1[System.Int32] GetResult[Int32](System.Collections.Generic.IAsyncEnumerable`1[Microsoft.EntityFrameworkCore.Storage.ValueBuffer], System.Threading.CancellationToken)'
Parameter name: method
System.Dynamic.Utils.ExpressionUtils.ValidateArgumentCount(MethodBase method, ExpressionType nodeKind, int count, ParameterInfo[] pis)
厄介なことに、1回目では例外が出ますが、2回目以降は応答が返ってこなくなりdotnetプロセスがCPUを永遠と使う現象に陥ります。
回避策
幸い、いくつか回避策があります。
First()をFirstOrDefault()に置き換えて、Count()をFirstOrDefault()より後ろに持って行く
await _context.ParentItems
.Include(parent => parent.SubItems)
.Where(parent => parent.SubItems.FirstOrDefault().Value1 == "foobar" || parent.SubItems.Count() == 0)
.ToListAsync()
非同期メソッドではなく同期メソッドを使用する
_context.ParentItems
.Include(parent => parent.SubItems)
.Where(parent => parent.SubItems.Count() == 0 || parent.SubItems.First().Value1 == "foobar")
.ToList()
そのほかに、0であるかの判定であればFirstOrDefault() == null
を利用するなどあると思います。
おわりに
普段こういった記事に助けられているので、自分も助けになれば幸いです。
英語が達者ならIssueを投げられたのですが…
リンク
GitHub - aspnet/EntityFramework
https://github.com/aspnet/EntityFramework
NuGet Gallery | Microsoft.EntityFrameworkCore
https://www.nuget.org/packages/Microsoft.EntityFrameworkCore/