Câu trả lời:
Tôi biết những người khác đã viết lý do tại sao bạn sử dụng cái này hay cái kia, nhưng tôi nghĩ tôi sẽ minh họa tại sao bạn KHÔNG nên sử dụng cái này, khi bạn muốn nói cái kia.
Lưu ý: Trong mã của tôi, tôi thường sẽ sử dụng FirstOrDefault()
và SingleOrDefault()
đó là một câu hỏi khác.
Ví dụ: lấy một bảng lưu trữ Customers
bằng các ngôn ngữ khác nhau bằng Khóa tổng hợp ( ID
, Lang
):
DBContext db = new DBContext();
Customer customer = db.Customers.Where( c=> c.ID == 5 ).First();
Mã này ở trên giới thiệu một lỗi logic có thể (khó theo dõi). Nó sẽ trả về nhiều hơn một bản ghi (giả sử bạn có bản ghi khách hàng bằng nhiều ngôn ngữ) nhưng nó sẽ luôn chỉ trả lại bản đầu tiên ... đôi khi có thể hoạt động ... nhưng không phải là bản ghi khác. Thật không thể đoán trước.
Vì mục đích của bạn là trả lại một lần Customer
sử dụng Single()
;
Sau đây sẽ đưa ra một ngoại lệ (đó là những gì bạn muốn trong trường hợp này):
DBContext db = new DBContext();
Customer customer = db.Customers.Where( c=> c.ID == 5 ).Single();
Sau đó, bạn chỉ cần đánh vào trán mình và nói với chính mình ... OOPS! Tôi quên lĩnh vực ngôn ngữ! Sau đây là phiên bản chính xác:
DBContext db = new DBContext();
Customer customer = db.Customers.Where( c=> c.ID == 5 && c.Lang == "en" ).Single();
First()
là hữu ích trong kịch bản sau đây:
DBContext db = new DBContext();
NewsItem newsitem = db.NewsItems.OrderByDescending( n => n.AddedDate ).First();
Nó sẽ trả về MỘT đối tượng và vì bạn đang sử dụng sắp xếp, nó sẽ là bản ghi gần đây nhất được trả về.
Sử dụng Single()
khi bạn cảm thấy nên luôn luôn trả về 1 bản ghi sẽ giúp bạn tránh các lỗi logic.
customers.Where(predicate).Single()
customers.Single(predicate)
nào không?
Độc thân sẽ đưa ra một ngoại lệ nếu nó tìm thấy nhiều hơn một bản ghi phù hợp với tiêu chí. Đầu tiên sẽ luôn luôn chọn bản ghi đầu tiên từ danh sách. Nếu truy vấn trả về chỉ 1 bản ghi, bạn có thể đi với First()
.
Cả hai sẽ ném một InvalidOperationException
ngoại lệ nếu bộ sưu tập trống. Hoặc bạn có thể sử dụng SingleOrDefault()
. Điều này sẽ không ném ngoại lệ nếu danh sách trống
Độc thân()
Trả về một yếu tố cụ thể duy nhất của một truy vấn
Khi sử dụng : Nếu chính xác 1 yếu tố được mong đợi; không 0 hoặc nhiều hơn 1. Nếu danh sách trống hoặc có nhiều hơn một phần tử, nó sẽ đưa ra một Ngoại lệ "Chuỗi chứa nhiều hơn một phần tử"
SingleOrDefault ()
Trả về một yếu tố cụ thể duy nhất của truy vấn hoặc giá trị mặc định nếu không tìm thấy kết quả
Khi sử dụng : Khi 0 hoặc 1 phần tử được mong đợi. Nó sẽ ném một ngoại lệ nếu danh sách có 2 mục trở lên.
Đầu tiên()
Trả về phần tử đầu tiên của truy vấn có nhiều kết quả.
Khi sử dụng : Khi 1 hoặc nhiều yếu tố được mong đợi và bạn chỉ muốn đầu tiên. Nó sẽ ném một ngoại lệ nếu danh sách không chứa phần tử.
FirstOrDefault ()
Trả về phần tử đầu tiên của danh sách với bất kỳ số lượng phần tử hoặc giá trị mặc định nếu danh sách trống.
Khi sử dụng : Khi nhiều yếu tố được mong đợi và bạn chỉ muốn đầu tiên. Hoặc danh sách trống và bạn muốn có một giá trị mặc định cho loại được chỉ định, giống như
default(MyObjectType)
. Ví dụ: nếu loại danh sách làlist<int>
nó sẽ trả về số đầu tiên từ danh sách hoặc 0 nếu danh sách trống. Nếu cólist<string>
, nó sẽ trả về chuỗi đầu tiên từ danh sách hoặc null nếu danh sách trống.
First
khi có 1 hoặc nhiều yếu tố được mong đợi , không chỉ "nhiều hơn 1" và FirstOrDefault
với bất kỳ số lượng yếu tố nào.
Có một sự khác biệt tinh tế, ngữ nghĩa giữa hai phương pháp này.
Sử dụng Single
để truy xuất phần tử đầu tiên (và duy nhất) từ một chuỗi nên chứa một phần tử và không còn nữa. Nếu chuỗi có nhiều hơn phần tử, việc gọi của Single
bạn sẽ khiến ngoại lệ bị ném do bạn chỉ ra rằng chỉ nên có một phần tử.
Sử dụng First
để truy xuất phần tử đầu tiên từ một chuỗi có thể chứa bất kỳ số phần tử nào. Nếu chuỗi có nhiều hơn phần tử, việc gọi của First
bạn sẽ không gây ra ngoại lệ do bạn chỉ ra rằng bạn chỉ cần phần tử đầu tiên trong chuỗi và không quan tâm nếu có nhiều hơn.
Nếu chuỗi không chứa phần tử, cả hai lệnh gọi phương thức sẽ khiến ngoại lệ bị ném vì cả hai phương thức đều mong muốn có ít nhất một phần tử.
Nếu bạn không đặc biệt muốn có một ngoại lệ được ném trong trường hợp có nhiều hơn một mục, hãy sử dụngFirst()
.
Cả hai đều hiệu quả, lấy mục đầu tiên. First()
hiệu quả hơn một chút vì không cần kiểm tra xem có mục thứ hai không.
Sự khác biệt duy nhất là Single()
hy vọng sẽ chỉ có một mục trong bảng liệt kê và sẽ đưa ra một ngoại lệ nếu có nhiều hơn một mục. Bạn sử dụng .Single()
nếu bạn đặc biệt muốn một ngoại lệ được ném trong trường hợp này.
Nếu tôi nhớ lại, Single () kiểm tra xem có phần tử nào khác sau phần tử đầu tiên không (và đưa ra một ngoại lệ nếu đó là trường hợp), trong khi First () dừng lại sau khi nhận được phần tử đó. Cả hai ném một ngoại lệ nếu chuỗi trống.
Cá nhân, tôi luôn sử dụng First ().
Về tính chính xác: Một đồng nghiệp và tôi đã thảo luận về hiệu suất của Single vs First (hoặc SingleOrDefault vs FirstOrDefault), và tôi đã tranh luận về việc First (hoặc FirstOrDefault) sẽ nhanh hơn và cải thiện hiệu suất (Tôi hoàn toàn làm về ứng dụng của chúng tôi chạy nhanh hơn).
Tôi đã đọc một số bài đăng trên Stack Overflow tranh luận về điều này. Một số người nói rằng có hiệu suất tăng nhỏ khi sử dụng First thay vì Single. Điều này là do First sẽ đơn giản trả về mục đầu tiên trong khi Single phải quét tất cả các kết quả để đảm bảo không có trùng lặp (nghĩa là: nếu nó tìm thấy mục ở hàng đầu tiên của bảng, nó vẫn sẽ quét mọi hàng khác để đảm bảo không có giá trị thứ hai phù hợp với điều kiện sẽ gây ra lỗi). Tôi cảm thấy như mình đang ở trên một nền tảng vững chắc với việc First First, nhanh hơn so với Single Single, vì vậy tôi bắt đầu chứng minh điều đó và đưa cuộc tranh luận vào phần còn lại.
Tôi thiết lập một bài kiểm tra trong cơ sở dữ liệu của mình và thêm 1.000.000 hàng ID UniqueIdentifier Foreign UniqueIdentifier Info nvarchar (50) (chứa đầy các chuỗi số 1 0 0 đến Điên 999,9999
Tôi đã tải dữ liệu và đặt ID làm trường khóa chính.
Sử dụng LinqPad, mục tiêu của tôi là chỉ ra rằng nếu bạn tìm kiếm một giá trị trên 'Nước ngoài' hoặc 'Thông tin' khi sử dụng Đơn, thì điều đó sẽ tồi tệ hơn nhiều so với sử dụng Đầu tiên.
Tôi không thể giải thích kết quả tôi nhận được. Trong hầu hết mọi trường hợp, sử dụng Single hoặc SingleOrDefault nhanh hơn một chút. Điều này không có ý nghĩa logic nào với tôi, nhưng tôi muốn chia sẻ điều đó.
Ví dụ: Tôi đã sử dụng các truy vấn sau:
var q = TestTables.First(x=>x.Info == "314638") ;
//Vs.
Var q = TestTables.Single(x=>x.Info =="314638") ; //(this was slightly faster to my surprise)
Tôi đã thử các truy vấn tương tự trên trường khóa 'Nước ngoài' không được lập chỉ mục với suy nghĩ sẽ chứng minh Đầu tiên nhanh hơn, nhưng Đơn luôn luôn nhanh hơn một chút trong các thử nghiệm của tôi.
Bạn có thể thử ví dụ đơn giản để có được sự khác biệt. Ngoại lệ sẽ được ném trên dòng 3;
List<int> records = new List<int>{1,1,3,4,5,6};
var record = records.First(x => x == 1);
record = records.Single(x => x == 1);
Các hồ sơ trong thực thể nhân viên:
Employeeid = 1
: Chỉ có một nhân viên có ID này
Firstname = Robert
: Nhiều hơn một nhân viên có tên này
Employeeid = 10
: Không có nhân viên có ID này
Bây giờ cần phải hiểu những gì Single()
và First()
có nghĩa là chi tiết.
Độc thân()
Single () được sử dụng để trả về một bản ghi duy nhất tồn tại trong một bảng, do đó, truy vấn bên dưới sẽ trả về Nhân viên employeed =1
vì chúng ta chỉ có một Nhân viên có Employeed
1. Nếu chúng ta có hai bản ghi EmployeeId = 1
thì nó sẽ báo lỗi (xem phần lỗi dưới đây trong truy vấn thứ hai nơi chúng tôi đang sử dụng một ví dụ cho Firstname
.
Employee.Single(e => e.Employeeid == 1)
Ở trên sẽ trả về một bản ghi duy nhất, trong đó có 1 employeeId
Employee.Single(e => e.Firstname == "Robert")
Ở trên sẽ đưa ra một ngoại lệ vì các bản ghi nhiều mức trong bảng cho FirstName='Robert'
. Ngoại lệ sẽ là
UnlimitedOperationException: Trình tự chứa nhiều hơn một phần tử
Employee.Single(e => e.Employeeid == 10)
Điều này sẽ, một lần nữa, đưa ra một ngoại lệ vì không có bản ghi nào tồn tại cho id = 10. Ngoại lệ sẽ là
UnlimitedOperationException: Trình tự không chứa phần tử.
Đối với EmployeeId = 10
nó sẽ trả về null, nhưng vì chúng tôi đang sử dụng Single()
nên nó sẽ báo lỗi. Để xử lý lỗi null chúng ta nên sử dụng SingleOrDefault()
.
Đầu tiên()
Đầu tiên () trả về từ nhiều bản ghi, các bản ghi tương ứng được sắp xếp theo thứ tự tăng dần theo để birthdate
nó sẽ trả về 'Robert' người già nhất.
Employee.OrderBy(e => e. Birthdate)
.First(e => e.Firstname == "Robert")
Ở trên nên trả lại cái cũ nhất, Robert theo DOB.
Employee.OrderBy(e => e. Birthdate)
.First(e => e.Employeeid == 10)
Ở trên sẽ đưa ra một ngoại lệ vì không có bản ghi nào cho id = 10 tồn tại. Để tránh một ngoại lệ null chúng ta nên sử dụng FirstOrDefault()
chứ không phải First()
.
Lưu ý: Chúng tôi chỉ có thể sử dụng First()
/ Single()
khi chúng tôi hoàn toàn chắc chắn rằng nó không thể trả về giá trị null.
Trong cả hai hàm, sử dụng SingleOrDefault () HOẶC FirstOrDefault () sẽ xử lý một ngoại lệ null, trong trường hợp không tìm thấy bản ghi nào, nó sẽ trả về null.