2015-05-05 10 views
49

Tôi đã tìm kiếm sự khác biệt giữa 2 cặp ở trên nhưng chưa tìm thấy bất kỳ bài viết nào giải thích rõ về nó cũng như khi sử dụng cái kia.Entity Framework SaveChanges() so với SaveChangesAsync() và Find() so với FindAsync()

Vậy sự khác biệt giữa SaveChanges()SaveChangesAsync() là gì?
Và giữa Find()FindAsync()?

Ở phía máy chủ, khi chúng tôi sử dụng phương thức Async, chúng tôi cũng cần thêm await. Vì vậy, tôi không nghĩ rằng nó là không đồng bộ trên phía máy chủ.

Chỉ giúp ngăn chặn giao diện người dùng chặn trên trình duyệt phía máy khách? Hoặc có bất kỳ ưu và nhược điểm nào giữa chúng?

+1

không đồng bộ nhiều, * nhiều hơn * ngăn chặn chuỗi giao diện người dùng của khách hàng chặn trong ứng dụng khách. Tôi chắc rằng có một câu trả lời của chuyên gia sẽ sớm xuất hiện. – jdphenix

Trả lời

98

Bất kỳ lúc nào bạn cần thực hiện tác vụ trên máy chủ từ xa, chương trình của bạn sẽ tạo yêu cầu, gửi yêu cầu, sau đó chờ phản hồi. Tôi sẽ sử dụng SaveChanges()SaveChangesAsync() làm ví dụ nhưng cũng áp dụng cho Find()FindAsync().

Giả sử bạn có danh sách myList trong số 100+ mục bạn cần thêm vào cơ sở dữ liệu của mình. Để chèn đó, chức năng của bạn sẽ giống như vậy:

using(var context = new MyEDM()) 
{ 
    context.MyTable.AddRange(myList); 
    context.SaveChanges(); 
} 

Trước tiên, bạn tạo và thể hiện của MyEDM, thêm danh sách myList vào bảng MyTable, sau đó gọi SaveChanges() để kéo dài sự thay đổi đối với cơ sở dữ liệu. Nó hoạt động như thế nào bạn muốn, các hồ sơ có được cam kết, nhưng chương trình của bạn không thể làm bất cứ điều gì khác cho đến khi cam kết kết thúc. Điều này có thể mất nhiều thời gian tùy thuộc vào những gì bạn đang cam kết. Nếu bạn đang thực hiện thay đổi đối với hồ sơ, thực thể phải cam kết một lần tại một thời điểm (tôi đã từng tiết kiệm mất 2 phút để cập nhật)!

Để giải quyết vấn đề này, bạn có thể thực hiện một trong hai điều. Đầu tiên là bạn có thể bắt đầu một chuỗi mới để xử lý chèn. Trong khi điều này sẽ giải phóng các thread gọi để tiếp tục thực hiện, bạn tạo ra một chủ đề mới chỉ là sẽ ngồi đó và chờ đợi. Không cần thiết cho chi phí đó, và đây là những gì mà mẫu hình async await giải quyết.

Đối với các giai đoạn I/O, await nhanh chóng trở thành người bạn tốt nhất của bạn.Lấy phần mã từ phía trên, chúng tôi có thể sửa đổi nó thành:

using(var context = new MyEDM()) 
{ 
    Console.WriteLine("Save Starting"); 
    context.MyTable.AddRange(myList); 
    await context.SaveChangesAsync(); 
    Console.WriteLine("Save Complete"); 
} 

Đây là một thay đổi rất nhỏ, nhưng có tác động sâu sắc đến hiệu quả và hiệu suất của mã của bạn. Vậy điều gì sẽ xảy ra? Sự khởi đầu của mã là như nhau, bạn tạo một thể hiện của MyEDM và thêm myList của bạn vào MyTable. Nhưng khi bạn gọi await context.SaveChangesAsync(), việc thực thi mã sẽ trở về chức năng gọi điện! Vì vậy, trong khi bạn đang chờ đợi tất cả các hồ sơ đó cam kết, mã của bạn có thể tiếp tục thực thi. Nói chức năng có chứa đoạn mã trên có chữ ký của public async Task SaveRecords(List<MyTable> saveList), chức năng gọi điện thoại có thể nhìn như thế này:

public async Task MyCallingFunction() 
{ 
    Console.WriteLine("Function Starting"); 
    Task saveTask = SaveRecords(GenerateNewRecords()); 

    for(int i = 0; i < 1000; i++){ 
     Console.WriteLine("Continuing to execute!"); 
    } 

    await saveTask; 
    Console.Log("Function Complete"); 
} 

Tại sao bạn sẽ có một chức năng như thế này, tôi không biết, nhưng những gì nó ra cho thấy cách async await hoạt động. Đầu tiên cho phép đi qua những gì sẽ xảy ra.

Thực hiện nhập MyCallingFunction, Function Starting sau đó Save Starting được ghi vào bảng điều khiển, sau đó chức năng SaveChangesAsync() được gọi. Tại thời điểm này, thực hiện trả về MyCallingFunction và nhập vòng lặp for viết 'Tiếp tục thực thi' lên tới 1000 lần. Khi SaveChangesAsync() phần lan, thực thi trả về hàm SaveRecords, viết Save Complete vào bảng điều khiển. Khi mọi thứ trong SaveRecords hoàn tất, việc thực thi sẽ tiếp tục trong MyCallingFunction ngay khi nó hoàn thành khi SaveChangesAsync() hoàn tất. Bối rối? Dưới đây là một ví dụ đầu ra:

 
Function Starting 
Save Starting 
Continuing to execute! 
Continuing to execute! 
Continuing to execute! 
Continuing to execute! 
Continuing to execute! 
.... 
Continuing to execute! 
Save Complete! 
Continuing to execute! 
Continuing to execute! 
Continuing to execute! 
.... 
Continuing to execute! 
Function Complete! 

Hoặc có thể:

 
Function Starting 
Save Starting 
Continuing to execute! 
Continuing to execute! 
Save Complete! 
Continuing to execute! 
Continuing to execute! 
Continuing to execute! 
.... 
Continuing to execute! 
Function Complete! 

Đó là vẻ đẹp của async await, mã của bạn có thể tiếp tục chạy trong khi bạn đang chờ đợi một cái gì đó để kết thúc. Trên thực tế, bạn sẽ có một chức năng hơn như thế này như chức năng của bạn gọi:

public async Task MyCallingFunction() 
{ 
    List<Task> myTasks = new List<Task>(); 
    myTasks.Add(SaveRecords(GenerateNewRecords())); 
    myTasks.Add(SaveRecords2(GenerateNewRecords2())); 
    myTasks.Add(SaveRecords3(GenerateNewRecords3())); 
    myTasks.Add(SaveRecords4(GenerateNewRecords4())); 

    await Task.WhenAll(myTasks.ToArray()); 
} 

Ở đây, bạn có bốn chức năng ghi lại tiết kiệm khác nhau sẽ cùng lúc. MyCallingFunction sẽ hoàn thành nhanh hơn rất nhiều bằng cách sử dụng async await sau đó nếu các chức năng riêng lẻ SaveRecords được gọi theo chuỗi.

Điều mà tôi chưa chạm vào là từ khóa await. Điều này có nghĩa là dừng chức năng hiện tại để thực thi cho đến khi bất cứ điều gì Task bạn đang đợi hoàn tất. Vì vậy, trong trường hợp của MyCallingFunction ban đầu, dòng Function Complete sẽ không được ghi vào bảng điều khiển cho đến khi chức năng SaveRecords kết thúc.

Ngắn câu chuyện ngắn, nếu bạn có tùy chọn sử dụng async await, bạn nên làm như vậy sẽ làm tăng hiệu suất của ứng dụng của bạn.

+3

giải thích rất hay, cảm ơn! – renakre

+3

99% thời gian tôi vẫn phải đợi các giá trị nhận được từ cơ sở dữ liệu trước khi tôi có thể tiếp tục. Tôi có nên sử dụng async không? Không đồng bộ cho phép 100 người kết nối với trang web của tôi một cách không đồng bộ? Nếu tôi không sử dụng async, điều đó có nghĩa là tất cả 100 người dùng phải đợi trong dòng 1 tại một thời điểm? – MIKE

+1

Đáng chú ý: Sinh ra một chủ đề mới từ nhóm chủ đề khiến ASP trở thành một gấu trúc buồn vì bạn chủ yếu lấy một chuỗi từ ASP (có nghĩa là thread không thể xử lý các yêu cầu khác hoặc làm gì cả vì nó bị kẹt trong một cuộc gọi chặn). Tuy nhiên, nếu bạn sử dụng 'await', ngay cả khi bạn không cần phải làm gì khác sau cuộc gọi tới SaveChanges, ASP sẽ nói" aha, thread này trả về một hoạt động không đồng bộ, điều này có nghĩa là tôi có thể để thread này xử lý một số yêu cầu khác trong luc đo!" Điều này làm cho quy mô ứng dụng của bạn theo chiều ngang tốt hơn nhiều. – kai

3

Tuyên bố này là không chính xác:

Về phía server, khi chúng tôi sử dụng phương pháp async, chúng tôi cũng cần thêm chờ đợi.

Bạn không cần thêm "đang chờ". "await" chỉ đơn thuần là một từ khóa thuận tiện trong C# cho phép bạn viết nhiều dòng mã hơn sau cuộc gọi, và những dòng khác sẽ chỉ được thực hiện sau khi thao tác Lưu hoàn tất. Nhưng như bạn đã chỉ ra, bạn có thể thực hiện điều đó đơn giản bằng cách gọi SaveChanges thay vì SaveChangesAsync.

Nhưng về cơ bản, cuộc gọi không đồng bộ là nhiều hơn thế. Ý tưởng ở đây là nếu có công việc khác bạn có thể thực hiện (trên máy chủ) trong khi tiến trình Lưu đang diễn ra, thì bạn nên sử dụng SaveChangesAsync. Không sử dụng "đang chờ". Chỉ cần gọi SaveChangesAsync, và sau đó tiếp tục làm các công cụ khác song song. Điều này bao gồm khả năng, trong một ứng dụng web, trả về một phản ứng cho khách hàng ngay cả trước khi Save đã hoàn thành. Nhưng tất nhiên, bạn vẫn sẽ muốn kiểm tra kết quả cuối cùng của Lưu để trong trường hợp nó không thành công, bạn có thể truyền đạt điều đó cho người dùng của mình hoặc đăng nhập bằng cách nào đó.

+3

Bạn thực sự muốn chờ các cuộc gọi này nếu không bạn có thể chạy các truy vấn và lưu dữ liệu đồng thời bằng cùng một cá thể DbContext và DbContext không phải là luồng an toàn. Ngày đầu đó chờ đợi làm cho nó dễ dàng để xử lý các trường hợp ngoại lệ. Không chờ đợi bạn sẽ phải lưu trữ nhiệm vụ và kiểm tra xem nó có bị lỗi hay không mà không biết khi nào nhiệm vụ hoàn thành, bạn sẽ không biết khi nào nên kiểm tra trừ khi bạn sử dụng '.ContinueWith' đòi hỏi nhiều suy nghĩ hơn là chờ đợi. – Pawel

+14

Câu trả lời này là lừa đảo, Gọi một phương pháp không đồng bộ mà không phải chờ đợi làm cho nó trở thành "lửa và quên."Phương pháp này sẽ biến mất và có thể sẽ hoàn thành đôi khi, nhưng bạn sẽ không bao giờ biết khi nào, và nếu nó ném ra một ngoại lệ, bạn sẽ không bao giờ nghe về nó, bạn không thể đồng bộ hóa với hoàn thành của nó. không được gọi với một quy tắc đơn giản (và không chính xác) như "đang chờ trên máy khách, không chờ đợi trên máy chủ." –

+1

Đây là một phần kiến ​​thức rất hữu ích mà tôi đã đọc trong tài liệu, nhưng chưa thực sự được xem xét. tùy chọn để: 1. SaveChangesAsync() thành "Lửa và quên", như John Melville nói ... điều này hữu ích đối với tôi trong một số trường hợp. 2. chờ SaveChangesAsync() thành "Fire, quay lại người gọi và sau đó thực hiện một số mã 'sau khi lưu' sau khi lưu xong. Phần rất hữu ích. Cảm ơn bạn. –

Các vấn đề liên quan