实体框架 SaveChanges() 与 SaveChangesAsync() 和 Find() 与 FindAsync() [英] Entity Framework SaveChanges() vs. SaveChangesAsync() and Find() vs. FindAsync()
问题描述
我一直在寻找上述 2 对之间的差异,但没有找到任何文章清楚地解释它以及何时使用一个或另一个.
那么 SaveChanges()
和 SaveChangesAsync()
有什么区别?
在 Find()
和 FindAsync()
之间?
在服务器端,当我们使用Async
方法时,我们还需要添加await
.因此,我不认为它在服务器端是异步的.
它是否仅有助于防止客户端浏览器上的 UI 阻塞?或者它们之间有什么优缺点?
任何时候您需要在远程服务器上执行操作,您的程序都会生成请求、发送请求,然后等待响应.我将使用 SaveChanges()
和 SaveChangesAsync()
作为示例,但同样适用于 Find()
和 FindAsync()代码>.
假设您有一个包含 100 多个项目的列表 myList
,需要添加到您的数据库中.要插入它,您的函数将如下所示:
using(var context = new MyEDM()){上下文.MyTable.AddRange(myList);context.SaveChanges();}
首先创建MyEDM
的实例,将列表myList
添加到表MyTable
,然后调用SaveChanges()
将更改持久化到数据库.它以您想要的方式工作,记录被提交,但您的程序在提交完成之前不能做任何其他事情.这可能需要很长时间,具体取决于您提交的内容.如果您要提交对记录的更改,实体必须一次提交这些更改(我曾经有一次保存需要 2 分钟才能更新)!
要解决此问题,您可以执行以下两项操作之一.首先是您可以启动一个新线程来处理插入.虽然这将释放调用线程以继续执行,但您创建了一个新线程,它将坐在那里等待.不需要这种开销,这就是 async await
模式解决的问题.
对于 I/O 操作,await
很快就会成为您最好的朋友.从上面的代码部分,我们可以将其修改为:
using(var context = new MyEDM()){Console.WriteLine("保存开始");上下文.MyTable.AddRange(myList);等待 context.SaveChangesAsync();Console.WriteLine("保存完成");}
这是一个很小的变化,但会对代码的效率和性能产生深远的影响.那么会发生什么?代码的开头是相同的,您创建一个 MyEDM
实例并将您的 myList
添加到 MyTable
.但是当您调用 await context.SaveChangesAsync()
时,代码的执行返回到调用函数!因此,当您等待所有这些记录提交时,您的代码可以继续执行.假设包含上述代码的函数具有public async Task SaveRecords(List
的签名,调用函数可能如下所示:
公共异步任务 MyCallingFunction(){Console.WriteLine("函数启动");任务 saveTask = SaveRecords(GenerateNewRecords());for(int i = 0; i <1000; i++){Console.WriteLine("继续执行!");}等待保存任务;Console.Log("功能完成");}
为什么会有这样的函数,我不知道,但它的输出显示了 async await
是如何工作的.首先让我们回顾一下会发生什么.
执行进入MyCallingFunction
,FunctionStarting
然后SaveStarting
被写入控制台,然后函数SaveChangesAsync()代码> 被调用.此时,执行返回到
MyCallingFunction
并进入for 循环写入'Continuing to Execute' 最多1000 次.SaveChangesAsync()
完成后,执行返回到 SaveRecords
函数,将 Save Complete
写入控制台.SaveRecords
中的所有内容完成后,将在 MyCallingFunction
中继续执行,就像 SaveChangesAsync()
完成时一样.使困惑?这是一个示例输出:
或者也许:
<前>功能启动保存开始继续执行!继续执行!保存完成!继续执行!继续执行!继续执行!....继续执行!功能齐全!这就是 async await
的美妙之处,您的代码可以在您等待某事完成时继续运行.实际上,您将有一个更像这样的函数作为您的调用函数:
公共异步任务 MyCallingFunction(){列表<任务>myTasks = new List();myTasks.Add(SaveRecords(GenerateNewRecords()));myTasks.Add(SaveRecords2(GenerateNewRecords2()));myTasks.Add(SaveRecords3(GenerateNewRecords3()));myTasks.Add(SaveRecords4(GenerateNewRecords4()));等待 Task.WhenAll(myTasks.ToArray());}
在这里,您有四种不同的保存记录功能同时运行.MyCallingFunction
使用 async await
的完成速度比串行调用单个 SaveRecords
函数要快得多.
我还没有涉及的一件事是 await
关键字.这样做是停止当前函数的执行,直到您等待的任何 Task
完成.因此,在原始 MyCallingFunction
的情况下,在 SaveRecords
函数完成之前,不会将 Function Complete
行写入控制台.
长话短说,如果您可以选择使用 async await
,您应该这样做,因为它会大大提高您的应用程序的性能.
I have been searching for the differences between 2 pairs above but haven't found any articles explaining clearly about it as well as when to use one or another.
So what is the difference between SaveChanges()
and SaveChangesAsync()
?
And between Find()
and FindAsync()
?
On server side, when we use Async
methods, we also need to add await
. Thus, I don't think it is asynchronous on server side.
Does it only help to prevent the UI blocking on client side browser? Or are there any pros and cons between them?
Any time that you need to do an action on a remote server, your program generates the request, sends it, then waits for a response. I will use SaveChanges()
and SaveChangesAsync()
as an example but the same applies to Find()
and FindAsync()
.
Say you have a list myList
of 100+ items that you need to add to your database. To insert that, your function would look something like so:
using(var context = new MyEDM())
{
context.MyTable.AddRange(myList);
context.SaveChanges();
}
First you create an instance of MyEDM
, add the list myList
to the table MyTable
, then call SaveChanges()
to persist the changes to the database. It works how you want, the records get committed, but your program cannot do anything else until the commit finishes. This can take a long time depending on what you are committing. If you are committing changes to the records, entity has to commit those one at a time (I once had a save take 2 minutes for updates)!
To solve this problem, you could do one of two things. The first is you can start up a new thread to handle the insert. While this will free up the calling thread to continue executing, you created a new thread that is just going to sit there and wait. There is no need for that overhead, and this is what the async await
pattern solves.
For I/O opperations, await
quickly becomes your best friend. Taking the code section from above, we can modify it to be:
using(var context = new MyEDM())
{
Console.WriteLine("Save Starting");
context.MyTable.AddRange(myList);
await context.SaveChangesAsync();
Console.WriteLine("Save Complete");
}
It is a very small change, but there are profound effects on the efficiency and performance of your code. So what happens? The begining of the code is the same, you create an instance of MyEDM
and add your myList
to MyTable
. But when you call await context.SaveChangesAsync()
, the execution of code returns to the calling function! So while you are waiting for all those records to commit, your code can continue to execute. Say the function that contained the above code had the signature of public async Task SaveRecords(List<MyTable> saveList)
, the calling function could look like this:
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");
}
Why you would have a function like this, I don't know, but what it outputs shows how async await
works. First let's go over what happens.
Execution enters MyCallingFunction
, Function Starting
then Save Starting
gets written to the console, then the function SaveChangesAsync()
gets called. At this point, execution returns to MyCallingFunction
and enters the for loop writing 'Continuing to Execute' up to 1000 times. When SaveChangesAsync()
finishes, execution returns to the SaveRecords
function, writing Save Complete
to the console. Once everything in SaveRecords
completes, execution will continue in MyCallingFunction
right were it was when SaveChangesAsync()
finished. Confused? Here is an example output:
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!
Or maybe:
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!
That is the beauty of async await
, your code can continue to run while you are waiting for something to finish. In reality, you would have a function more like this as your calling function:
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());
}
Here, you have four different save record functions going at the same time. MyCallingFunction
will complete a lot faster using async await
than if the individual SaveRecords
functions were called in series.
The one thing that I have not touched on yet is the await
keyword. What this does is stop the current function from executing until whatever Task
you are awaiting completes. So in the case of the original MyCallingFunction
, the line Function Complete
will not be written to the console until the SaveRecords
function finishes.
Long story short, if you have an option to use async await
, you should as it will greatly increase the performance of your application.
这篇关于实体框架 SaveChanges() 与 SaveChangesAsync() 和 Find() 与 FindAsync()的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!