I'm having problems with Includes when pulling data out of the entity framework. I posted a very similar question yesterday here, which has the same sample I'm about to use. I'm using Entity Framework v4.0.
I have the following simple model, a table containing list of Page Forms (~200). Each form has one or more Fields (~4000 total), and each field has may have some Parameters (~16000 total).
I use the following code to pull data out:
EntityConnection myConnection = new EntityConnection("name=myModel");
if(conn.State != ConnectionState.Open) {
conn.Open();
}
ObjectContext context = new ObjectContext("name=myModel");
context.ContextOptions.LazyLoadingEnabled = false;
ObjectQuery<PageForm> myObjectSet = context.CreateObjectSet<PageForm>()
.Include("FormFields.FormFieldParameters");
IQueryable<PageForm> myFilteredObjectSet = myObjectSet.Where(c => c.FormID == 1);
List<PageForm> myReturnValue = myFilteredObjectSet.toList();
Which generates the following sql query
SELECT
[Project1].[FormID] AS [FormID],
[Project1].[FormName] AS [FormName],
[Project1].[C2] AS [C1],
[Project1].[FormID1] AS [FormID1],
[Project1].[FieldID] AS [FieldID],
[Project1].[FieldName] AS [FieldName],
[Project1].[C1] AS [C2],
[Project1].[FieldParamID] AS [FieldParamID],
[Project1].[Value] AS [Value],
[Project1].[FieldID1] AS [FieldID1]
FROM (
SELECT
[Extent1].[FormID] AS [FormID],
[Extent1].[FormName] AS [FormName],
[Join1].[FieldID] AS [FieldID],
[Join1].[FieldName] AS [FieldName],
[Join1].[FormID] AS [FormID1],
[Join1].[FieldParamID] AS [FieldParamID],
[Join1].[Value] AS [Value],
[Join1].[FieldID1] AS [FieldID1],
CASE WHEN ([Join1].[FieldID] IS NULL) THEN CAST(NULL AS int) WHEN ([Join1].[FieldParamID] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1],
CASE WHEN ([Join1].[FieldID] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C2]
FROM [dbo].[PageForm] AS [Extent1]
LEFT OUTER JOIN (
SELECT
[Extent2].[FieldID] AS [FieldID],
[Extent2].[FieldName] AS [FieldName],
[Extent2].[FormID] AS [FormID],
[Extent3].[FieldParamID] AS [FieldParamID],
[Extent3].[Value] AS [Value],
[Extent3].[FieldID] AS [FieldID1]
FROM [dbo].[FormField] AS [Extent2]
LEFT OUTER JOIN [dbo].[FormFieldParameter] AS [Extent3] ON [Extent2].[FieldID] = [Extent3].[FieldID]
) AS [Join1] ON [Extent1].[FormID] = [Join1].[FormID]
WHERE 1 = [Extent1].[FormID]
) AS [Project1]
ORDER BY [Project1].[FormID] ASC, [Project1].[C2] ASC, [Project1].[FieldID] ASC, [Project1].[C1] ASC
Now I'm interested in this part of the query:
LEFT OUTER JOIN (
SELECT
/**/
FROM [dbo].[FormField] AS [Extent2]
LEFT OUTER JOIN [dbo].[FormFieldParameter] AS [Extent3] ON [Extent2].[FieldID] = [Extent3].[FieldID]
) AS [Join1] ON [Extent1].[FormID] = [Join1].[FormID]
This seems to query the entire FormField and FormFieldParameter tables, with no filtering applied. So, what I think is happening, is that using .Include("FormField.FormFieldParameter")
is akin to saying - "And return all data from these tables in the resulting query". What I really want is "Return only the data from these tables which relates to the filtered PageForm table".
Is there a way to do this? Sorry if this question sounds too simple, or similar to my previous question, but I'm really having trouble getting my head around the internals of Entity Framework.
EDIT 1:
If I change the above sample to the following which includes a where clause, then the query is orders of magnitude faster (roughly 10x faster).
LEFT OUTER JOIN (
SELECT
/**/
FROM [dbo].[FormField] AS [Extent2]
LEFT OUTER JOIN [dbo].[FormFieldParameter] AS [Extent3] ON [Extent2].[FieldID] = [Extent3].[FieldID]
WHERE 1 = [Extent2].[FormID]
) AS [Join1] ON [Extent1].[FormID] = [Join1].[FormID]
EDIT 2:
Some more information. I've figured out that If i add some funky filtering to the initial query, I can force a much more efficient query to be ran.
IQueryable<PageForm> myFilteredObjectSet = myObjectSet
.Where(c => c.FormID == 1)
.Where(a => a.FormFields
.Where(c => c.FormFieldParameters
.Any(d => d.FieldID == c.FieldID))
.Any(b => b.FormID == 1)
);
This causes the query to filter correctly, and runs much faster. However, I'm certain this isn't the best way go about this, as the nested Where/Any statements very quickly become a nightmare to deal with when you have more than a handful of Includes. There must be a better way?