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

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

问题描述

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

  class Repository< T>其中T:class 
{
public IQueryable< T> SearchExact(字符串关键字)
{
//有没有办法让下面的行通用
// return db.ContactModels.Where(i => i.Name == keyword )
//我也试过db.GetTable< T>()。其中​​(i => i.Name ==关键字)
//但变量i没有Name属性它只会在运行时知道它
// db也有一个方法ITable GetTable(Type modelType)但不认为这对我有帮助
}
}

在MainViewModel中,我将这样的搜索方法称为:

 库< 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>()其中(Expression.Lambda< Func< TModel,bool>>(expr,param));


解决方案

界面解决方案 p>

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

  public interface IName 
{
string Name {get; }
}

然后您的存储库可以声明为:

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

替代接口解决方案



或者,您可以使用第二个泛型参数将where放在SearchExact方法中:

  class Repository< T>其中T:class 
{
public IQueryable< T> SearchExact< U>(string keyword)其中U:T,IName
{
返回db.GetTable< U>()其中(i => i.Name == keyword);




$ b $ p
$ b

这允许Repository类与对象一起使用不要实现IName,而SearchExact方法只能用于实现IName的对象。



反射解决方案



如果你不能添加类似IName的界面到你的对象中,你可以使用反射:

  class Repository< T>其中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);




$ b

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



关于界面解决方案的更多注意事项以及为什么您可以使用它

在你的评论中你提到你不能使用一个接口,但是不能解释为什么。你说这三个模型中没有共同点,所以我认为从它们中取出一个接口是不可能的。从你的问题我明白,所有三个模型都有一个名称属性。在这种情况下,它可以在所有三个接口上实现接口。 只需实现如图所示的接口和,IName给你的三个类定义。这将为您提供本地查询和SQL生成的最佳性能。



即使所讨论的属性并非全部称为名称,仍然可以使用接口解决方案,为每个属性添加一个Name属性,并使其getter和setter访问其他属性。

表达式解决方案



如果IName解决方案不起作用,并且您需要SQL转换才能工作,则可以使用表达式构建LINQ查询来完成此操作。这项工作量较大,对本地使用效率明显较低,但会很好地转换为SQL。代码将如下所示:

  class Repository< T>其中T:Class 
{
public IQueryable< T> SearchExact(string关键字,
表达式< Func< T,字符串>> getNameExpression)
{
var param = Expression.Parameter(typeof(T),i);
return db.GetTable< T>()。其中​​(
Expression.Lambda< Func< T,bool>>(
Expression.Equal(
Expression.Invoke b $ b Expression.Constant(getNameExpression),
param),
Expression.Constant(关键字),
param));
}
}

,因此会被调用:

  repository.SearchExact(Text To Find,i => i.Name)


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

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

Solution:

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

解决方案

Interface solution

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

Alternate interface solution

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

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.

Reflection solution

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.

More notes on interface solution and why you might use it

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.

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.

Expression solution

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

and it would be called thusly:

repository.SearchExact("Text To Find", i => i.Name)

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

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