2012-03-13 4 views
3

Я решил свою проблему, но потому, что я пытался и не смог ее решить раньше, и на этот раз мне потребовалось немало усилий, я хотел опубликовать вопрос , и если никто не имеет лучшего ответа, мое решение, поэтому я не забываю, как это сделать в будущем, и помогать кому-либо другому сталкиваться с подобной проблемой. Моя задача заключается в следующем:Как объединить запросы LINQ-to-SQL оптимально

У меня есть функция, используемая для фильтрации списка опций для возврата только те, которые много прослежена, и, когда это применимо, также показывают только элементы, соответствующие заданному шаблону:

Private Shared Function FilterResultsLot(ByVal source As IQueryable(Of Item), _ 
    ByVal itemCode As String) As IQueryable(Of Item) 
    If HasFilter(itemCode, True) Then 
     Return From row In source Where row.ItemDetail.IsLotTraced AndAlso _ 
       row.ItemCode.ToLower() Like itemCode.ToLower() 
    Else 
     Return From row In source Where row.ItemDetail.IsLotTraced 
    End If 
    End Function 

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

Private Shared Function FilterResults(source As IQueryable(Of Item), _ 
    itemCode As String, filter As Func(Of Item, Boolean)) As IQueryable(Of Item) 
    Dim predicate As Func(Of Item, Boolean) 
    If HasFilter(itemCode, True) Then 
     predicate = Function(row) row.ItemCode.ToLower() Like itemCode.ToLower() AndAlso filter.Invoke(row) 
    Else 
     predicate = filter 
    End If 
    Return source.Where(predicate).AsQueryable() 
    End Function 

и эта функция работает через LINQ-to-SQL, но она теряет значительную оптимизацию , В то время как первая функция будет выполнить SQL запрос заканчивая:

FROM [dbo].[OITM] AS [t0] 
LEFT OUTER JOIN [dbo].[FSE_ItemDetail] AS [t1] ON [t1].[ItemCode] = [t0].[ItemCode] 
WHERE ([t1].[IsLotTraced] = 1) AND (LOWER([t0].[ItemCode]) LIKE @p0 ESCAPE ''~'') 

Второй будет выполнять несколько запросов SQL с этими окончаниями (и потенциально гораздо больше):

FROM [dbo].[OITM] AS [t0] 

FROM [dbo].[FSE_ItemDetail] AS [t0] 
WHERE [t0].[ItemCode] = @p0',N'@p0 nvarchar(4000)',@p0=N'BF-BIKE' 

FROM [dbo].[FSE_ItemDetail] AS [t0] 
WHERE [t0].[ItemCode] = @p0',N'@p0 nvarchar(4000)',@p0=N'BF-BIKE-ITEM' 

Таким образом, вопрос в том, как объединить выражения делегата LINQ-to-SQL в одно без вызова Invoke или AsQueryable или без потери оптимального исполнения.

+0

Если бы это был C#, я бы попытался заменить все использования 'Func <>' с 'Expression >', поскольку компилятор может перевести лямбда в любой. Это относится и к VB? Можете ли вы назначить выражение встроенной функции выражению 'Expression (Of Func (Of whatever))? – AakashM

+0

Да, я думаю, я пробовал это, но все еще не знал, как вызвать выражение. Вы заметите, что решение, которое я придумал, делает именно то, что вы предлагаете, но также находит способ «вызывать» выражение (объединить его в дерево выражений Queryable), которое было частью, которую я отсутствовал. – BlueMonkMN

ответ

1

Вот мое текущее решение. Прокомментируйте, пожалуйста, или предоставить лучшие решения, если у вас есть какие-либо:

Private Shared Function FilterResults(source As IQueryable(Of Item), itemCode As String, filter As Expressions.Expression(Of Func(Of Item, Boolean))) As IQueryable(Of Item) 
    If HasFilter(itemCode, True) Then 
     Return Queryable.Where(Queryable.Where(source, Function(row) row.ItemCode.ToLower() Like itemCode.ToLower()), filter) 
    Else 
     Return Queryable.Where(source, filter) 
    End If 
    End Function 

Edit:

После работы с ним немного больше, я предпочитаю упрощение/альтернативы этому синтаксису:

Return Queryable.Where(Queryable.Where(source, _ 
    Function(row) row.ItemCode.ToLower() Like itemCode.ToLower()), filter) 

Мой предпочтение:

Return Queryable.Where(From row in source _ 
    Where row.ItemCode.ToUpper() Like itemCode.ToUpper(), filter) 

(I a lso предложить ToUpper вместо ToLower, чтобы принудительно не учитывать регистр на сервере, зависящем от регистра, потому что я слышал, что UPPER лучше оптимизирован для этого.)