在我的asp.net mvc5 Web应用程序中编写异步存储库方法

时间:2015-09-07 00:21:20

标签: asp.net-mvc asynchronous asp.net-mvc-5 async-await entity-framework-6

我正在开发一个asp.net mvc5 Web应用程序+实体框架6.我希望在我的应用程序中有一个存储库模型类。目前我正在遵循这种方法来拥有异步操作方法和异步存储库模型类。

例如,我有以下存储库保存方法: -

public class Repository
    {

        private Entities t = newEntities();

        public async Task Save()
        {
            await t.SaveChangesAsync();
        }

我从我的行动方法中调用如下: -

Repository repository = new Repository();
 public async Task<ActionResult> GetResourceByName(string resourcename)
      {
         await  repository.Save();
      }

这是一个正确的方法吗?因为我正在做以下事情: -

  1. 我将存储库方法定义为Task&amp;还有Action方法作为Task。所以我应该把它们都作为任务。?

  2. 我在动作方法(调用repo方法时)和存储库上两次使用await?那些await是多余的?

  3. //请注意,这只是一个存储库方法的简单示例,它仅用于保存模型,但我问的是方法本身,因为我将在更复杂的存储库方法中使用此方法。

4 个答案:

答案 0 :(得分:2)

  1. Q1:是的,两者都应该返回Task以及它应该如何,并且它完全没问题。
  2. Q2:是的,两者都应该等待(如果你有更多的层,那么所有这些层中的所有函数调用都应该等待)。
  3. 基本上:一旦你去async / await,所有的调用链应该是async / await以避免线程相关的问题......

    如果你没有等待,那就叫做:&#34; Fire and Forget&#34;这是不推荐的,因为这个请求将以成功消息返回给用户,但是稍后,你在没有await的情况下进行的调用可能会失败并抛出异常,你不知道那个异常是什么......

答案 1 :(得分:2)

与两个答案相反,两个awaits不是必需的。只有在您继续之前需要await的结果时,才需要asyncTask。如果您不需要该结果,则不需要标记您的方法async

这意味着这种方法......

public async Task Save()
{
    await t.SaveChangesAsync();
}

可以简化为

public Task Save()
{
    return t.SaveChangesAsync();
}

即使这些在功能上是相同的,我喜欢第二个版本,因为它表明Task是异步工作的识别,而async / await只是编译器等待该工作完成的信号。

答案 2 :(得分:1)

无论其他答案如何,这就是为什么你应该使用await等等。

ASP.NET具有将在每个await上捕获的同步上下文,并且将为该上下文发布延续(async方法的其余部分)。这意味着不仅要进行线程切换,还要使用ASP.NET上下文填充切换到的线程。

如果为异步调用添加后缀(await调用ConfigureAwait(false),则不会发生ASP.NET上下文切换(尽管线程切换会发生)。

因为动作视图引擎很可能需要ASP.NET上下文,所以不能在MVC动作方法上使用ConfigureAwait(false)。但你应该使用它所调用的所有方法。

所以,你的代码应该是这样的:

public class Repository
{
    private Entities t = new Entities();
    public async Task SaveAsync()
    {
        await t.SaveChangesAsync().ConfigureAwait(false);
    }
}


Repository repository = new Repository();
public async Task<ActionResult> GetResourceByName(string resourcename)
{
    await repository.SaveAsync();
}

因为await调用ConfigureAwait(false)之后可能没有ASP.NET上下文,所以你应该从ASP.NET上下文中传递所需的所有内容。

答案 3 :(得分:0)

回答你的问题:

  1. 是的,他们都应该返回任务。
  2. 是的,SELECT T0.C0, T1.C0, T2.C0, CASE WHEN T1.C0 IS NULL AND T2.C0 IS NULL THEN 1 END, CASE WHEN T2.C0 = 'NM' THEN 'New to PHI' WHEN T1.C0 IS NOT NULL OR T2.C0 IS NOT NULL THEN 'Transferred' ELSE 'New to PHI' END, T0.C1 FROM ( SELECT DISTINCT "Memberships"."MemberNo" AS C0, COUNT("Memberships"."MemberNo") AS C1 FROM "dbo"."Memberships" WHERE ( "Memberships"."IsProspect" = N'N' ) AND ( ( "Memberships"."PaidToDate" > "Memberships"."JoinDate" OR "Memberships"."HealthTermEntryDate" IS NULL ) AND "Memberships"."JoinDate" BETWEEN '20150701 00:00:00.0' AND '20160630 23:59:59.997' ) GROUP BY "Memberships"."MemberNo" ) T0 LEFT OUTER JOIN ( SELECT DISTINCT "MembershipPlans"."PrevFundID" AS C0, "Memberships"."MemberNo" AS C1 FROM "dbo"."Memberships" INNER JOIN "dbo"."MembershipPlans" ON ( "Memberships"."MemberNo" = "MembershipPlans"."MemberNo" ) WHERE ( "Memberships"."IsProspect" = N'N' ) AND ( "MembershipPlans"."IsPrevCover" IN (N'Y') ) ) T1 ON T0.C0 = T1.C1 LEFT OUTER JOIN ( SELECT DISTINCT "MembershipUDPs"."Property" AS C0, "MembershipUDPs"."MemberNo" AS C1 FROM "dbo"."Memberships" LEFT OUTER JOIN "dbo"."MembershipUDPs" ON ( "Memberships"."MemberNo" = "MembershipUDPs"."MemberNo" ) WHERE ( "Memberships"."IsProspect" = N'N' ) AND ( "MembershipUDPs"."PropertyID" IN (N'TF ') AND "Memberships"."JoinDate" >= '20150608 00:00:00.0' ) ) T2 ON T0.C0 = T2.C1 有道理。不,它们不是多余的。
  3. 为了理解你可能(不必要)为什么需要await两次,我建议你看看这篇文章,它给出了一个非常明确的例子,说明它如何以及为什么有效:

    C# async and await: Why Do We Need Them?

    如果您想使用存储库并且可能想考虑为您的业务逻辑使用服务,我建议您检查这个易于扩展和通用的项目:

    URF - Unit of Work & Repositories Framework

    在考虑从头开始开发所有内容之前,您应该检查一下。我真的很喜欢这个项目!