我正在尝试用asp.net mvc制作我的第一个web应用程序,包括带linq的远程数据库。所以我正在使用mvc的默认模板,我在AccountController中重新编写了使用linq实现注册用户的代码,现在我很有意思使用async。那么可以用异步处理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);
}
答案 0 :(得分:8)
让我们先看看你的第一个动作:
[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
块中看起来像尾调用的东西实际上不是尾调用,因为它也隐含地在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();
}