c# - 通过泛型类型参数访问属性

标签 c# wpf linq-to-sql generics

我正在尝试为我的模型创建一个通用存储库。目前我有 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/

相关文章:

c# - IIS 内的 dotnet 进程崩溃。如何确定原因并采取补救措施?

.net - 找不到资源词典

c# - 在wpf中绘制双线

.net - Linq2Sql 与存储过程

c# - swagger.json 路径和定义为空。规范中未定义任何操作

c# - 无法将类型 'System.Windows.Controls.WebBrowser' 转换为 'SHDocVw.DWebBrowserEvents'

wpf - 从下到上显示列表框项目

c# - LINQ:无效的匿名类型成员声明符。匿名类型成员必须使用成员分配、简单名称或成员访问来声明

linq - 在 Linq 查询中使用自定义函数

c# - 枚举的变量总是被初始化?