Telerik's RadGridView + WCF Data Services + Entity Framework = Terrible performance
-
10-02-2021 - |
문제
We have a simple LOB application that:
- pulls data from EF
- serves data across the wire with WCF Data Services
- renders that data on Telerik's RadGridView
This works really well in the default scenario as users are able to filter data by using the built-in Telerik filter control which presents all the options they want.
The problem happens when re-constructing the query sent from WCF Data Services when the 'Contains' operator is used:
- WCF Data Services adds a bunch of "IIF" lambda expressions which,
- EF then expands into T-SQL CASE statements
This takes a query that should look like:
SELECT TOP (25)
[Project1].[TaskID] AS [TaskID],
[Project1].[ProductSubmissionID] AS [ProductSubmissionID],
...
FROM ( SELECT
[Extent1].[TaskID] AS [TaskID],
[Extent1].[ProductSubmissionID] AS [ProductSubmissionID],
...
FROM [dbo].[Task] AS [Extent1]
LEFT OUTER JOIN [dbo].[OperationDataProduct] AS [Extent2] ON [Extent1].[ProductID] = [Extent2].[ProductID]
LEFT OUTER JOIN [dbo].[vProductOwnership] AS [Extent3] ON [Extent1].[ProductID] = [Extent3].[ProductID]
LEFT OUTER JOIN [dbo].[User] AS [Extent4] ON [Extent2].[ChannelManagerID] = [Extent4].[UserID]
LEFT OUTER JOIN [dbo].[User] AS [Extent5] ON [Extent2].[ProductOwnerID] = [Extent5].[UserID]
WHERE [Extent1].Type IN ('Content','Concept','Financial') AND [Extent1].MarketplaceName LIKE '%prod%'
Into one that looks like this:
SELECT TOP (25)
[Project1].[TaskID] AS [TaskID],
[Project1].[ProductSubmissionID] AS [ProductSubmissionID],
...
FROM ( SELECT
[Extent1].[TaskID] AS [TaskID],
[Extent1].[ProductSubmissionID] AS [ProductSubmissionID],
...
FROM [dbo].[Task] AS [Extent1]
LEFT OUTER JOIN [dbo].[OperationDataProduct] AS [Extent2] ON [Extent1].[ProductID] = [Extent2].[ProductID]
LEFT OUTER JOIN [dbo].[vProductOwnership] AS [Extent3] ON [Extent1].[ProductID] = [Extent3].[ProductID]
LEFT OUTER JOIN [dbo].[User] AS [Extent4] ON [Extent2].[ChannelManagerID] = [Extent4].[UserID]
LEFT OUTER JOIN [dbo].[User] AS [Extent5] ON [Extent2].[ProductOwnerID] = [Extent5].[UserID]
WHERE (CASE WHEN (CASE WHEN (((CASE WHEN (CASE WHEN ([Extent1].[MarketplaceName] IS NULL) THEN CAST(NULL AS varchar(1)) ELSE LOWER([Extent1].[MarketplaceName]) END IS NULL) THEN CAST(NULL AS bit) WHEN (CASE WHEN ([Extent1].[MarketplaceName] IS NULL) THEN CAST(NULL AS varchar(1)) ELSE LOWER([Extent1].[MarketplaceName]) END LIKE N'%prod%') THEN cast(1 as bit) WHEN ( NOT (CASE WHEN ([Extent1].[MarketplaceName] IS NULL) THEN CAST(NULL AS varchar(1)) ELSE LOWER([Extent1].[MarketplaceName]) END LIKE N'%prod%')) THEN cast(0 as bit) END) = 1) AND ([Extent1].[SubmissionOrTaskType] IN (N'Content',N'Concept',N'Financial'))) THEN cast(1 as bit) WHEN ( NOT (((CASE WHEN (CASE WHEN ([Extent1].[MarketplaceName] IS NULL) THEN CAST(NULL AS varchar(1)) ELSE LOWER([Extent1].[MarketplaceName]) END IS NULL) THEN CAST(NULL AS bit) WHEN (CASE WHEN ([Extent1].[MarketplaceName] IS NULL) THEN CAST(NULL AS varchar(1)) ELSE LOWER([Extent1].[MarketplaceName]) END LIKE N'%prod%') THEN cast(1 as bit) WHEN ( NOT (CASE WHEN ([Extent1].[MarketplaceName] IS NULL) THEN CAST(NULL AS varchar(1)) ELSE LOWER([Extent1].[MarketplaceName]) END LIKE N'%prod%')) THEN cast(0 as bit) END) = 1) AND ([Extent1].[SubmissionOrTaskType] IN (N'Content',N'Concept',N'Financial')))) THEN cast(0 as bit) END IS NULL) THEN cast(0 as bit) WHEN (((CASE WHEN (CASE WHEN ([Extent1].[MarketplaceName] IS NULL) THEN CAST(NULL AS varchar(1)) ELSE LOWER([Extent1].[MarketplaceName]) END IS NULL) THEN CAST(NULL AS bit) WHEN (CASE WHEN ([Extent1].[MarketplaceName] IS NULL) THEN CAST(NULL AS varchar(1)) ELSE LOWER([Extent1].[MarketplaceName]) END LIKE N'%prod%') THEN cast(1 as bit) WHEN ( NOT (CASE WHEN ([Extent1].[MarketplaceName] IS NULL) THEN CAST(NULL AS varchar(1)) ELSE LOWER([Extent1].[MarketplaceName]) END LIKE N'%prod%')) THEN cast(0 as bit) END) = 1) AND ([Extent1].[SubmissionOrTaskType] IN (N'Content',N'Concept',N'Financial'))) THEN cast(1 as bit) WHEN ( NOT (((CASE WHEN (CASE WHEN ([Extent1].[MarketplaceName] IS NULL) THEN CAST(NULL AS varchar(1)) ELSE LOWER([Extent1].[MarketplaceName]) END IS NULL) THEN CAST(NULL AS bit) WHEN (CASE WHEN ([Extent1].[MarketplaceName] IS NULL) THEN CAST(NULL AS varchar(1)) ELSE LOWER([Extent1].[MarketplaceName]) END LIKE N'%prod%') THEN cast(1 as bit) WHEN ( NOT (CASE WHEN ([Extent1].[MarketplaceName] IS NULL) THEN CAST(NULL AS varchar(1)) ELSE LOWER([Extent1].[MarketplaceName]) END LIKE N'%prod%')) THEN cast(0 as bit) END) = 1) AND ([Extent1].[SubmissionOrTaskType] IN (N'Content',N'Concept',N'Financial')))) THEN cast(0 as bit) END) = 1
) AS [Project1]
ORDER BY [Project1].[TaskID] ASC
My question is: has anyone bumped into this problem before and is there a low cost solution?
I can see re-writing the EF QueryProvider to be a solution, but it's not exactly low cost.
TIA
해결책
We wound up writing a QueryProvider to fix this issue. It wound up being lower cost than originally expected, but still adds quiet a bit of unnecessary complexity. The IQToolkit has some decent examples of how to begin this.