Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/323.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 我能打电话给';正常';来自异步API控制器方法的静态方法_C#_Asynchronous_Asp.net Web Api - Fatal编程技术网

C# 我能打电话给';正常';来自异步API控制器方法的静态方法

C# 我能打电话给';正常';来自异步API控制器方法的静态方法,c#,asynchronous,asp.net-web-api,C#,Asynchronous,Asp.net Web Api,WebAPI和Async相对较新,所以请耐心听我说 我有一个WebAPI应用程序,其中包含许多操作。本质上,这些调用SQL Server存储过程 它一直工作得很好,但我希望通过将方法转换为异步,使它更高效、更健壮 让我展示一种方法——因为它们都很相似 旧版本 新版本(异步我想&希望) 不,这种方法是错误的。通常,您应该避免在ASP.NET上运行任务。运行(以及将工作队列排入线程池的任何其他方法) 不要从控制器开始“向下”工作,而应该从最低级别开始向上工作。也就是说,首先检查静态方法并确定是否存在

WebAPI和Async相对较新,所以请耐心听我说

我有一个WebAPI应用程序,其中包含许多操作。本质上,这些调用SQL Server存储过程

它一直工作得很好,但我希望通过将方法转换为异步,使它更高效、更健壮

让我展示一种方法——因为它们都很相似

旧版本 新版本(异步我想&希望)
不,这种方法是错误的。通常,您应该避免在ASP.NET上运行
任务。运行
(以及将工作队列排入线程池的任何其他方法)

不要从控制器开始“向下”工作,而应该从最低级别开始向上工作。也就是说,首先检查静态方法并确定是否存在任何自然异步操作。它们通常是I/O。两个会立即跳到我的面前:and(而且很可能
staticfunctiontogetetinstanceofclassfromresults
还有更多)。你应该先用
wait
调用那些,然后让
async
自然地向你的控制器发展(编译器会指导你)

此外,正如@StephenBrickner所评论的,您可能需要后退一步,确定
async
是否会对您有所帮助。我有一份报告,涵盖了主要考虑因素。特别是,如果您的后端无法扩展(例如,如果它是单个SQL server实例,而不是类似Azure SQL的实例),那么(通常)没有必要让您的web服务器进行扩展

var Result=wait Task.Run(()=>mystaticheper.A_Static_Method(db,b))

将工作传递给新线程。释放旧线程。等待新线程中的结果。现在,一根线可以在停止的地方继续

如果您同时运行多个这样的任务,这可能会很有用,但您需要花费精力让一个线程做一些事情来释放一个线程。这是全部成本,没有收益

其中,异步wins是:

  • 您同时有多个异步操作
  • 您有一个使用异步I/O的异步操作,以便完全释放调用线程
  • 第二个更重要,尤其是在网络方面

    我们先考虑一下你的“帮助者”方法。其中有些调用具有真正的异步等价物,因此我们可以创建真正的异步版本:

    public static async Task<A_Class> AStaticMethodAsync(SqlConnection dbConn, Guid A_Param)
    {
      try
      {
        if (dbConn.State != ConnectionState.Closed) dbConn.Close();
    
        using (var cmd = new SqlCommand("MyStoredProc", dbConn))
        {
          cmd.CommandType = CommandType.StoredProcedure;
          cmd.Parameters.Add("@sp_Param", SqlDbType.UniqueIdentifier).Value = A_PAram
    
          await dbConn.OpenAsync();
          using(SqlDataReader reader = await cmd.ExecuteReader())
          {
            var A_Class = StaticFunctionToGetInstanceOfClassFromResults( reader );
            A_Class.rc = 1;
            return A_Class
          }
        }
      }
      catch (Exception)
      {
        //server error
        // Why are you wrapping an exception instead of just passing it up the stack? This is weird.
        return new A_Class(A_ClassError.beApiError);
      }
    }
    

    而且它确实受益于异步行为。

    为什么您认为在另一个线程上运行该方法并等待结果比同步运行更“高效和健壮”?想象一下,我克隆了你,让你做点什么。丹尼尔·凯利(Daniel Kelley)说得很好,为什么给克隆人任务并等待它完成比自己完成要好。异步编程有开销,应该根据需要使用,而不仅仅是为了使用它。在这种情况下,因为您在这里所做的只是运行一个存储过程,所以没有必要这样做。如果这是一个长期运行的程序,我会从那里开始,并研究如何使它更有效。我也不建议在静态类中使用任何保持状态的东西,比如数据库连接。。。。我在考虑不阻塞线程,因为我的API可能有很多并发请求。。。。不管怎样,如果我必须从我的API调用一个外部API呢。那么异步操作是否更适用?另外,dbConnString是我的BaseController类的一个属性,我将它传递给我的静态方法。我在考虑不阻塞线程,因为我的API可能会有很多并发请求。这当然是个好主意。问题是,因为你正在阻止另一个线程来实现这一点,所以你一无所获。谢谢Jon。你提高了我对这件事的理解。顺便说一句,我的捕获不是最优雅的,但在这种情况下,它起到了作用。发送回响应代码为“API错误”的对象。谢谢。我强烈地考虑看看我是否能把它变成一个捕捉异常的东西,如果我是你,就返回API界面上的“API错误”响应。谢谢史蒂芬,是的,我已经把所有回复都写在了板上,并且在我真正需要ASYNC的时候“加载了我的枪”。Ie-当我有多个任务要运行时,可以同时独立运行。作为我自己的练习,我让我的一个更简单的方法有一个异步选项,只是为了练习。谢谢
    [System.Web.Http.HttpGet]
    public async Task<A_Class> MyAPIMethodAsync(Guid b)
    {
        var db = new SqlConnection(this.dbConnString);
        try
        {
            var Result = await Task.Run(() => MyStaticHelper.A_Static_Method(db, b));
            return Result;
        }
        finally
        {
            db.Dispose();       
        }
    }
    
    public static A_Class A_Static_Method(SqlConnection dbConn, Guid A_Param)
    {
        SqlDataReader reader = null;
        try
        {
            try
            {
                if (dbConn.State != ConnectionState.Closed) dbConn.Close();
    
                using (var cmd = new SqlCommand("MyStoredProc", dbConn))
                {
                    cmd.CommandType = CommandType.StoredProcedure;
                    cmd.Parameters.Add("@sp_Param", SqlDbType.UniqueIdentifier).Value = A_PAram
    
                    dbConn.Open();
                    reader = cmd.ExecuteReader();
    
                    var A_Class = StaticFunctionToGetInstanceOfClassFromResults( reader );
                    A_Class.rc = 1;
                    return A_Class 
                }
            }
            catch (Exception)
            {
                //server error
                return new A_Class(A_ClassError.beApiError);
            }
        }
        finally
        {
            if (reader != null) reader.Close();
            dbConn.Close();
        }            
    }
    
    public static async Task<A_Class> AStaticMethodAsync(SqlConnection dbConn, Guid A_Param)
    {
      try
      {
        if (dbConn.State != ConnectionState.Closed) dbConn.Close();
    
        using (var cmd = new SqlCommand("MyStoredProc", dbConn))
        {
          cmd.CommandType = CommandType.StoredProcedure;
          cmd.Parameters.Add("@sp_Param", SqlDbType.UniqueIdentifier).Value = A_PAram
    
          await dbConn.OpenAsync();
          using(SqlDataReader reader = await cmd.ExecuteReader())
          {
            var A_Class = StaticFunctionToGetInstanceOfClassFromResults( reader );
            A_Class.rc = 1;
            return A_Class
          }
        }
      }
      catch (Exception)
      {
        //server error
        // Why are you wrapping an exception instead of just passing it up the stack? This is weird.
        return new A_Class(A_ClassError.beApiError);
      }
    }
    
    [System.Web.Http.HttpGet]
    public async Task<A_Class> MyAPIMethodAsync(Guid b)
    {
      using (SqlConnection DB = new SqlConnection(this.dbConnString))
      {
        return await MyStaticHelper.AStaticMethodAsync(DB, b);
      }
    }