[英]How can I code an outer join using LINQ and EF6?
我有三個表Exam,Test和UserTest。
CREATE TABLE [dbo].[Exam] (
[ExamId] INT IDENTITY (1, 1) NOT NULL,
[SubjectId] INT NOT NULL,
[Name] NVARCHAR (50) NOT NULL,
[Description] NVARCHAR (MAX) NOT NULL,
CONSTRAINT [PK_Exam] PRIMARY KEY CLUSTERED ([ExamId] ASC),
CONSTRAINT [FK_ExamSubject] FOREIGN KEY ([SubjectId]) REFERENCES [dbo].[Subject] ([SubjectId]),
CONSTRAINT [FK_Exam_ExamType] FOREIGN KEY ([ExamTypeId]) REFERENCES [dbo].[ExamType] ([ExamTypeId])
);
CREATE TABLE [dbo].[Test] (
[TestId] INT IDENTITY (1, 1) NOT NULL,
[ExamId] INT NOT NULL,
[Title] NVARCHAR (100) NULL,
[Status] INT NOT NULL,
[CreatedDate] DATETIME NOT NULL,
CONSTRAINT [PK_Test] PRIMARY KEY CLUSTERED ([TestId] ASC),
CONSTRAINT [FK_TestExam] FOREIGN KEY ([ExamId]) REFERENCES [dbo].[Exam] ([ExamId])
);
CREATE TABLE [dbo].[UserTest] (
[UserTestId] INT IDENTITY (1, 1) NOT NULL,
[UserId] NVARCHAR (128) NOT NULL,
[TestId] INT NOT NULL,
[Result] INT NULL
CONSTRAINT [PK_UserTest] PRIMARY KEY CLUSTERED ([UserTestId] ASC),
CONSTRAINT [FK_UserTestTest] FOREIGN KEY ([TestId]) REFERENCES [dbo].[Test] ([TestId])
);
考試可以進行多次測試,用戶可以多次嘗試任何測試。
如何使用擴展方法語法對LINQ語句進行編碼,該語法允許我查看UserId == 1的以下內容(我假設Where子句中的UserId == 1):
Exam Test Title UserTestID UserId Result
1 1 1a 1 1 20
1 1 1a 2 1 30
1 1 1a 3 1 40
1 2 1b 4 1 98
1 3 1c 5 1 44
2 4 2a
2 5 2b 6 1 12
或者如果UserId == 2:
Exam Test Title UserTestID UserId Result
1 1 1a 7 2 27
1 2 1b
1 3 1c 8 2 45
2 4 2a
2 5 2b
或者如果UserId為null
Exam Test Title UserTestID UserId Result
1 1 1a
1 2 1b
1 3 1c
2 4 2a
2 5 2b
請注意,由於我收到的建議,這個問題已經發生了一些變化。 現在有一個賞金,我希望我能接受的快速回答。
如果您的Test
實體具有UserTests
集合,則可以使用此查詢:
string userId = "1";
var result = context.Tests
.SelectMany(t => t.UserTests
.Where(ut => ut.UserId == userId)
.DefaultIfEmpty()
.Select(ut => new
{
ExamId = t.ExamId,
TestId = t.TestId,
Title = t.Title,
UserTestId = (int?)ut.UserTestId,
UserId = ut.UserId,
Result = ut.Result
}))
.OrderBy(x => x.ExamId)
.ThenBy(x => x.TestId)
.ThenBy(x => x.UserTestId)
.ToList();
在此處使用DefaultIfEmpty()
可確保LEFT OUTER JOIN
以便您始終為給定的Test
提供至少一個UserTest
實體(可能為null
)。 將UserTest
的非可空屬性(如UserTestId
)轉換為可空類型(例如int?
)非常重要,否則您可以獲得一個異常,即從數據庫返回的NULL
值不能存儲在非可空的.NET類型。
如果你沒有,不想一個UserTests
在你收集Test
實體可以使用GroupJoin
它們基本上將左外連接由兩個表的替代TestId
:
string userId = "1";
var result = context.Tests
.GroupJoin(context.UserTests.Where(ut => ut.UserId == userId),
t => t.TestId,
ut => ut.TestId,
(t, utCollection) => new
{
Test = t,
UserTests = utCollection
})
.SelectMany(x => x.UserTests
.DefaultIfEmpty()
.Select(ut => new
{
ExamId = x.Test.ExamId,
TestId = x.Test.TestId,
Title = x.Test.Title,
UserTestId = (int?)ut.UserTestId,
UserId = ut.UserId,
Result = ut.Result
}))
.OrderBy(x => x.ExamId)
.ThenBy(x => x.TestId)
.ThenBy(x => x.UserTestId)
.ToList();
var tests = (from t in context.Tests
// where !t.UsertTests.Any() //if no user took the test
// || t.UserTests.Any(ut=>ut.Student.StudentId == stId)
select new {Test = t, Exam = t.Exam,
UserTests = t.UserTests.Where(ut=>ut.Student.StudentId == stId))
.ToList();
第二個想法,可能會更好。 如果有任何匹配的或空的用戶,這將為您提供考試,測試和使用習慣
以下是討論的鏈接,該討論顯示了如何使用參數調用存儲過程: 如何在存儲過程中使用DbContext.Database.SqlQuery <TElement>(sql,params)? EF Code First CTP5
以下是編寫存儲過程的一種方法:
CREATE PROCEDURE dbo.sample1 (
@oneId NVARCHAR(128) = N'xx') AS
BEGIN
SET NOCOUNT ON;
SELECT @oneId AS userId,
r.TestId,
r.Result
FROM (
SELECT t.UserId, e.testId, t.Result
FROM dbo.UserTest AS e
LEFT OUTER JOIN dbo.UserTest AS t ON e.TestId = t.TestId AND t.UserId = @oneId
WHERE e.UserId = 0) AS r
ORDER BY r.TestId
END
go
試試這個:
var tests = context.Tests.Include( "Exam" )
.Select( t => new
{
Test = t,
UserTests = t.UserTests.Where( ut => ut.UserId == studentId )
} )
.ToList();
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.