【问题标题】:Dependency injection problem - Initializing remote service connection依赖注入问题 - 初始化远程服务连接
【发布时间】:2020-06-14 21:19:04
【问题描述】:

在我的 .Net Core 3.0 应用程序中,我想使用 Microsoft Graph Nuget 库。我创建了一个连接类,它使用[MSAL][1] 对我的应用程序进行身份验证,然后创建连接并返回它。我的想法是使用Dependency Injection 在构造函数中注入这个连接对象。但是,由于创建连接的方法是异步的,我似乎有一个问题,如何在构造函数中使用它。

我的连接类

 public class AuthorizeGraphApi: IAuthorizeGraphApi
    {
        private readonly IConfiguration _config;

        public AuthorizeGraphApi(IConfiguration config)
        {
            _config = config;
        }

        public async Task<GraphServiceClient> ConnectToAAD()
        {
            string accessToken = await GetAccessTokenFromAuthorityAsync();
            var graphServiceClient = new GraphServiceClient(new DelegateAuthenticationProvider((requestMessage) => {
                requestMessage
                    .Headers
                    .Authorization = new AuthenticationHeaderValue("Bearer", accessToken);

                return Task.FromResult(0);
            }));
            return graphServiceClient;
        }

        private async Task<string> GetAccessTokenFromAuthorityAsync()
        {
            // clientid, authUri, etc removed for this example.
            IConfidentialClientApplication _conn;
            _conn = ConfidentialClientApplicationBuilder.Create(clientId)
                .WithClientSecret(clientSecret)
                .WithAuthority(new Uri(authUri))
                .Build();
           string[] scopes = new string[] { $"api://{clientId}/.default" };

           AuthenticationResult result = null;
           // AcquireTokenForClient only has async method.
           result = await _conn.AcquireTokenForClient(scopes)
              .ExecuteAsync();

           return result.AccessToken;
        }
    }

我的图表服务发送请求

public class AzureIntuneService
{
    private readonly IAuthorizeGraphApi _graphClient;
    public AzureIntuneService(IAuthorizeGraphApi client)
    {
        //Gives: cannot implicitely convert to Threading.Tasks.Task.... error
        _graphClient = client.ConnectToAAD();
    }

    public async Task<IList<string>> GetAADInformationAsync()
    {
        // then here, use the graphClient object for the request...
        var payload = await _graphClient.Groups.Request().GetAsync();
        return payload
    }
}

我在我的启动中注册了上述类如下:

services.AddScoped<IAuthorizeGraphApi, AuthorizeGraphApi>();

我的想法是这样,我不需要在每个方法中调用 _graphClient。如何以正确的方式注入连接对象?或者关于这个(注入连接对象)的最佳实践是什么?

【问题讨论】:

标签: c# .net-core dependency-injection async-await


【解决方案1】:

一种方法是存储对Task 的引用,并确保使用该连接的任何公共方法都是async

public class AzureIntuneService
{
    private readonly Task<GraphServiceClient> _graphClientTask;
    public AzureIntuneService(IAuthorizeGraphApi client)
    {
        _graphClientTask = client.ConnectToAAD();
    }

    public async Task<IList<string>> GetAADInformationAsync()
    {
        var client = await _graphClientTask; // Get the client when connected
        var payload = await client.Groups.Request().GetAsync();
        return payload;
    }
}

【讨论】:

    【解决方案2】:

    构造函数不是async,并且永远不应该用于初始化任何async。解决此问题的唯一方法是通过执行.Result 进行异步同步,这总是一个问题。

    在您的情况下,接受DelegateAuthenticationProviderGraphServiceClient 接受AuthenticateRequestAsyncDelegate。这允许您有一个async 委托来构造客户端。

    所以现在你可以做

    new DelegateAuthenticationProvider(async requestMessage =>  
        {
            string accessToken = await GetAccessTokenFromAuthorityAsync(); 
            //rest of code here
        } 
    )
    

    这允许您将ConnectToAAD 签名更改为只返回GraphServiceClient 而不是Task&lt;GraphServiceClient&gt;

    【讨论】:

      【解决方案3】:

      当您需要异步数据时,您必须远离常规构造函数并创建工厂方法(私有静态函数)。如下所示:

      public sealed class MyClass
      {
        private MyData asyncData;
        private MyClass() { ... }
      
        private async Task<MyClass> InitializeAsync()
        {
          asyncData = await GetDataAsync();
          return this;
        }
      
        public static Task<MyClass> CreateAsync()
        {
          var ret = new MyClass();
          return ret.InitializeAsync();
        }
      }
      
      public static async Task UseMyClassAsync()
      {
        MyClass instance = await MyClass.CreateAsync();
        ...
      }
      

      更多:https://blog.stephencleary.com/2013/01/async-oop-2-constructors.html

      【讨论】:

      • 唯一的问题是 async 工厂方法不适用于 DI 框架。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-27
      • 1970-01-01
      • 2021-05-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多