我正在尝试使用 asp.net mvc 制作我的第一个 Web 应用程序,包括使用 linq 的远程数据库。所以我使用来自 mvc 的默认模板,我在 AccountController 中重新制作代码,使用 linq 实现注册用户,现在我很感兴趣用异步来做。那么是否可以用异步处理linq?如果是,请告诉我如何做,这对我很有帮助。这是我通过 linq 注册的示例:
[AllowAnonymous]
public ActionResult Register()
{
return View();
}
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
using (DataBaseClassDataContext dc = new DataBaseClassDataContext())
{
Users tbUsers = new Users();
tbUsers.Login = model.Login;
tbUsers.Name = model.Name;
tbUsers.Surname = model.Surname;
tbUsers.Password = model.Password;
tbUsers.E_Mail = model.Email;
tbUsers.Knowledge = model.Knowledge;
dc.Users.InsertOnSubmit(tbUsers);
dc.SubmitChanges();
ModelState.Clear();
ViewBag.Message = "Successfully Registration Done";
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
最佳答案
让我们先看看你的第一个 Action :
[AllowAnonymous]
public ActionResult Register()
{
return View();
}
好吧,这里没有做任何会延迟当前线程的事情,所以异步做任何事情都没有好处,所以最好的办法就是不理会这个。
现在,让我们看看您的第二个操作。它包含:
dc.SubmitChanges();
在将更改发送到数据库时,这确实会阻塞当前线程。它可能不会大受欢迎,但它是我们可能从使用异步代码中获益的地方,尤其是因为如今它非常容易实现。
首先更改签名:
public async Task<ActionResult> Register(RegisterViewModel model)
如果你现在编译它会工作,但你会得到一个警告:
This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
但是我们已经做了两件事:
- 从返回中更改
ActionResult
返回Task<ActionResult>
. - 用过
async
这意味着我们可以做return View(model);
它变成了实际返回Task<ActionResult>
的代码运行时将运行您原始方法中的代码。
但实际上我们并没有得到任何东西。然而,而不是 dc.SubmitChanges()
我们将使用 dc.SubmitChangesAsync()
.
这是假设它存在,这将取决于您的数据提供者。稍后会详细介绍。
如果它确实存在则不返回 void
或一些值 T
它会返回 Task
或 Task<T>
.
我们可以自己编写代码来处理该任务,但最简单的做法是:
await dc.SubmitChangesAsync();
现在我们拥有的方法是返回一个 Task<Action>
当最初Run
(MVC 将为我们处理那部分)将执行代码直至 await
。 .然后它会释放运行代码的线程去做其他事情。 SubmitChangesAsync
之后已完成其工作,该方法的执行将在此时恢复。
类似地,我们可以将方法转换为:
var someList = someQuery.ToList();
进入:
var someList = await someQuery.ToListAsync();
现在,正如我所说,这一切都取决于导致数据库访问实际上具有异步版本的方法。如果没有SubmitChangesAsync()
那么要么你必须放弃这种方法(嘘!),要么你必须编写自己的实现(不是微不足道的)。
当前版本的 EntityFramework 提供了以下内容:
AllAsync
AnyAsync
AverageAsync
ContainsAsync
CountAsync
FirstAsync
FirstOrDefaultAsync
ForEachAsync
LoadAsync
LongCountAsync
MaxAsync
MinAsync
SingleAsync
SingleOrDefaultAsync
SumAsync
ToArrayAsync
ToDictionaryAsync
ToListAsync
SaveChangesAsync
也就是说,几乎所有导致实际数据库访问的方法都有一个 Async
版本。
如果您没有使用提供此功能的提供程序,那么转向异步代码并不容易;它涉及的不仅仅是快速回答才能做好。
最后一个警告。假设你有一个 async
总是执行单个 await
的方法那await
是尾调用:
public async Task<List<int>> GetList(int typeID)
{
if(typeID < 1)
throw new ArgumentOutOfRangeException();
return await (from stuff in place where stuff.TypeID == typeID select stuff).ToListAsync();
}
这里你不应该使用async
和 await
完全没有,因为你正在创建一个任务,它总是会导致等待一个任务,所以额外的 await
无缘无故地使幕后的事情复杂化。在这里你应该只返回任务:
public Task<List<int>> GetList(int typeID)
{
if(typeID < 1)
throw new ArgumentOutOfRangeException();
return (from stuff in place where stuff.TypeID == typeID select stuff).ToListAsync();
}
但请注意,在 using
中看起来像尾调用的东西 block 并不是真正的尾调用,因为它也隐含地执行了 Dispose()
在 using
的末尾,因此您不能以这种方式简化此类调用。
您可以简化存在不同可能的尾调用的情况,只要每条路径都执行这样的调用或抛出异常:
public async Task<List<int>> GetList(int? typeID)
{
if(!typeID.HasValue)
throw new ArgumentNullException();
if(typeID.Value < 0)
return await (from stuff in place).ToListAsync();
return await (from stuff in place where stuff.TypeID == typeID select stuff).ToListAsync();
}
可以变成:
public Task<List<int>> GetList(int? typeID)
{
if(!typeID.HasValue)
throw new ArgumentNullException();
if(typeID.Value < 0)
return (from stuff in place).ToListAsync();
return (from stuff in place where stuff.TypeID == typeID select stuff).ToListAsync();
}
关于c# - 如何使用 linq 进行异步,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30260716/