EF 6 - Truy vấn Linq-to-Entities
Truy vấn Linq-to-Entities
Trong bài này chúng ta sẽ học cách viết các truy vấn LINQ-to-Entities và nhận kết quả trong Entity Framework 6.x cũng như trong Entity Framework Core. Truy cập Hướng dẫn LINQ để tìm hiểu LINQ.
Lớp DbSet kế thừa từ lớp IQuerayable. Vì vậy, chúng ta có thể sử dụng LINQ để truy vấn đối với DbSet, sẽ được chuyển đổi thành truy vấn SQL. EF API thực thi truy vấn SQL này đến cơ sở dữ liệu, nhận tập kết quả phẳng, chuyển đổi nó thành các đối tượng thực thể thích hợp và trả về nó dưới dạng kết quả truy vấn.
Sau đây là một số toán tử truy vấn chuẩn (hoặc phương thức mở rộng) có thể được sử dụng với các truy vấn LINQ-to-Entities.
Một số phương thức mở rộng của LINQ |
---|
First() |
FirstOrDefault() |
Single() |
SingleOrDefault() |
ToList() |
Count() |
Min() |
Max() |
Last() |
LastOrDefault() |
Average() |
Phương thức Find()
Ngoài các phương thức mở rộng LINQ, chúng ta có thể sử dụng phương thức Find () của DbSet để tìm kiếm thực thể dựa trên giá trị khóa chính.
Giả sử rằng SchoolDbEntities là lớp DbContext và Stduents là thuộc tính DbSet.
var ctx = new SchoolDBEntities();
var student = ctx.Students.Find(1);
Trong ví dụ trên, ctx.Student.Find (1) trả về một bản ghi sinh viên có StudentId là 1 trong cơ sở dữ liệu. Nếu không tìm thấy bản ghi nào, thì nó sẽ trả về null. Truy vấn trên sẽ thực hiện truy vấn SQL sau.
SELECT
[Extent1].[StudentID] AS [StudentID],
[Extent1].[StudentName] AS [StudentName],
[Extent1].[StandardId] AS [StandardId]
FROM [dbo].[Student] AS [Extent1]
WHERE [Extent1].[StudentId] = @p0',N'@p0 int',@p0=1
go
Phương thức First/FirstOrDefault
Nếu bạn muốn lấy một đối tượng sinh viên đơn khi có nhiều sinh viên, tên của đối tượng đó là “Student1” trong CSDL thì bạn sử dụng First hoặc FirstOrDefault như bên dưới:
Cú pháp truy vấn Linq:
using (var ctx = new SchoolDBEntities())
{
var student = (from s in ctx.Students
where s.StudentName == "Bill"
select s).FirstOrDefault<Student>();
}
Cú pháp truy vấn Linq:
using (var ctx = new SchoolDBEntities())
{
var student = ctx.Students
.Where(s => s.StudentName == "Bill")
.FirstOrDefault<Student>();
}
Câu truy vấn trên sẽ thực thi truy vấn CSDL như sau:
SELECT TOP (1)
[Extent1].[StudentID] AS [StudentID],
[Extent1].[StudentName] AS [StudentName],
[Extent1].[StandardId] AS [StandardId]
FROM [dbo].[Student] AS [Extent1]
WHERE 'Bill' = [Extent1].[StudentName]
Câu truy vấn trên sẽ thực thi truy vấn CSDL như sau:
SELECT TOP (1)
[s].[StudentId], [s].[DoB], [s].[FirstName], [s].[GradeId],
[s].[LastName], [s].[MiddleName]
FROM [Students] AS [s]
WHERE [s].[FirstName] = N'Bill'
Phương thức truy vấn có tham số
Để thực thi một truy vấn có tham số trong cơ sở dữ liệu nếu truy vấn LINQ-to-Entities, xem ví dụ sau.
using (var ctx = new SchoolDBEntities())
{
string name = "Bill";
var student = ctx.Students
.Where(s => s.StudentName == name)
.FirstOrDefault<Student>();
}
Câu truy vấn trên sẽ thực thi truy vấn CSDL như sau:
SELECT TOP (1)
[Extent1].[StudentId] AS [StudentId],
[Extent1].[Name] AS [Name]
FROM [dbo].[Student] AS [Extent1]
WHERE ([Extent1].[Name] = @p__linq__0) OR (([Extent1].[Name] IS NULL)
AND (@p__linq__0 IS NULL))',N'@p__linq__0 nvarchar(4000)',@p__linq__0=N'Bill'
Sự khác biệt giữa First và FirstOrDefault
- First () sẽ ném một ngoại lệ nếu không có dữ liệu.
- FirstOrDefault () trả về giá trị mặc định (null) nếu không có dữ liệu.
Phương thức ToList
Phương thức ToList trả về một tập kết quả. Nếu bạn muốn liệt kê tất cả các sinh viên có cùng tên thì hãy sử dụng ToList ():
using (var ctx = new SchoolDBEntities())
{
var studentList = ctx.Students.Where(s => s.StudentName == "Bill").ToList();
}
Chúng ta cũng có thể sử dụng ToArray, ToDictionary hoặc ToLookup. Câu truy vấn trên sẽ thực thi truy vấn CSDL như sau::
SELECT
[Extent1].[StudentID] AS [StudentID],
[Extent1].[StudentName] AS [StudentName],
[Extent1].[StandardId] AS [StandardId]
FROM [dbo].[Student] AS [Extent1]
WHERE 'Bill' = [Extent1].[StudentName]
go
Phương thức GroupBy
Chúng ta có nhóm theo toán tử hoặc phương thức mở rộng GroupBy để lấy kết quả dựa trên nhóm theo thuộc tính cụ thể của một thực thể.
Ví dụ sau lấy các kết quả được nhóm theo từng trong bảng Standard. Sử dụng vòng lặp foreach để lấy giá trị.
Cú pháp truy vấn LINQ:
using (var ctx = new SchoolDBEntities())
{
var students = from s in ctx.Students
group s by s.StandardId into studentsByStandard
select studentsByStandard;
foreach (var groupItem in students)
{
Console.WriteLine(groupItem.Key);
foreach (var stud in groupItem)
{
Console.WriteLine(stud.StudentId);
}
}
}
Cú pháp phương thức LINQ:
using (var ctx = new SchoolDBEntities())
{
var students = ctx.Students.GroupBy(s => s.StandardId);
foreach (var groupItem in students)
{
Console.WriteLine(groupItem.Key);
foreach (var stud in groupItem)
{
Console.WriteLine(stud.StudentId);
}
}
}
Câu truy vấn trên sẽ thực thi truy vấn CSDL như sau:
SELECT
[Project2].[C1] AS [C1],
[Project2].[StandardId] AS [StandardId],
[Project2].[C2] AS [C2],
[Project2].[StudentID] AS [StudentID],
[Project2].[StudentName] AS [StudentName],
[Project2].[StandardId1] AS [StandardId1]
FROM ( SELECT
[Distinct1].[StandardId] AS [StandardId],
1 AS [C1],
[Extent2].[StudentID] AS [StudentID],
[Extent2].[StudentName] AS [StudentName],
[Extent2].[StandardId] AS [StandardId1],
CASE WHEN ([Extent2].[StudentID] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C2]
FROM (SELECT DISTINCT
[Extent1].[StandardId] AS [StandardId]
FROM [dbo].[Student] AS [Extent1] ) AS [Distinct1]
LEFT OUTER JOIN [dbo].[Student] AS [Extent2] ON ([Distinct1].[StandardId] = [Extent2].[StandardId]) OR (([Distinct1].[StandardId] IS NULL) AND ([Extent2].[StandardId] IS NULL))
) AS [Project2]
ORDER BY [Project2].[StandardId] ASC, [Project2].[C2] ASC
go
Phương thức OrderBy
Sử dụng toán tử OrderBy với các từ khóa tăng dần / giảm dần trong cú pháp truy vấn LINQ để nhận danh sách thực thể được sắp xếp.
using (var ctx = new SchoolDBEntities())
{
var students = from s in ctx.Students
orderby s.StudentName ascending
select s;
}
sử dụng phương thức OrderBy hoặc OrderByDescending để nhận danh sách thực thể đã được sắp xếp.
using (var ctx = new SchoolDBEntities())
{
var students = ctx.Students.OrderBy(s => s.StudentName).ToList();
// or descending order
var descStudents = ctx.Students.OrderByDescending(s => s.StudentName).ToList();
}
Câu truy vấn trên sẽ thực thi truy vấn CSDL như sau:
SELECT
[Extent1].[StudentID] AS [StudentID],
[Extent1].[StudentName] AS [StudentName],
[Extent1].[StandardId] AS [StandardId]
FROM [dbo].[Student] AS [Extent1]
ORDER BY [Extent1].[StudentName] ASC
go
Anonymous Object Result
Chúng ta chỉ cần lấy vài thuộc tính của đối tượng trong truy vần LINQ. Xem ví dụ sau:
Truy vấn sau đây trả về một danh sách các đối tượng nặc danh chứa các thuộc tính StudentId và StudentName.
Cú pháp truy vấn LINQ:
using (var ctx = new SchoolDBEntities())
{
var anonymousObjResult = from s in ctx.Students
where s.StandardId == 1
select new {
Id = st.StudentId,
Name = st.StudentName
};
foreach (var obj in anonymousObjResult)
{
Console.Write(obj.Name);
}
}
Cú pháp phương thức LINQ:
using (var ctx = new SchoolDBEntities())
{
var anonymousObjResult = ctx.Students
.Where(st => st.Standard == 1)
.Select(st => new {
Id = st.StudentId,
Name = st.StudentName });
foreach (var obj in anonymousObjResult)
{
Console.Write(obj.Name);
}
}
Câu truy vấn trên sẽ thực thi truy vấn CSDL như sau:
SELECT
[s].[StudentID] AS [Id], [s].[StudentName] AS [Name]
FROM [Student] AS [s]
WHERE [s].[StandardId] = 1
go
ProjectionResult trong truy vấn trên sẽ là kiểu nặc danh bởi vì không có lớp hoặc thực thể nào có những thuộc tính đó. Vì vậy trình biên dịch sẽ đánh dấu nó như là nặc danh.
Date: October 23, 2016Author: Khanh Le0 Comments
Chúng ta đã tạo EDM, DbContext và những lớp thực thể trong những phần trước. Bạn sẽ học những kiểu truy vấn khác nhau mà Entity Framework, nó lần lượt chuyển đổi thành truy vấn SQL cho CSDL bên dưới.
Entity Framework hỗ trợ 3 kiểu truy vấn: LINQ to Entities, Entity SQL và Native SQL.
I. LINQ to Entities:
Language-Integrated Query (LINQ) là một ngôn ngữ truy vấn mạnh mẽ được giới thiệu trong VS 2008. Bạn có thể sử dụng LINQ trong C# hoặc Visual Basic để truy vấn những dữ liệu nguồn khác nhau. LINQ-to-Entities hoạt động trên những thực thể Entity Framework để truy cập dữ liệu từ CSDL bên dưới. Bạn có thể sử dụng cú pháp LINQ method hoặc cú pháp truy vấn khi truy vấn với EDM.
Cú pháp LINQ Method:
//Querying with LINQ to Entities
using (var context = new SchoolDBEntities())
{
var L2EQuery = context.Students.where(s => s.StudentName == "Bill");
var student = L2EQuery.FirstOrDefault<Student>();
}
Cú pháp LINQ Query:
using (var context = new SchoolDBEntities())
{
var L2EQuery = from st in context.Students
where st.StudentName == "Bill"
select st;
var student = L2EQuery.FirstOrDefault<Student>();
}
Đầu tiên bạn phải tạo một đối tượng của lớp context đó là SchoolDBEntities. Bạn nên khởi tạo nó ở using() để một khi nó bên ngoài phạm vi thì nó sẽ tự động gọi phương thức Dispose() của DbContext. Cả hai cú pháp trên, context đều trả về IQueryable.
Những truy vấn Linq-to-Entities Projection:
Projection là một quá trình lựa chọn dữ liệu trong một dạng khác hơn là một dạng thực thể cụ thể đang truy vấn. Có nhiều cách của projection. Chúng ta giờ sẽ nhìn thấy vài kiểu của projection:
First/FirstOrDefault:
Nếu bạn muốn lấy một đối tượng sinh viên đơn khi có nhiều sinh viên, tên của đối tượng đó là “Student1” trong CSDL thì bạn sử dụng First hoặc FirstOrDefault như bên dưới:
using (var ctx = new SchoolDBEntities())
{
var student = (from s in ctx.Students
where s.StudentName == "Student1"
select s).FirstOrDefault<Student>();
}
Câu truy vấn trên sẽ thành truy vấn CSDL như sau:
SELECT TOP (1)
[Extent1].[StudentID] AS [StudentID],
[Extent1].[StudentName] AS [StudentName],
[Extent1].[StandardId] AS [StandardId]
FROM [dbo].[Student] AS [Extent1]
WHERE 'Student1' = [Extent1].[StudentName]
Khác nhau giữa First và FirstOrDefault là First() sẽ ném ra một exception nếu không có dữ liệu kết quả cho tiêu thức đã cung cấp trong khi FirstOrDefault() trả về giá trị mặc định (null) nếu không có dữ liệu kết quả.
Single/SingleOrDefault:
Bạn cũng có thể sử dụng Single hoặc SingleOrDefault để lấy một đối tượng sinh viên đơn như thể hiện bên dưới:
using (var ctx = new SchoolDBEntities())
{
var student = (from s in context.Students
where s.StudentID == 1
select s).SingleOrDefault<Student>();
}
Câu truy vấn trên sẽ thực thi truy vấn CSDL như sau:
SELECT TOP (2)
[Extent1].[StudentID] AS [StudentID],
[Extent1].[StudentName] AS [StudentName],
[Extent1].[StandardId] AS [StandardId]
FROM [dbo].[Student] AS [Extent1]
WHERE 1 = [Extent1].[StudentID]
go
Single hoặc SingleOrDefault sẽ ném ra một exception nếu kết quả trả về có nhiều hơn một phần tử. Sử dụng Single hoặc SingleOrDefault nếu bạn chắn chắn rằng kết quả trả về sẽ có chỉ một phần tử. Nếu kết quả có nhiều phần tử thì sẽ có vài vấn đề.
ToList:
Nếu bạn muốn liệt kê tất cả những sinh viên với tên ‘Student1’ (nếu có nhiều sinh viên trùng tên) thì sử dụng ToList():
using (var ctx = new SchoolDBEntities())
{
var studentList = (from s in ctx.Students
where s.StudentName == "Student1"
select s).ToList<Student>();
}
Câu truy vấn trên sẽ thành truy vấn CSDL như sau:
SELECT
[Extent1].[StudentID] AS [StudentID],
[Extent1].[StudentName] AS [StudentName],
[Extent1].[StandardId] AS [StandardId]
FROM [dbo].[Student] AS [Extent1]
WHERE 'Student1' = [Extent1].[StudentName]
go
GroupBy:
Nếu bạn muốn nhóm những sinh viên bằng StandardId thì sử dụng groupby:
using (var ctx = new SchoolDBEntities())
{
var students = from s in ctx.Students
group s by s.StandardId into studentsByStandard
select studentsByStandard;
}
Câu truy vấn trên sẽ thực thi truy vấn CSDL như sau:
SELECT
[Project2].[C1] AS [C1],
[Project2].[StandardId] AS [StandardId],
[Project2].[C2] AS [C2],
[Project2].[StudentID] AS [StudentID],
[Project2].[StudentName] AS [StudentName],
[Project2].[StandardId1] AS [StandardId1]
FROM ( SELECT
[Distinct1].[StandardId] AS [StandardId],
1 AS [C1],
[Extent2].[StudentID] AS [StudentID],
[Extent2].[StudentName] AS [StudentName],
[Extent2].[StandardId] AS [StandardId1],
CASE WHEN ([Extent2].[StudentID] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C2]
FROM (SELECT DISTINCT
[Extent1].[StandardId] AS [StandardId]
FROM [dbo].[Student] AS [Extent1] ) AS [Distinct1]
LEFT OUTER JOIN [dbo].[Student] AS [Extent2] ON ([Distinct1].[StandardId] = [Extent2].[StandardId]) OR (([Distinct1].[StandardId] IS NULL) AND ([Extent2].[StandardId] IS NULL))
) AS [Project2]
ORDER BY [Project2].[StandardId] ASC, [Project2].[C2] ASC
go
OrderBy:
Nếu bạn muốn lấy danh sách của những sinh viên được sắp xếp theo StudentName thì sử dụng OrderBy:
using (var ctx = new SchoolDBEntities())
{
var student1 = from s in ctx.Students
orderby s.StudentName ascending
select s;
}
Câu truy vấn trên sẽ thực thi truy vấn CSDL như sau:
SELECT
[Extent1].[StudentID] AS [StudentID],
[Extent1].[StudentName] AS [StudentName],
[Extent1].[StandardId] AS [StandardId]
FROM [dbo].[Student] AS [Extent1]
ORDER BY [Extent1].[StudentName] ASC
go
Anonymous Class result:
Nếu bạn muốn lấy chỉ StudentName, StandardName và danh sách của những khóa học trong một đối tượng đơn thì viết projection sau:
using (var ctx = new SchoolDBEntities())
{
var projectionResult = from s in ctx.Students
where s.StudentName == "Student1"
select new {
s.StudentName, s.Standard.StandardName, s.Courses
};
}
Câu truy vấn trên sẽ thực thi truy vấn CSDL như sau:
SELECT
[Extent1].[StudentID] AS [StudentID],
[Extent1].[StudentName] AS [StudentName],
[Extent2].[City] AS [City]
FROM [dbo].[Student] AS [Extent1]
LEFT OUTER JOIN [dbo].[StudentAddress] AS [Extent2] ON [Extent1].[StudentID] = [Extent2].[StudentID]
WHERE 1 = [Extent1].[StandardId]
go
ProjectionResult trong truy vấn trên sẽ là kiểu nặc danh bởi vì không có lớp hoặc thực thể nào có những thuộc tính đó. Vì vậy trình biên dịch sẽ đánh dấu nó như là nặc danh.
Những truy vấn nested:
Bạn cũng có thể thực thi những truy vấn nested LINQ to entity như sau:
Truy vấn nested thể hiện ở trên sẽ kết quả trong một danh sách nặc danh với một đối tượng StudentName và Course.
SELECT
[Extent1].[StudentID] AS [StudentID],
[Extent1].[StudentName] AS [StudentName],
[Join1].[CourseId1] AS [CourseId],
[Join1].[CourseName] AS [CourseName],
[Join1].[Location] AS [Location],
[Join1].[TeacherId] AS [TeacherId]
FROM [dbo].[Student] AS [Extent1]
INNER JOIN (SELECT [Extent2].[StudentId] AS [StudentId], [Extent3].[CourseId] AS [CourseId1], [Extent3].[CourseName] AS [CourseName], [Extent3].[Location] AS [Location], [Extent3].[TeacherId] AS [TeacherId]
FROM [dbo].[StudentCourse] AS [Extent2]
INNER JOIN [dbo].[Course] AS [Extent3] ON [Extent3].[CourseId] = [Extent2].[CourseId] ) AS [Join1] ON [Extent1].[StudentID] = [Join1].[StudentId]
WHERE 1 = [Extent1].[StandardId]
go