【问题标题】:Multiple search parameters with LINQ使用 LINQ 的多个搜索参数
【发布时间】:2019-08-08 15:56:40
【问题描述】:

我正在编写我认为应该是一个相对简单的 Windows 窗体应用程序。我正在使用 LINQ to SQL,尽管我以前从未使用过它。我们有一个 SQL Server 数据库,我正在创建一个前端来访问该数据库。我正在尝试找出使用它搜索多个(任意数量)搜索参数的最有效方法。

在 Windows 窗体中,我创建了一个字典,其中包含要搜索的每个搜索键及其值,并将其传递给我的 search() 方法。我正在尝试找到一种方法来使用每个键及其关联值来搜索数据库。这是我想要做的:

public IQueryable<Product> Search(Dictionary<string, string> searchParams)
{
   DBDataContext dc = new DBDataContext();
   var query = dc.Products;

   foreach (KeyValuePair<string, string> temp in searchParams)
   {
      query = query.Where(x => x.(temp.Key) == temp.Value);
   }

   return query;
}

我意识到在语法上 x.(temp.Key) 是不正确的,但我希望这能说明我正在尝试做的事情。我想知道是否有另一种方法可以去做我想做的事情而不必做一个巨大的 switch 语句(或 if/else if tree)。

编辑

所以,我对其进行了一些修改,但我仍然遇到问题。这是我目前拥有的:

public IQueryable<Product> Search(Dictionary<string, string> searchParams)
{
    DBDataContext dc = new DBDataContext();

    string sQuery = "";
    foreach (KeyValuePair<string, string> temp in searchParams)
    {
        sQuery += temp.Key + "=" + temp.Value + " AND ";
    }

    var query = dc.Products.Where(sQuery);

    return query;
}

根据 LINQ 动态查询库文章,这应该没问题。这是我得到的错误:

无法从用法中推断方法“System.Linq.Queryable.Where(System.Linq.IQueryable, System.Linq.Expressions.Expression>)”的类型参数。尝试明确指定类型参数。

【问题讨论】:

  • 搜索方法参数是否需要是字典而不是 Func ???
  • 不完全确定你的意思...你能澄清一下吗?
  • 字典中的每个键都包含要搜索的参数(以及要搜索的每个参数值)。
  • 根据您的动态查询构建器示例,我在下面的末尾添加了关于“AND”的编辑,希望能为您解决。

标签: c# linq-to-sql


【解决方案1】:

这是一个使用 Dynamic LINQ Query Library 的示例(我刚刚测试过)。

using System.Linq.Dynamic;
// ...

Dictionary<string, string> searchParams = new Dictionary<string,string>();

searchParams.Add("EmployeeID", "78");
searchParams.Add("EmpType", "\"my emp type\"");

IQueryable<Employee> query = context.Employees;

foreach (KeyValuePair<string, string> keyValuePair in searchParams)
{
    query = query.Where(string.Format("{0} = {1}", keyValuePair.Key,  keyValuePair.Value));
}

List<Employee> employees = query.ToList();

并且,为了绝对清楚这段代码确实有效,这里是实际生成的SQL:

FROM [HumanResources].[Employee] AS [t0]
WHERE ([t0].[EmpType] = @p0) AND ([t0].[EmployeeID] = @p1)',N'@p0 nvarchar(11),@p1 int',@p0=N'my emp type',@p1=78

【讨论】:

  • 它终于编译好了,所以这是一个巨大的第一步。非常感谢你和凯文尼尔森。现在不同的问题:当我输入要搜索的值时,DynamicLibrary 中会引发异常,即“产品”中不存在属性或字段 [我搜索的值]。因此,似乎不是在每个条目的键中搜索值,而是在每个条目中搜索我要搜索的值的字段。如果我正在搜索名字,它正在寻找 Employee.jonathan = first_name,而不是 Employee.first_name = jonathan。
  • 我尝试将查询字符串的顺序切换为 query = query.Where(string.Format("{0} = {1}", keyValuePair.Value, keyValuePair.Key));它仍然抛出同样的异常。
  • 在调试器中,似乎无论字典中键和值的顺序如何,DynamicLibrary 总是搜索值,而不是键。
  • 嗯,没有看到你的实际代码很难说。切换键和值无济于事。一种策略是将查询字符串临时硬编码到 Where() 中以消除字符串构建错误,并确保它首先以这种方式工作。喜欢:query = query.Where("EmployeeID = 78");等等。
  • 另外,您可能希望使用 SQL Server Profiler 来验证生成的 SQL 查询。 IIRC,当您使用 Dynamic 时,VS 调试器无法向您显示 SQL。
【解决方案2】:

如果由于某种原因不需要字典,我会让你的搜索方法如下:

public IQueryable<Product> Search( Func<Product, bool> isMatch )
{
   DBDataContext dc = new DBDataContext();
   return dc.Products.Where( isMatch ).AsQueryable();
}

然后,你会使用这样的方法:

Obj.Search( item => item.Property1 == "Hello" && item.Property2 == "World" );

有什么理由你不能这样做吗?

[编辑:添加 AsQueryable()]

[编辑:使用字符串的动态查询]

看看这里,看看这是否有帮助。我没有使用它,但看起来它就是你要找的东西: http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

就个人而言,我通常更喜欢类型安全的 Expression> 方法,因为这会给您带来编译时错误……但如果需要字符串,那么看起来是最好的方法。

根据上面的链接,看起来你应该可以做到:

query = query.Where( String.Format("{0}={1}",dict.Key,dict.Value) );

[编辑:字符串构建示例]

因此,问题之一是您的 sql 查询将在字符串末尾以 AND 结尾,但之后没有条件...所以,可以尝试更改为这个...语法可能已关闭稍微,但应该是对的:

public IQueryable<Product> Search(Dictionary<string, string> searchParams)
{
    DBDataContext dc = new DBDataContext();

    StringBuilder sQuery = new StringBuilder();
    foreach (KeyValuePair<string, string> temp in searchParams)
    {
        if( sQuery.Length > 0 ) sQuery.Append(" AND ");
        sQuery.AppendFormat("{0}={1}",temp.Key,temp.Value);
    }

    var query = dc.Products.Where(sQuery.ToString());

    return query;
}

这只会在第一个之后的条件上使用“AND”。希望对你有帮助...

仅供参考 - 这是题外话,但“为什么”我使用 StringBuilder 是字符串连接的方式会导致字符串被破坏,并且每个循环在内存中创建一个新字符串 4 次......所以改变了到 StringBuilder ,因为这将创建一个可以填充的缓冲区,并且仅在必要时调整大小。

【讨论】:

  • 您的函数将返回IEnumerable,而不是IQueryable,因此代码无法编译。此外,数据库中的行将在 C# 代码中过滤,而不是在数据库层中。它会更慢并且需要更多内存。
  • 添加了 AsQueryable() 以便编译
  • 听起来 Athari 对 Expression 方面有所了解,但是,我不知道……所以最好听从他的回答。
【解决方案3】:

不用字典,你可以用这个方法:

using System.Linq.Expressions;
// ...

public static IQueryable<Product> Search(Expression<Func<Product, bool>> search)
{
    DBDataContext dc = new DBDataContext();
    return dc.Products.Where(search);
}

你可以这样使用它:

Search(p => p.Name == "Name" && p.Id == 0);

您也不会局限于字符串属性。

【讨论】:

  • 那么,您的意思是,对于 Linq to SQL,您需要使用 Expression 才能在数据库中执行查询?只是做 Func 将在 C# 中执行?
  • 这仍然具有我试图避免的相同固有限制。我仍然需要显式输入属性 p.Name,而不是根据字典中存在的任何键(p.[KEY] = [VALUE])动态生成它。我绝不会束缚或依附于使用字典,但在我看来,这似乎是存储要搜索的键及其值的唯一方法。
  • 好的,所以字典很重要...我添加了一个链接到我的答案中使用 MS 动态查询库使用字符串
  • 这是我得到的错误:“错误 1 ​​方法 'System.Linq.Queryable.Where(System.Linq.IQueryable, System. Linq.Expressions.Expression>)' 无法从用法中推断出来。尝试显式指定类型参数。"
【解决方案4】:

你试过为你的字符串加上单引号吗? 根据您的 foreach 循环,查询结束时会有额外的“AND”

试试这个

string sQuery = searchParam.Select(entry => string.Format("{0} = '{1}'", entry.Key, entry.Value)).Aggregate((current, next) => current + " AND " next);

【讨论】:

    【解决方案5】:

    如果您愿意创建一个可搜索术语的字典,那么我使用的方法类似于以下方法。我已经调整了我所做的更接近您的工作方式的内容,我还更改了表的名称以适合我可以使用的数据上下文/对象模型。

    这个想法是创建一个关键字列表,您的查询支持搜索。然后添加返回表达式的函数,然后可以将其传递到查询的 where 子句中。

    您当然应该为无效键等添加一些错误处理。

    public IQueryable<Person> Search(Dictionary<string, string> searchParams)
    {
        DBDataContext dc = new DBDataContext();
        var query = dc.Persons.Where(p => true); //do an 'empty predicate' because you want 'query' to be an iqueryable<Person> not a Table<Person>
    
        //build a list of the types of things you can filter on.
        var criteriaDefinitions = new Dictionary<string,Func<string,Expression<Func<Person,bool>>>>();
        criteriaDefinitions.Add("FirstName",s => p => p.FirstName == s);
        criteriaDefinitions.Add("LastName",s => p => p.LastName == s);
    
        //you can do operations other than just equals
        criteriaDefinitions.Add("EmailContains",s => p => p.Email.Contains(s));
    
        //you can even create expressions that integrate joins.
        criteriaDefinitions.Add("HasContactInCity",s => p => p.Contacts.Any(c => c.City == s));
    
    
        foreach (KeyValuePair<string, string> temp in searchParams)
        {
            //grab the correct function out of the dictionary
            var func = criteriaDefinitions[temp.Key];
    
            //evaluating the function will return an expression which can passed into the where clause.
            var expr = func(temp.Value);
            query = query.Where(expr);
        }
    
        return query;
    }
    

    【讨论】:

      【解决方案6】:

      用于 linq 搜索查询多个可选参数 可以使用此代码:

      var query = dx.GetAllJobs().Where(x => x.JobName.Contains(keyword));
      
      if (SecLink != 0)
      {
          query = query.Where(x => x.SectorLink.Equals(SecLink));
      }
      
      if (LocLink != 0)
      {
          query = query.Where(x => x.LocationLink.Equals(LocLink));
      }
      
      if (IndLink != 0)
      {
          query = query.Where(x => x.IndustryLink.Equals(IndLink));
      }
      
      if (VacLink != 0)
      {
          query = query.Where(x => x.VacancyTypeLink.Equals(VacLink));
      }
      
      var lstJobs = query.ToList();
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-03-16
        • 2021-05-01
        • 1970-01-01
        • 2013-09-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多