C# の LINQ 式によるデータベースアクセスの際に、クラス/インターフェースの値など、プリミティブ型ではない型を含めるとエラーになることがあります。
たとえば、次のようなコードが考えられます。
問題例
public async Task<IEnumerable<IProduct>> SearchProductsAsync(string orderNo, string refMasterNo, string productName, IEndUser endUser, bool needHidden, int limit) { List<Product> products; using (var context = new DbContext()) { products = await context.Products .Where(p => !string.IsNullOrEmpty(orderNo) ? p.OrderNo.Contains(orderNo) : true) .Where(p => !string.IsNullOrEmpty(refMasterNo) ? p.RefMasterNo.Contains(refMasterNo) : true) .Where(p => !string.IsNullOrEmpty(productName) ? p.Name.Contains(productName) : true) .Where(endUser != null ? p.EndUserId == endUser.Id : true) // エラー .Where(p => needHidden ? true : p.IsDeleted == false) .Take(limit) .ToListAsync(); } return products; }
テーブルから、入力のあった値だけ絞り込みをかけたい場合、上述のように記述することもできると思いますが、これだとエラーになってしまいます。
Unable to create a constant value of type '***.Services.Db.Interfaces.IEndUser'.
Only primitive types or enumeration types are supported in this context
LINQ を生成するときに、enum やプリミティブ型以外のものを持ってくると、正しいコードが生成できなくてエラーになってしまいます。コードをシンプルにしてやる必要があるわけですね。
対応例
先にプリミティブ型 int にデータを移してから、LINQ を実行します。
public async Task<IEnumerable<IProduct>> SearchProductsAsync(string orderNo, string refMasterNo, string productName, IEndUser endUser, bool needHidden, int limit) { List<Product> products; int endUserId = endUser?.Id ?? 0; using (var context = new DbContext()) { products = await context.Products .Where(p => !string.IsNullOrEmpty(orderNo) ? p.OrderNo.Contains(orderNo) : true) .Where(p => !string.IsNullOrEmpty(refMasterNo) ? p.RefMasterNo.Contains(refMasterNo) : true) .Where(p => !string.IsNullOrEmpty(productName) ? p.Name.Contains(productName) : true) .Where(p => endUserId > 0 ? p.EndUserId == endUserId : true) .Where(p => needHidden ? true : p.IsDeleted == false) .OrderBy(p => p.UpdatedDateTime) .Take(limit) .ToListAsync(); } return products; }
考察
単純に DB 問い合わせをするクエリの中に、知らない型を含めたらダメじゃないか(そこまで都合よく最適化してくれない)という理解でいいと思います。
という記事があって、これも DB データとローカルのコレクションをおそらく Join しようとしてエラーになっているのだと思います。なので、DB アクセスをする処理のときは、ローカルなデータから利用する型は、プリミティブ型だけに制限できているのか注意が必要です。
エラー発生の雰囲気がちょっとだけ通常と違う不思議な感じ(コンパイル前の構文チェックでは問題ないし)なので、原因は何になるのかドキっとするかもしれないけど、エラーメッセージにちゃんと原因(理由)が書いてありました。
エラー内容を読んであげようという学び(常識だった)