使用 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)
现在我需要获取 GiveName
或 Surname
包含 searchTerm
的客户数量,以防客户是 Person
或相同的 searchTerm
] 在 Name
字段中,以防它是一家公司。
TL;DR
如何实现具有 Customers
的单个可搜索分页列表(包含 Person
和 Company
类型)的视图?我的意思是就带有签名的方法而言 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
方法,该方法仅在需要过滤具体派生实体时适用。经过反复试验,幸运的是有两个受支持的结构 - is
和 as
(重要: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
.
我有一个带有 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)
现在我需要获取 GiveName
或 Surname
包含 searchTerm
的客户数量,以防客户是 Person
或相同的 searchTerm
] 在 Name
字段中,以防它是一家公司。
TL;DR
如何实现具有 Customers
的单个可搜索分页列表(包含 Person
和 Company
类型)的视图?我的意思是就带有签名的方法而言 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
方法,该方法仅在需要过滤具体派生实体时适用。经过反复试验,幸运的是有两个受支持的结构 - is
和 as
(重要: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
.