通过泛型类型参数访问属性 [英] Accessing properties through Generic type parameter

查看:21
本文介绍了通过泛型类型参数访问属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试为我的模型创建一个通用存储库.目前我有 3 种不同的模型,它们之间没有关系.(联系人、备注、提醒).

I'm trying to create a generic repository for my models. Currently i've 3 different models which have no relationship between them. (Contacts, Notes, Reminders).

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 方法:

In MainViewModel, I call the Search method like this:

Repository<ContactModel> _contactRepository = new Repository<ContactModel>();

public void Search(string keyword)
{
    var filteredList = _contactRepository.SearchExact(keyword).ToList();
}

解决方案:

最终采用 Ray 的动态表达式解决方案:

Finally went with Ray's Dynamic Expression solution:

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));

推荐答案

接口解决方案

如果你可以为你的对象添加一个接口,你就可以使用它.例如,您可以定义:

If you can add an interface to your object you can use that. For example you could define:

 public interface IName
 {
   string Name { get; }
 }

那么您的存储库可以声明为:

Then your repository could be declared as:

class Repository<T> where T:class, IName
{
  public IQueryable<T> SearchExact(string keyword)  
  {  
    return db.GetTable<T>().Where(i => i.Name == keyword);
  }
}  

替代界面解决方案

或者,您可以使用第二个通用参数将where"放在 SearchExact 方法上:

Alternatively you could put the "where" on your SearchExact method by using a second generic parameter:

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 的对象.

This allows the Repository class to be used with objects that don't implement IName, whereas the SearchExact method can only be used with objects that implement IName.

反射解决方案

如果你不能给你的对象添加类似 IName 的接口,你可以使用反射来代替:

If you can't add an IName-like interface to your objects, you can use reflection instead:

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);
  }
}

这比使用接口慢,但有时它是唯一的方法.

This is slower than using an interface, but sometimes it is the only way.

有关界面解决方案的更多说明以及您可能使用它的原因

在您的评论中,您提到您不能使用界面,但没有解释原因.你说这三个模型没有任何共同点.所以我认为用它们制作一个界面是不可能的."从您的问题中,我了解到所有三个模型都具有名称"属性.在这种情况下, 可以在所有三个上实现一个接口.只需为三个类定义中的每一个实现如图所示的接口和,IName".这将为您提供本地查询和 SQL 生成的最佳性能.

In your comment you mention that you can't use an interface but don't explain why. You say "Nothing in common is present in the three models. So i think making an interface out of them is not possible." From your question I understood that all three models have a "Name" property. In that case, it is possible to implement an interface on all three. Just implement the interface as shown and ", IName" to each of your three class definitions. This will give you the best performance for both local queries and SQL generation.

即使有问题的属性不都被称为Name",您仍然可以通过向每个属性添加Name"属性并让其 getter 和 setter 访问其他属性来使用接口解决方案.

Even if the properties in question are not all called "Name", you can still use the nterface solution by adding a "Name" property to each and having its getter and setter access the other property.

表达解决方案

如果 IName 解决方案不起作用并且您需要 SQL 转换工作,您可以通过使用表达式构建您的 LINQ 查询来实现.这更多的工作并且在本地使用时效率明显较低,但可以很好地转换为 SQL.代码应该是这样的:

If the IName solution won't work and you need the SQL conversion to work, you can do this by building your LINQ query using Expressions. This more work and is significantly less efficient for local use but will convert to SQL well. The code would be something like this:

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)

这篇关于通过泛型类型参数访问属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆