Скомпилированные запросы Linq и int[] в качестве параметра

Я использую следующий скомпилированный запрос LINQ to SQL.

 private static Func<MyDataContext, int[], int> MainSearchQuery =
     CompiledQuery.Compile((MyDataContext db, int[] online ) =>
              (from u in db.Users where online.Contains(u.username)
               select u));

Я знаю, что невозможно использовать входной параметр последовательности для скомпилированного запроса, и я получаю ошибку "Параметры не могут быть последовательностями" при его выполнении.

В другом сообщении здесь related я увидел, что есть какое-то решение, но Я не мог этого понять.

Кто-нибудь знает, как использовать выполненный запрос с массивом в качестве входного параметра?

Пожалуйста, опубликуйте пример, если вы это сделаете.


person dan    schedule 28.12.2009    source источник
comment
Просто читайте глубже, а ТОГДА дайте нам знать, когда вы застряли. social.msdn.microsoft .com/forums/en-US/linqtosql/thread/   -  person Hamish Grubijan    schedule 28.12.2009
comment
я сделал :(, это действительно сложно, и примеры на самом деле не связаны с простым требованием передачи массива в качестве параметра.   -  person dan    schedule 28.12.2009


Ответы (2)


Как и в сообщении, на которое вы ссылались, это невозможно из коробки. В посте также упоминается создание собственного поставщика запросов, но это немного накладно и сложно, что вам, вероятно, не нужно.

Здесь у вас есть несколько вариантов:

  1. Не используйте скомпилированный запрос. Скорее, есть метод, который создаст предложение where из каждого элемента в массиве, что приведет к чему-то вроде этого (псевдо-код):

    where 
        online[0] == u.username ||
        online[1] == u.username ||
        ...
        online[n] == u.username
    

    Обратите внимание, что здесь вам придется использовать выражение для создания каждого предложения ИЛИ.

  2. Если вы используете SQL Server 2008, создайте скалярную функцию, которая будет принимать таблицу параметр со значением и значение для повторного сравнения. Он вернет бит (чтобы указать, находится ли элемент в значениях в таблице). Затем предоставьте эту функцию через LINQ-to-SQL в вашем контексте данных. Оттуда вы сможете создать для этого CompiledQuery. Обратите внимание, что в этом случае вы должны взять IEnumerable<string> (при условии, что имя пользователя имеет тип string) вместо массива, просто потому, что у вас может быть более одного способа представления последовательности строк, и для SQL-сервера для этой операции он выиграл неважно, какой порядок.

person casperOne    schedule 28.12.2009
comment
Спасибо! я не мог заставить ни один из них работать. 1) если я не использую скомпилированный запрос, у меня вообще нет проблем, я могу просто передать int[] в качестве параметра. может быть, я не понял, что вы имели в виду. 2) я читал о table-value , но думаю, что ошибаюсь. я создал скалярную функцию, которая получает параметр табличного значения, но что она возвращает? - person dan; 29.12.2009
comment
@dan 1) Если вы вообще не используете скомпилированный запрос, вы все равно не можете использовать «Содержит». Вы должны разделить запрос на несколько операторов ИЛИ. 2) Скалярная функция с параметром с табличным значением вернет бит, который равен 0, если элемент не содержится в таблице, которую вы передаете, 1, если это так. - person casperOne; 29.12.2009

Одно решение, которое я нашел для себя (для MS SQL 2005/2008). И я не уверен, что во всех сценариях уместно просто написать динамический sql и выполнить его в контексте данных, используя метод ExecuteQuery.

Например, если у меня есть неограниченный список, который я пытаюсь передать в запрос для выполнения содержащего...

' Mock a list of values
Dim ids as New List(of Integer)
ids.Add(1)
ids.Add(2)
' ....
ids.Add(1234)

Dim indivs = (From c In context.Individuals _
                    Where ids.Contains(c.Id) _
                    Select c).ToList

Я бы изменил этот запрос, чтобы создать строку SQL для выполнения непосредственно в базе данных, например...

Dim str As New Text.StringBuilder("")
Dim declareStmt as string = "declare @ids table (indivId int) "  & vbcrlf)

For i As Integer = 0 To ids.Count - 1

     str.Append("select " & ids(i).ToString() & " & vbcrlf)

     If i < ids.Count Then
          str.Append("union " & vbcrlf)
     End If

Next

Dim selStatement As String = "select * From " & context.Mapping.GetTable(GetType(Individuals)).TableName & _
      " indiv " & vbcrlf & _
      " inner join @ids ids on indiv.id = ids.id"

Dim query = declareStmt & str.ToString & selStatement
Dim result = context.ExecuteQuery(of Individual)(query).ToList

Таким образом, за исключением любых синтаксических ошибок или ошибок, которые я закодировал (вышеприведенный код является более или менее псевдокодом и не тестировался), вышеприведенное сгенерирует табличную переменную в SQL и выполнит внутреннее соединение с желаемой таблицей (Индивидуальные в этом примере) и избежит использование оператора «IN».

Надеюсь, это поможет кому-то!

person Amit    schedule 28.04.2010