我有一个带有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)
...
那是我尝试过的:
我在每个类中添加了一个静态方法,该方法将生成一个Expression
来搜索该特定类,以及它如何查找Person
类:
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'
。第二个是我不知道如何返回每个GetFilters
的执行结果,我怀疑我会得到false
任何记录,因为它是最后一个默认值Expression
中的Block
...
可能是我走错了路,这应该以完全不同的方式完成?
答案 0 :(得分:2)
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
),对于as
运算符,相应的表达式metod为Expression.TypeAs
。
答案 1 :(得分:0)
您不需要做所有这些。只需创建一个通用方法,在调用它时关闭。您的通用方法可以是这样的:
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");