接口方案
如果你可以向你的对象添加一个接口,你就可以使用它。例如您可以定义:
public interface IName
{
string Name { get; }
}
那么你的存储库可以声明为:
class Repository<T> where T:class, IName
{
public IQueryable<T> SearchExact(string keyword)
{
return db.GetTable<T>().Where(i => i.Name == keyword);
}
}
替代接口解决方案
或者,您可以使用第二个通用参数将“where”放在 SearchExact 方法上:
class Repository<T> where T:class
{
public IQueryable<T> SearchExact<U>(string keyword) where U: T,IName
{
return db.GetTable<U>().Where(i => i.Name == keyword);
}
}
这允许 Repository 类与不实现 IName 的对象一起使用,而 SearchExact 方法只能与实现 IName 的对象一起使用。
反射解决方案
如果您无法向对象添加类似 IName 的接口,则可以使用反射:
class Repository<T> where T:class
{
static PropertyInfo _nameProperty = typeof(T).GetProperty("Name");
public IQueryable<T> SearchExact(string keyword)
{
return db.GetTable<T>().Where(i => (string)_nameProperty.GetValue(i) == keyword);
}
}
这比使用接口慢,但有时这是唯一的方法。
有关接口解决方案以及为何使用它的更多说明
在您的评论中,您提到您无法使用界面,但没有解释原因。你说“这三个模型没有任何共同点。所以我认为用它们制作一个界面是不可能的。”从你的问题中我了解到所有三个模型都有一个“名称”属性。在这种情况下,它is可以在所有三个上实现一个接口。只需将所示的接口和“,IName”实现到您的三个类定义中的每一个即可。这将为您提供本地查询和 SQL 生成的最佳性能。
即使有问题的属性并非全部称为“Name”,您仍然可以通过向每个属性添加“Name”属性并使其 getter 和 setter 访问其他属性来使用接口解决方案。
表达解决方案
如果 IName 解决方案不起作用并且您需要 SQL 转换才能工作,则可以通过使用表达式构建 LINQ 查询来实现此目的。这需要更多工作,并且对于本地使用来说效率明显较低,但可以很好地转换为 SQL。代码会是这样的:
class Repository<T> where T:Class
{
public IQueryable<T> SearchExact(string keyword,
Expression<Func<T,string>> getNameExpression)
{
var param = Expression.Parameter(typeof(T), "i");
return db.GetTable<T>().Where(
Expression.Lambda<Func<T,bool>>(
Expression.Equal(
Expression.Invoke(
Expression.Constant(getNameExpression),
param),
Expression.Constant(keyword),
param));
}
}
它将被这样称呼:
repository.SearchExact("Text To Find", i => i.Name)