Несколько параметров поиска с LINQ - PullRequest
8 голосов
/ 28 октября 2010

Я пишу то, что, по моему мнению, должно быть относительно простым приложением Windows Form. Я использую LINQ to SQL, хотя никогда не использовал его раньше. У нас есть база данных SQL Server, и я создаю интерфейс для доступа к этой базе данных. Я пытаюсь найти наиболее эффективный способ поиска нескольких (произвольное количество) параметров поиска с его помощью.

В форме окна я создаю словарь с каждым поисковым ключом и его значением для поиска и передаю его в метод 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).

EDIT

Итак, я немного пересмотрел, но у меня все еще есть проблемы с этим. Вот что у меня сейчас есть:

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>)' не могут быть выведены из использования. Попробуйте указать аргументы типа явно.

Ответы [ 5 ]

3 голосов
/ 29 октября 2010

Вот пример, который работает (я только что проверил), используя Динамическую библиотеку запросов LINQ .

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
2 голосов
/ 28 октября 2010

Если по какой-либо причине словарь не требуется, я бы применил ваш метод поиска следующим образом:

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

Лично я, как правило, предпочел бы подход с типобезопасным выражением>, так как это даст вам ошибки времени компиляции ... но если нужны строки, то выглядит наилучшим способом сделать это.

Исходя из вышеуказанной ссылки, похоже, что вы должны сделать:

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

Это будет использовать только «И» в условиях после первого. Надеюсь, это поможет ...

К вашему сведению - это не по теме, но «почему», я использовал StringBuilder, заключается в том, что конкатенация строк так, как вы это делали, приводила к тому, что строка уничтожалась и новая строка создавалась в памяти 4 раза за цикл ... так изменилось в StringBuilder, так как это создаст буфер, который может быть заполнен и изменен только при необходимости.

0 голосов
/ 02 ноября 2010

, если вы хотите создать словарь поисковых терминов, чем я использовал что-то близкое к подходу ниже.Я адаптировал то, что я делаю, чтобы он более точно подходил к тому, как вы работали, я также изменил имена таблиц, чтобы они соответствовали имеющейся у меня модели данных / текстовых данных

Идея состоит в том, чтобы создать ключевой список терминов, по которым ваш запрос поддерживает поиск.Затем добавьте функции, которые возвращают выражение, которое затем можно передать в предложение 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;
}
0 голосов
/ 01 ноября 2010

Вы пытались поставить одинарную кавычку для вашей строки?в зависимости от цикла foreach в конце запроса будет добавлено «И»

, попробуйте вместо этого

string sQuery = searchParam.Select(entry => string.Format("{0} = '{1}'", entry.Key, entry.Value)).Aggregate((current, next) => current + " AND " next);
0 голосов
/ 28 октября 2010

Вместо использования словаря вы можете использовать этот метод:

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

Вы также не будете ограничены строковыми свойствами.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...