我有一个程序,它为多种类型的类传递一些查询条件,EF使用这些查询条件从数据库中查询数据。每个被查询的类型都有一个标准类,但是这些方法之间存在很多重复和相似之处,我希望重构这些方法,但没有成功。
这些是示例(简化)服务方法:
private static IQueryable<Class1> FilterClass1ResultsOnId(Class1QueryCriteria queryCriteria, IQueryable<Class1> queryResults)
{
if (!string.IsNullOrEmpty(queryCriteria.Id))
{
queryResults = from view in queryResults where view.Id==queryCriteria.Id select view;
}
return queryResults;
}
private static IQueryable<Class2> FilterClass2ResultsOnId(Class2QueryCriteria queryCriteria, IQueryable<Class2> queryResults)
{
if (!string.IsNullOrEmpty(queryCriteria.Id))
{
queryResults = from view in queryResults where view.Id == queryCriteria.Id select view;
}
return queryResults;
}
......就像这样被称为......
queryResults = FilterClass1ResultsOnId(queryCriteria, queryResults);
该服务有一系列这些'FilterClassXonSOMECRITERIA'方法来缩小最终返回的IQueryable。除了输入参数的类型和输出的类型之外,还有很多这些服务方法基本相同。
我最初尝试重构接受queryResults
参数的方法,该参数是一个接口而不是一个具体的类 - 一个只有Id
属性的简单接口 - 然后创建{{1} }和Class1
实现该接口。但是,如果我只想使用一种方法,那么我还需要使这些方法的返回类型非具体。这就是我被卡住的地方。
我一直在环顾四周,看过协方差和逆向信息,但我似乎无法定义使用这些接口和泛型类型的方法/接口没有得到'无法解析符号T'或'参数类型XXX不能分配给参数类型YYY''。
此时我想与那些更熟悉这个问题的人进行健全检查:我想做的是什么?我是否可以使用仅由接口指定参数的方法,并从该方法返回一个也仅由接口指定的对象?
编辑:标准类非常简单,实际上是EF模型中定义的复杂类型。但这是一个简化的例子:
Class2
答案 0 :(得分:2)
您可以手动构建过滤器表达式:
private static Expression<Func<T, bool>> WhereById<T>(string id)
{
var pExpr = Expression.Parameter(typeof(T));
var idExpr = Expression.Property(pExpr, "Id");
var eqExpr = Expression.Equal(idExpr, Expression.Constant(id));
return Expression.Lambda<Func<T, bool>>(eqExpr, pExpr);
}
private static IQueryable<T> FilterById<T>(string id, IQueryable<T> source)
{
if (!string.IsNullOrEmpty(queryCriteria.Id))
{
var whereExpr = WhereById<T>(string id);
return source.Where(whereExpr);
}
return queryResults;
}
private static IQueryable<Class1> FilterClass1ResultsOnId(Class1QueryCriteria queryCriteria, IQueryable<Class1> queryResults)
{
return FilterById(queryCriteria.Id, queryResults);
}
如果使用字符串Id
属性创建基本类型,则可以避免需要单独的重载。
或者,您可以使条件类通用,并直接包含查询表达式,例如
public class QueryCriteria<T>
{
public Expression<Func<T, bool>> FilterExpression { get; set; }
}
然后你可以写下这样的东西:
public static IQueryable<T> FilterResults<T>(QueryCriteria<T> criteria, IQueryable<T> source)
{
return criteria.FilterExpression == null ? source : source.Where(criteria.FilterExpression);
}
答案 1 :(得分:1)
您应该能够为条件定义接口:
interface IQueryCriteria {
string Id { get; set; }
}
..如有必要,还可以使用“视图”类型的界面:
interface IViewType {
string Id { get; set; }
}
然后你的方法(非复数)可以是通用的:
private static IQueryable<T> FilterClassResultsOnId<T>(IQueryCriteria queryCriteria, IQueryable<T> queryResults)
where T : IViewType
{
if (!string.IsNullOrEmpty(queryCriteria.Id))
{
queryResults = from view in queryResults where view.Id == queryCriteria.Id select view;
}
return queryResults;
}
这里的关键是泛型类型约束where T : IViewType
。这允许在方法体中使用view.Id
,因为编译器可以确认该类型至少会实现具有Id
属性的接口。