我正在尝试为我的模型创建一个通用存储库。目前我有 3 个不同的模型,它们之间没有关系。 (联系人、备注、提醒)。
class Repository<T> where T:class
{
public IQueryable<T> SearchExact(string keyword)
{
//Is there a way i can make the below line generic
//return db.ContactModels.Where(i => i.Name == keyword)
//I also tried db.GetTable<T>().Where(i => i.Name == keyword)
//But the variable i doesn't have the Name property since it would know it only in the runtime
//db also has a method ITable GetTable(Type modelType) but don't think if that would help me
}
}
在 MainViewModel 中,我这样调用 Search 方法:
Repository<ContactModel> _contactRepository = new Repository<ContactModel>();
public void Search(string keyword)
{
var filteredList = _contactRepository.SearchExact(keyword).ToList();
}
解决方案:
最后采用了 Ray 的动态表达式解决方案:
public IQueryable<TModel> SearchExact(string searchKeyword, string columnName)
{
ParameterExpression param = Expression.Parameter(typeof(TModel), "i");
Expression left = Expression.Property(param, typeof(TModel).GetProperty(columnName));
Expression right = Expression.Constant(searchKeyword);
Expression expr = Expression.Equal(left, right);
}
query = db.GetTable<TModel>().Where(Expression.Lambda<Func<TModel, bool>>(expr, param));
最佳答案
接口(interface)解决方案
如果你可以为你的对象添加一个接口(interface),你就可以使用它。例如你可以定义:
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);
}
}
备用接口(interface)解决方案
或者,您可以使用第二个通用参数将“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 的接口(interface),你可以使用反射来代替:
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);
}
}
这比使用接口(interface)慢,但有时这是唯一的方法。
关于界面解决方案的更多说明以及您可能会使用它的原因
在您的评论中,您提到您不能使用界面,但没有解释原因。你说“这三个模型没有任何共同点。所以我认为用它们制作一个接口(interface)是不可能的。”从你的问题我了解到所有三个模型都有一个“名称”属性。在那种情况下,可以在所有三个上实现一个接口(interface)。只需为您的三个类定义中的每一个实现如图所示的接口(interface)和“,IName”。这将为您提供本地查询和 SQL 生成的最佳性能。
即使有问题的属性并非都称为“名称”,您仍然可以通过向每个属性添加一个“名称”属性并使其 getter 和 setter 访问其他属性来使用接口(interface)解决方案。
表达液
如果 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)
关于c# - 通过泛型类型参数访问属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3059420/