Entity Framework 4 is excluding records that I don't believe it should be. Here is my scenario:
Table Definitions
Table_1
UniqueIdentifier ID not null
int AnotherField
Table_2
UniqueIdentifier ID not null
UniqueIdentifier Table_1ID not null
int Priority not null
There is a relationship between table 1 and table 2 through the table_1ID field. It's defined in the database and the entity framework recognizes it.
I have a Query defined in my DomainService as:
private ObjectQuery<Table_1> Table_1WithIncludes()
{
return this.ObjectContext.Table_1
.Include("Table_2")
}
If I have a record in table 1 and table 2 that are related, they get returned as expected. If I do NOT have a record in table 2 that relates back to table 1, then the record is getting excluded.
Upon running SQL Server Profiler, I noticed that the entity framework added the following CAST and where clauses:
CASE WHEN ([Join2].[Priority] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1]
WHERE ([Project1].[C1] > 0)
So essentially, if the Priority field in Table_2 is null (which it is when there isn't a record in Table 2 that corresponds to the parent Tabe_1 table) the where clause causes both records to drop out (null > 0 fails).
Now, if I change the definition of the Priority field so that it is not required, it eliminates the cast and the check and all is well. But it doesn't seem like I should have to do this.
Have I done something wrong? Is my understanding faulty?
The full EntityQuery and generated query text is below in case it is helpful.
private ObjectQuery<Event> EventsWithIncludes()
{
return this.ObjectContext.Events
.Include("Place")
.Include("EventInvitees")
.Include("EventInvitees.User");
}
exec sp_executesql N'SELECT
[Project2].[NumberOfPeople] AS [NumberOfPeople],
[Project2].[ID] AS [ID],
[Project2].[CreatorID] AS [CreatorID],
[Project2].[CreateDate] AS [CreateDate],
[Project2].[PlacesID] AS [PlacesID],
[Project2].[EventDate] AS [EventDate],
[Project2].[EventTime] AS [EventTime],
[Project2].[Availability] AS [Availability],
[Project2].[EscalationLevels] AS [EscalationLevels],
[Project2].[Rank] AS [Rank],
[Project2].[RowVersion] AS [RowVersion],
[Project2].[ID1] AS [ID1],
[Project2].[Name] AS [Name],
[Project2].[Phone] AS [Phone],
[Project2].[DefaultPar] AS [DefaultPar],
[Project2].[DefaultSlope] AS [DefaultSlope],
[Project2].[DefaultRating] AS [DefaultRating],
[Project2].[CreateDate1] AS [CreateDate1],
[Project2].[UpdateDate] AS [UpdateDate],
[Project2].[MetroAreaID] AS [MetroAreaID],
[Project2].[C1] AS [C1],
[Project2].[PriorityOrder] AS [PriorityOrder],
[Project2].[ID2] AS [ID2],
[Project2].[EventsID] AS [EventsID],
[Project2].[InviteeUsersID] AS [InviteeUsersID],
[Project2].[RowVersion1] AS [RowVersion1],
[Project2].[Attending] AS [Attending],
[Project2].[StatusChange] AS [StatusChange],
[Project2].[ID3] AS [ID3],
[Project2].[First] AS [First],
[Project2].[Last] AS [Last],
[Project2].[UserName] AS [UserName],
[Project2].[Password] AS [Password],
[Project2].[Gender] AS [Gender],
[Project2].[Email] AS [Email],
[Project2].[Email_Sharing] AS [Email_Sharing],
[Project2].[Email_Receive] AS [Email_Receive],
[Project2].[Phone1] AS [Phone1],
[Project2].[Phone_Sharing] AS [Phone_Sharing],
[Project2].[Phone_Receive] AS [Phone_Receive],
[Project2].[CreateDate2] AS [CreateDate2],
[Project2].[UpdateDate1] AS [UpdateDate1]
FROM ( SELECT
[Project1].[ID] AS [ID],
[Project1].[CreatorID] AS [CreatorID],
[Project1].[CreateDate] AS [CreateDate],
[Project1].[NumberOfPeople] AS [NumberOfPeople],
[Project1].[PlacesID] AS [PlacesID],
[Project1].[EventDate] AS [EventDate],
[Project1].[Availability] AS [Availability],
[Project1].[EscalationLevels] AS [EscalationLevels],
[Project1].[Rank] AS [Rank],
[Project1].[RowVersion] AS [RowVersion],
[Project1].[EventTime] AS [EventTime],
[Extent3].[ID] AS [ID1],
[Extent3].[Name] AS [Name],
[Extent3].[Phone] AS [Phone],
[Extent3].[DefaultPar] AS [DefaultPar],
[Extent3].[DefaultSlope] AS [DefaultSlope],
[Extent3].[DefaultRating] AS [DefaultRating],
[Extent3].[CreateDate] AS [CreateDate1],
[Extent3].[UpdateDate] AS [UpdateDate],
[Extent3].[MetroAreaID] AS [MetroAreaID],
[Join2].[ID1] AS [ID2],
[Join2].[EventsID] AS [EventsID],
[Join2].[InviteeUsersID] AS [InviteeUsersID],
[Join2].[PriorityOrder] AS [PriorityOrder],
[Join2].[RowVersion] AS [RowVersion1],
[Join2].[Attending] AS [Attending],
[Join2].[StatusChange] AS [StatusChange],
[Join2].[ID2] AS [ID3],
[Join2].[First] AS [First],
[Join2].[Last] AS [Last],
[Join2].[UserName] AS [UserName],
[Join2].[Password] AS [Password],
[Join2].[Gender] AS [Gender],
[Join2].[Email] AS [Email],
[Join2].[Email_Sharing] AS [Email_Sharing],
[Join2].[Email_Receive] AS [Email_Receive],
[Join2].[Phone] AS [Phone1],
[Join2].[Phone_Sharing] AS [Phone_Sharing],
[Join2].[Phone_Receive] AS [Phone_Receive],
[Join2].[CreateDate] AS [CreateDate2],
[Join2].[UpdateDate] AS [UpdateDate1],
CASE WHEN ([Join2].[PriorityOrder] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1]
FROM (SELECT
[Extent1].[ID] AS [ID],
[Extent1].[CreatorID] AS [CreatorID],
[Extent1].[CreateDate] AS [CreateDate],
[Extent1].[NumberOfPeople] AS [NumberOfPeople],
[Extent1].[PlacesID] AS [PlacesID],
[Extent1].[EventDate] AS [EventDate],
[Extent1].[Availability] AS [Availability],
[Extent1].[EscalationLevels] AS [EscalationLevels],
[Extent1].[Rank] AS [Rank],
[Extent1].[RowVersion] AS [RowVersion],
[Extent1].[EventTime] AS [EventTime],
(SELECT
COUNT(1) AS [A1]
FROM [dbo].[EventInvitees] AS [Extent2]
WHERE ([Extent1].[ID] = [Extent2].[EventsID]) AND ([Extent2].[InviteeUsersID] = @p__linq__2)) AS [C1]
FROM [dbo].[Events] AS [Extent1] ) AS [Project1]
LEFT OUTER JOIN [dbo].[Places] AS [Extent3] ON [Project1].[PlacesID] = [Extent3].[ID]
LEFT OUTER JOIN (SELECT [Extent4].[ID] AS [ID1], [Extent4].[EventsID] AS [EventsID], [Extent4].[InviteeUsersID] AS [InviteeUsersID], [Extent4].[PriorityOrder] AS [PriorityOrder], [Extent4].[RowVersion] AS [RowVersion], [Extent4].[Attending] AS [Attending], [Extent4].[StatusChange] AS [StatusChange], [Extent5].[ID] AS [ID2], [Extent5].[First] AS [First], [Extent5].[Last] AS [Last], [Extent5].[UserName] AS [UserName], [Extent5].[Password] AS [Password], [Extent5].[Gender] AS [Gender], [Extent5].[Email] AS [Email], [Extent5].[Email_Sharing] AS [Email_Sharing], [Extent5].[Email_Receive] AS [Email_Receive], [Extent5].[Phone] AS [Phone], [Extent5].[Phone_Sharing] AS [Phone_Sharing], [Extent5].[Phone_Receive] AS [Phone_Receive], [Extent5].[CreateDate] AS [CreateDate], [Extent5].[UpdateDate] AS [UpdateDate]
FROM [dbo].[EventInvitees] AS [Extent4]
INNER JOIN [dbo].[Users] AS [Extent5] ON [Extent4].[InviteeUsersID] = [Extent5].[ID] ) AS [Join2] ON [Project1].[ID] = [Join2].[EventsID]
WHERE ([Project1].[C1] > 0) AND ([Project1].[CreatorID] = @p__linq__0) AND (([Project1].[EventDate] IS NULL) OR ([Project1].[EventDate] >= @p__linq__1))
) AS [Project2]
ORDER BY [Project2].[ID] ASC, [Project2].[ID1] ASC, [Project2].[C1] ASC',N'@p__linq__2 uniqueidentifier,@p__linq__0 uniqueidentifier,@p__linq__1 datetime2(7)',@p__linq__2='33BB8199-7B25-4B3A-B96D-044EB7DB70AE',@p__linq__0='33BB8199-7B25-4B3A-B96D-044EB7DB70AE',@p__linq__1='1900-01-01 00:00:00'
Table Definition
USE [TheGreen18]
GO
/****** Object: Table [dbo].[EventInvitees] Script Date: 03/17/2012 22:27:16 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[EventInvitees](
[ID] [uniqueidentifier] NOT NULL,
[EventsID] [uniqueidentifier] NOT NULL,
[InviteeUsersID] [uniqueidentifier] NOT NULL,
[PriorityOrder] [int] NOT NULL,
[RowVersion] [timestamp] NULL,
[Attending] [bit] NULL,
[StatusChange] [datetime] NULL,
CONSTRAINT [PK_EventInvitees] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[EventInvitees] WITH CHECK ADD CONSTRAINT [FK_EventInvitees_Events] FOREIGN KEY([EventsID])
REFERENCES [dbo].[Events] ([ID])
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[EventInvitees] CHECK CONSTRAINT [FK_EventInvitees_Events]
GO
ALTER TABLE [dbo].[EventInvitees] WITH CHECK ADD CONSTRAINT [FK_EventInvitees_Users] FOREIGN KEY([InviteeUsersID])
REFERENCES [dbo].[Users] ([ID])
GO
ALTER TABLE [dbo].[EventInvitees] CHECK CONSTRAINT [FK_EventInvitees_Users]
GO
If your foreign key is non null able, then your relation is one to many according to EF, some every child must have parent. So theoretically you can not have child without parent.
If you want to have child without parent, then your relation is zero or one to many, and your foreign key should be null able.
What I've seen of Entity Framework a single Include
is always translated as an outer join. With multiple Includes, however, the order of the includes determines whether the joins are inner or outer.
In your example I assume that "Place" is outer joined, whereas both "EventInvitees" and "User" are inner joined. (At least that's what happens when I do a similar query).
If you would change the order into
.Include("EventInvitees")
.Include("EventInvitees.User")
.Include("Place")
"Place" would be inner joined and "EventInvitees" outer joined (again, based on my similar case).
I cannot find any documentation on the exact logic of this at MSDN, so your result may differ from mine.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.