使用 EF TPT 在多个表中搜索记录

Searching for records in several tables using EF TPT

我有一个带有 TPT 继承映射的项目,现在我需要给它添加一个搜索功能,以便在多个表中查找记录。这就是我目前拥有的:

public abstract class Customer
{
    public int      Id      { get; set; }
    public string   Memo    { get; set; }
...
}

public class Person : Customer
{
    public string   GivenName   { get; set; }
    public string   Surname     { get; set; }
...
}
public class Company : Customer
{
    public string       Name     { get; set; }
...
}

我还有一个工作单元和一堆存储库,我需要将过滤功能添加到 CustomerRepository 的几个方法中。假设我有一个具有以下签名的 Count 方法

public int Count(System.Linq.Expressions.Expression<Func<Customer, bool>> filter = null)

现在我需要获取 GiveNameSurname 包含 searchTerm 的客户数量,以防客户是 Person 或相同的 searchTerm ] 在 Name 字段中,以防它是一家公司。

TL;DR
如何实现具有 Customers 的单个可搜索分页列表(包含 PersonCompany 类型)的视图?我的意思是就带有签名的方法而言 public IHttpActionResult Get(string searchTerm, int pageSize, int pageNumber)...

那是我试过的:
我向每个 classes 添加了一个静态方法,该方法会生成一个 Expression 来搜索特定的 class,这就是它查找 Person class 的方式:

public static System.Linq.Expressions.Expression<Func<Person, bool>> GetFilter(string searchTerm)
{
    if (String.IsNullOrWhiteSpace(searchTerm))
    {
        return null;
    }
    var parameterExpression = System.Linq.Expressions.Expression.Parameter(typeof(Person));
    System.Reflection.MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });

    return System.Linq.Expressions.Expression.Lambda<Func<Person, bool>>(
            System.Linq.Expressions.Expression.OrElse(
                System.Linq.Expressions.Expression.Call(
                    System.Linq.Expressions.Expression.PropertyOrField(parameterExpression, "GivenName"),
                    method,
                    System.Linq.Expressions.Expression.Constant(searchTerm, typeof(string))
                ),
                System.Linq.Expressions.Expression.Call(
                    System.Linq.Expressions.Expression.PropertyOrField(parameterExpression, "Surname"),
                    method,
                    System.Linq.Expressions.Expression.Constant(searchTerm, typeof(string))
                )
            ), parameterExpression);
}

并尝试构建一个 Expression 来检查客户的类型,然后进行适当的数据检查,但在这里我被难住了......这就是我现在所拥有的:

    var parameterExpression = System.Linq.Expressions.Expression.Parameter(typeof(Customer));
    var typeIsPerson = System.Linq.Expressions.Expression.TypeIs(parameterExpression, typeof(Person));
    var typeIsCompany = System.Linq.Expressions.Expression.TypeIs(parameterExpression, typeof(Company));

    var q = System.Linq.Expressions.Expression.Block(
        System.Linq.Expressions.Expression.IfThen(typeIsPerson, Person.GetFilter(searchTerm)),
        System.Linq.Expressions.Expression.IfThen(typeIsCompany, Company.GetFilter(searchTerm)),
        System.Linq.Expressions.Expression.Constant(false));

    var a = System.Linq.Expressions.Expression.Lambda<Func<Customer, bool>>(
            q, parameterExpression);

这里我有两个问题(至少?),首先,当我尝试调用 Count 时,我得到一个非常不愉快的 NotSupportedException 异常,显示 Unknown LINQ expression of type 'Block'。第二个是我不知道如何 return 每个 GetFilters 的执行结果,我怀疑我会得到任何记录的 false 因为它是默认值我的 Block 中的最后一个 Expression...
可能是我走错了路,这是应该以完全不同的方式完成的事情吗?

您不需要做所有这些。只需创建一个通用方法,您将在调用它时 close。你的通用方法可以是这样的:

public static int Count<T>(Expression<Func<T, bool>> filter = null)
{
    var ctx = new StackContext();
    return ctx.Customers.OfType<T>().Where(filter).Count();
}

你可以这样称呼它:

// Here you are closing the generic to be of type Person
var personsCount = Count<Person>(x => x.GivenName == "George");
// Here you are closing the generic to be of type Customer
var companyCount = Count<Company>(x => x.Name == "Test");

LINQ to Entities 通常不支持表达式块。通常您不需要它们,因为您可以仅使用 C# 条件运算符 ? :(映射到 Expression.Condition)构建几乎任何表达式。

但在尝试动态构建表达式之前,您需要找到一个 EF 支持的构造,该构造可与 TPT(和其他 EF 继承模型)多态查询一起使用。这并不容易,因为所有示例都使用 OfType 方法,该方法仅在需要过滤具体派生实体时适用。经过反复试验,幸运的是有两个受支持的结构 - isas(重要:as,而不是强制转换!)。

所以有问题的静态构建的谓词表达式可能是这样的:

Expression<Func<Customer, bool>> predicate = c =>
    c is Person ? 
        ((c as Person).GivenName.Contains(searchTerm) || (c as Person).Surname.Contains(searchTerm)) :
    c is Company ? 
        (c as Company).Name.Contains(searchTerm) :
    false;

(坦率地说,您不想查看生成的 SQL,但它有效)

现在您可以根据需要动态构建它。您已经找到 is 表达式方法 (Expression.TypeIs), for as operator the corresponding expression metod is Expression.TypeAs.