C# 在构造函数中异步设置控制器属性

C# 在构造函数中异步设置控制器属性,c#,asynchronous,dependency-injection,async-await,C#,Asynchronous,Dependency Injection,Async Await,我有一个完全异步的MVC API,它将一个接口注入其所有控制器: 公共页面控制器(IDataAdapter dataAdapter){ _dataAdapter=dataAdapter; } 接口的实现需要在其SQLConnection对象上设置访问令牌。托管身份安全访问需要此访问令牌(请参阅)。在构造控制器时需要设置该令牌,因此在每次新请求时都需要设置该令牌。令牌可以更改,因此我们不能让它成为静态值。这就是我们今天在接口实现中所做的: 公共部分类DataAdapter:BaseEntityD

我有一个完全异步的MVC API,它将一个接口注入其所有控制器:

公共页面控制器(IDataAdapter dataAdapter){
_dataAdapter=dataAdapter;
}
接口的实现需要在其SQLConnection对象上设置访问令牌。托管身份安全访问需要此访问令牌(请参阅)。在构造控制器时需要设置该令牌,因此在每次新请求时都需要设置该令牌。令牌可以更改,因此我们不能让它成为静态值。这就是我们今天在接口实现中所做的:

公共部分类DataAdapter:BaseEntityDataAdapter,IDataAdapter{ AppInsightsLogger _log=新的AppInsightsLogger(); #区域构造函数 /// ///实例化数据适配器并取出dsn信息 /// 公共DataAdapter():基(“db”){ 试一试{ var conn=base.Database.Connection作为System.Data.SqlClient.SqlConnection; conn.AccessToken=(新AzureServiceTokenProvider()).GetAccessTokenAsync(“https://database.windows.net/三、结果; }捕获(例外情况除外){ _LogError(ex); 投掷; } } } 您可以看到,我们使用.Result来计算异步操作,而异步操作实际上是同步运行的。我知道我应该使用工厂模式,如下所示:

public async Task<DataAdapter> ConstructAdapterAsync() {
    try {
        DataAdapter da = new DataAdapter();
        var conn = da.Database.Connection as System.Data.SqlClient.SqlConnection;
        conn.AccessToken = await (new AzureServiceTokenProvider()).GetAccessTokenAsync("https://database.windows.net/");

        return da;
    } catch (Exception ex) {
        _log.LogError(ex);
        throw;
    }
}
公共异步任务ConstructAdapterAsync(){
试一试{
DataAdapter da=新的DataAdapter();
var conn=da.Database.Connection作为System.Data.SqlClient.SqlConnection;
conn.AccessToken=await(新AzureServiceTokenProvider()).GetAccessTokenAsync(“https://database.windows.net/");
返回da;
}捕获(例外情况除外){
_LogError(ex);
投掷;
}
}

但这仍然给我留下了一个问题,在控制器构造函数中,如果不强制异步工厂函数同步运行,我就无法调用它。我是否可以异步设置此属性

当我需要类似的东西时,我使用了工厂模式,但有两个关键点:

  • 使工厂方法成为类本身中的静态方法,因为这样可以访问
    private
    属性和方法
  • 将构造函数标记为
    private
    ,这样没有使用工厂方法的人就不能使用该类
  • 在您的情况下,它看起来是这样的:

    公共部分类DataAdapter:BaseEntityDataAdapter,IDataAdapter{ AppInsightsLogger _log=新的AppInsightsLogger(); 私有DataAdapter():基(“db”){} 公共静态异步任务ConstructAdapterAsync(){ DataAdapter da=新的DataAdapter(); 试一试{ var conn=da.Database.Connection作为System.Data.SqlClient.SqlConnection; conn.AccessToken=await(新AzureServiceTokenProvider()).GetAccessTokenAsync(“https://database.windows.net/"); 返回da; }捕获(例外情况除外){ da.\u log.LogError(ex); 投掷; } } }
    当我需要一些类似的东西时,我使用了工厂模式,但有两个关键点:

  • 使工厂方法成为类本身中的静态方法,因为这样可以访问
    private
    属性和方法
  • 将构造函数标记为
    private
    ,这样没有使用工厂方法的人就不能使用该类
  • 在您的情况下,它看起来是这样的:

    公共部分类DataAdapter:BaseEntityDataAdapter,IDataAdapter{ AppInsightsLogger _log=新的AppInsightsLogger(); 私有DataAdapter():基(“db”){} 公共静态异步任务ConstructAdapterAsync(){ DataAdapter da=新的DataAdapter(); 试一试{ var conn=da.Database.Connection作为System.Data.SqlClient.SqlConnection; conn.AccessToken=await(新AzureServiceTokenProvider()).GetAccessTokenAsync(“https://database.windows.net/"); 返回da; }捕获(例外情况除外){ da.\u log.LogError(ex); 投掷; } } }
    简单的答案是:不要在构造函数中执行异步操作,而是在其他地方初始化这些操作。也不要使用
    .Result
    ,尤其是在ASP.NET中,因为这样可能会导致阻塞。@DavidG当然,但在这种情况下,是否还有其他解决方法?当一个新的请求进入时(据我所知),框架会自动调用API控制器的构造函数,并且需要在数据库上的任何内容进入之前设置此属性。也许您需要一个创建SQL连接的工厂。控制器构造函数可以将工厂作为依赖项。然后,当您需要一个SQL连接(在您的操作方法中)时,您可以在工厂中调用异步方法,以获得具有访问令牌的正确构造的连接。简单的答案是:不要在构造函数中执行异步操作,请在别处初始化这些操作。也不要使用
    .Result
    ,尤其是在ASP.NET中,因为这样可能会导致阻塞。@DavidG当然,但在这种情况下,是否还有其他解决方法?当一个新的请求进入时(据我所知),框架会自动调用API控制器的构造函数,并且需要在数据库上的任何内容进入之前设置此属性。也许您需要一个创建SQL连接的工厂。控制器构造函数可以将工厂作为依赖项。然后,当您需要一个SQL连接(在您的操作方法中)时,您可以在工厂上调用异步方法,以获得一个具有访问令牌的正确构造的连接。