动态 LINQ 而不是 ExecuteSqlRaw

Dynamic LINQ instead of ExecuteSqlRaw

因为我要使用Entity Framework Core,所以我尝试转换一个SqlRaw语句

            var sql = @$"
                         update ItemList
                         set flag = 1
                         where
                             flag = 0 and
                            {groupingField} in (select {groupingField} from ItemList
                                                    where ID in (select itemID from selectedItems))
            ";
            int noOfRowsAffected = DbContext.ExecuteSqlRaw(sql);

转换为一系列 LINQ 语句。

            var ids = DbContext.selectedItems
                      .Select(x => x.itemID).ToList();

            var groupIds = DbContext.ItemList
                           .Where(p => ids.Contains(p.Id) && p.flag == 0
                           .Select(p => p.GetType().GetProperty(groupingField).GetValue(p)).ToList();

            var rows = DbContext.ItemList
                .Where(p => groupIds.Contains(p.GetType().GetProperty(groupingField).GetValue(p)))
                .ToList();

            foreach(var row in rows)
            {
                row.flag = 1;
            }

当我执行语句时,我在第三条语句 (var rows = ...) 上捕获到异常:

The LINQ expression 'DbSet<ItemList>
.Where(t => __groupIds_0.Contains(t.GetType().GetProperty(__groupingField_1).GetValue(t)))' could not be 
translated. Either rewrite the query in a form that can be translated, or switch to client evaluation 
explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync(). 
See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.

如何重写可以翻译的声明?

以下扩展有助于完成您想要的工作,甚至支持 IEnumerableIQueryable 项目:

var ids = DbContext.selectedItems.Select(x => x.itemID);

var rows = DbContext.ItemList
      .FilterByItems(ids, groupingField)
      .Where(p => p.flag == 0)
      .ToList();

foreach (var row in rows)
{
      row.flag = 1;
}      

和实现:

public static class FilterExtensions
{
      public static IQueryable<TEntity> FilterByItems<TEntity, TKey>(this IQueryable<TEntity> query,
            IEnumerable<TKey> items, Expression<Func<TEntity, TKey>> entityKey)
      {
            var entityParam = entityKey.Parameters[0];

            if (items is IQueryable queryableItems)
            {
                  var containsLambda = Expression.Lambda<Func<TEntity, bool>>(
                        Expression.Call(typeof(Queryable), nameof(Queryable.Contains),
                              new[] {typeof(TKey)},
                              queryableItems.Expression,
                              entityKey.Body
                        ),
                        entityParam);
                  return query.Where(containsLambda);
            }
            else
            {
                  var containsLambda = Expression.Lambda<Func<TEntity, bool>>(
                        Expression.Call(typeof(Enumerable), nameof(Enumerable.Contains),
                              new[] {typeof(TKey)},
                              Expression.Constant(items),
                              entityKey.Body
                        ),
                        entityParam);
                  return query.Where(containsLambda);
            }
      }

      public static IQueryable<TEntity> FilterByItems<TEntity, TKey>(this IQueryable<TEntity> query,
            IEnumerable<TKey> items, string entityKey)
      {
            var entityParam     = Expression.Parameter(typeof(TEntity), "e");
            var entityKeyLambda =
                  Expression.Lambda<Func<TEntity, TKey>>(Expression.PropertyOrField(entityParam, entityKey), entityParam);

            return query.FilterByItems(items, entityKeyLambda);
      }
}