实体框架4排除了我认为不应该的记录。这是我的情景:
表定义
Table_1
UniqueIdentifier ID not null
int AnotherField
Table_2
UniqueIdentifier ID not null
UniqueIdentifier Table_1ID not null
int Priority not null
表1和表2之间通过table_1ID字段存在关系。它在数据库中定义,实体框架识别它。
我在DomainService中定义了一个Query:
private ObjectQuery<Table_1> Table_1WithIncludes()
{
return this.ObjectContext.Table_1
.Include("Table_2")
}
如果我在表1和表2中有相关的记录,则会按预期返回。如果我在表2中没有与表1相关的记录,则该记录将被排除。
运行SQL Server Profiler后,我注意到实体框架添加了以下CAST和where子句:
CASE WHEN ([Join2].[Priority] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1]
WHERE ([Project1].[C1] > 0)
基本上,如果Table_2中的Priority字段为null(表2中没有与父Tabe_1表对应的记录),where子句会导致两个记录都丢失(null> 0失败)。
现在,如果我更改优先级字段的定义以使其不是必需的,它会消除演员表和支票,一切都很好。但似乎我不应该这样做。
我做错了什么吗?我的理解有问题吗?
完整的EntityQuery和生成的查询文本如下,以防有用。
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'
表定义
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
答案 0 :(得分:0)
如果您的外键是非空的,那么根据EF,您的关系是一对多,有些孩子必须有父。所以理论上你不能没有父母的孩子。
如果你想让孩子没有父母,那么你的关系是零或一对多,你的外键应该是空的。
答案 1 :(得分:0)
我在实体框架中看到的单个Include
始终被翻译为外部联接。但是,对于多个包含,包含的顺序决定了连接是内部还是外部。
在你的例子中,我假设“Place”是外连接的,而“EventInvitees”和“User”都是内连接的。 (至少那是我做类似查询时会发生的事情)。
如果您要将订单更改为
.Include("EventInvitees")
.Include("EventInvitees.User")
.Include("Place")
“Place”将是内部连接,“EventInvitees”外部连接(再次,基于我的类似情况)。
我在MSDN上找不到关于此的确切逻辑的任何文档,因此您的结果可能与我的不同。