【问题标题】:Convert Expression<Func<T, bool>> to another Predicate Expression Expression<Func<U, bool>>将 Expression<Func<T, bool>> 转换为另一个谓词表达式 Expression<Func<U, bool>>
【发布时间】:2021-08-25 07:05:08
【问题描述】:

我想将一个谓词表达式(Expression>)转换为另一个谓词表达式(Expression>)但转换后我无法通过 LINQ 进行查询。 我已经尝试过thisthis 方法,但没有任何工作正常 谁能告诉我如何正确地做到这一点,我解决这个问题的方法。

using System;
using System.Linq;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Threading.Tasks;
using System.Reflection;

public class ItemEntity {
    public int ItemId {set;get;}
    public int ItemParentId {set;get;}
    public int ItemName {set;get;}
}

public class Item {
   public int Id{ set;get;}
   public int ParentId{set;get;}
   public int Name {set;get;}
}

public class Program
{
    
    public static Item Convert(ItemEntity itemChild)
    {
        return new Item()
        {
            Id = itemChild.ItemId,
            ParentId = itemChild.ItemParentId,
            Name = itemChild.ItemName
        };
    }
    
    public async Task<IList<ItemEntity>> SelectAsync(Expression<Func<ItemEntity, bool>> predicate)
    {
        // using Microsoft.EntityFrameworkCore;
        // private readonly DbContext _context; // Injected globally by using Service.AddScoped<ItemContext>();
        // return await _context.Set<ItemEntity>().AsNoTracking().Where(predicate).ToListAsync();
        return await Task.Run(() => new List<ItemEntity>()); // actually return the result with matching predicate
    }
    public async Task<List<Item>> GetItems(Expression<Func<Item, bool>> expression)
    {
        
            MethodInfo convertMethod = ((Func<ItemEntity, Item>)Convert).Method;
            
            var p = Expression.Parameter(typeof(ItemEntity));
            var converted = Expression.Lambda<Func<ItemEntity, bool>>(
                Expression.Invoke(expression, Expression.Convert(p, typeof(Item), convertMethod)), p);
            
            IList<ItemEntity> res = await SelectAsync(converted);
            var t = res.Select(x => Convert(x)).ToList();
            return t;
    }
    
    public static void Main()
    {
        Program pr = new Program();
        Func<Expression<Func<Item, bool>>, Task<List<Item>>> getItem = pr.GetItems;
        var res = getItem.Invoke(x => x.Id.Equals(1));
        Console.WriteLine("Hello World");
    }
}

但我遇到了错误

无法翻译 LINQ 表达式“DbSet()\r\n .Where(i => ((Item)i).Id.Equals(__Id_0))”。以可翻译的形式重写查询,或通过插入对“AsEnumerable”、“AsAsyncEnumerable”、“ToList”或“ToListAsync”的调用显式切换到客户端评估。请参阅https://go.microsoft.com/fwlink/?linkid=2101038 了解更多信息。

我无法正确理解它,根据我的理解,我使用 ToList() 进行客户端评估,但还提供了将 ItemEntity 转换为 Item 的方法。

我是否有任何其他方法可以基于 ItemEntity 创建新的表达式树,然后在 DBSet 上进行查询? 任何帮助表示赞赏

使用的版本:

  • 点网 5.0
  • Microsoft.EntityFrameworkCore 5.0.6
  • EntityFramework 6.4.4
  • 数据库 SQL 服务器

【问题讨论】:

  • 你在发明另一个通用存储库吗?
  • @SvyatoslavDanyliv 一点也不,这里我给你示例代码,要求是我的业务逻辑是玩 Item 类和 ItemEntity 类代表 SQL 服务器数据库表,当尝试做项目对象列表上的 LINQ 我希望根据参数 ItemEntity 将该谓词表达式转换为谓词表达式以从数据库中获取数据,在我的代码中,所有存储库都位于单独的 dll 上

标签: linq c#-4.0 expression-trees asp.net-core-5.0 entity-framework-core-5.0


【解决方案1】:

尝试以下方法。主要思想是在实现之前对投影的 DTO 使用过滤器。

using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Threading.Tasks;

public class ItemEntity
{
    public int ItemId { set; get; }
    public int ItemParentId { set; get; }
    public int ItemName { set; get; }
}

public class Item
{
    public int Id { set; get; }
    public int ParentId { set; get; }
    public int Name { set; get; }
}

public class Program
{
    public static Expression<Func<ItemEntity, Item>> ToItem()
    {
        return itemChild => new Item
        {
            Id = itemChild.ItemId,
            ParentId = itemChild.ItemParentId,
            Name = itemChild.ItemName
        };
    }

    public Task<IList<Item>> SelectAsync(Expression<Func<Item, bool>> predicate)
    {
        return _context.Set<ItemEntity>()
            .AsNoTracking()
            .Select(ToItem())
            .Where(predicate)
            .ToListAsync();

        //return Task.Run(() => new List<ItemEntity>().AsQueryable().Select(ToItem()).Where(predicate).ToList());
    }

    public async Task<IList<Item>> GetItems(Expression<Func<Item, bool>> expression)
    {
        var res = await SelectAsync(expression);
        return res;
    }

    public static void Main()
    {
        var pr = new Program();
        var res = pr.GetItems(x => x.Id == 1);
        Console.WriteLine("Hello World");
    }
}

【讨论】:

  • 感谢您为我提供解决方案,但这里有一个小问题,首先 _context.Set() 返回一个 IQuerable 并且您首先进行投影然后进行选择,如果我这样做,那么我有通过使用 AsIEnumerable 或 ToList 或 TOListAsync() 这不是最佳方法来强制客户端评估,这就是为什么我想将 Expression> 转换为 Expression> 在这种情况下它可以向服务器端查询
  • 第二个 ToItem() 在不同的 dll 中,SelectAsync 在不同的 dll 中,我不想在 Repository dll 中创建任何反向依赖
  • 今天晚些时候会更正答案。但我认为这里的问题和往常一样 - 愚蠢的通用存储库模式。
猜你喜欢
  • 1970-01-01
  • 2021-06-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-09
相关资源
最近更新 更多