首页 > 解决方案 > LINQ 通用 IQueryableWHERE 子句

问题描述

假设我有一个带有 3 个 DBSet 的 DBContext:

public class DatabaseContext : DbContext
{
    public DbSet<A> As { get; set; }
    public DbSet<B> Bs { get; set; }
    public DbSet<C> Cs { get; set; }
}

class A
{
    public string Text { get; set; }
    public string Code { get; set; }
}

class B
{
    public string Name { get; set; }
    public string Code { get; set; }
}

class C
{
    public string Book { get; set; }
    public string Code { get; set; }
}

我想编写一个通用方法:

  1. 通常将三个中的任何一个DbSet作为第一个参数
  2. 将字符串值作为第二个参数
  3. 不强制转换为,返回提供的字段与提供的字符串匹配的IEnumerable第一条记录DbSetCode

到目前为止,我有这个方法:

public static T GetCode<T>(IQueryable<T> set, string code) where T : class
{
    var Prop = typeof(T).GetProperty("Code");
    return set.Where(x => (string)Prop.GetValue(x) == code).FirstOrDefault();
}

当我尝试使用此行调用它时:

var _A = GetCode(TheDB.As, "123");
var _B = GetCode(TheDB.Bs, "123");
var _C = GetCode(TheDB.Cs, "123");

我收到此错误:

无效操作异常:

无法翻译 LINQ 表达式 'DbSet<A>.Where(m => (string)__Prop_0.GetValue(m) == __code_1)'。

以可翻译的形式重写查询,或通过插入对 AsEnumerable()、AsAsyncEnumerable()、ToList() 或 ToListAsync() 的调用显式切换到客户端评估。

我怎样才能写出能够正确翻译的WHERE子句?如果我将 转换为 ,我的方法有效,但我不想这样做,因为该集合可能非常大,我希望数据库(而不是我的应用程序)进行记录搜索。DbSetIQueryableIQueryableIEnumerable

标签: linqreflectiondbcontext

解决方案


最简单(并且类型安全)的方法是只创建一个类似的接口IHaveCode并将泛型参数限制为它:

interface IHaveCode
{
     string Code { get; set; }
}
class A: IHaveCode
{
    public string Text { get; set; }
    public string Code { get; set; }
}

class B: IHaveCode
{
    public string Name { get; set; }
    public string Code { get; set; }
}

class C: IHaveCode
{
    public string Book { get; set; }
    public string Code { get; set; }
}

public static T GetCode<T>(IQueryable<T> set, string code) where T : IHaveCode
{
    return set.Where(x => x.Code == code).FirstOrDefault();
}

如果由于某种原因它不适合您,那么您将需要自己构建一个表达式树


推荐阅读