How to use DI with Microsoft Graph

Viewed 4983

I have a .net web api core project and I will call microsoft graph

So I created a configuration class:

public class GraphConfiguration
    {
        public static void Configure(IServiceCollection services, IConfiguration configuration)
        {
            //Look at appsettings.Development.json | https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-2.1
            var graphConfig = new AppSettingsSection();
            configuration.GetSection("AzureAD").Bind(graphConfig);

            IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
                .Create(graphConfig.ClientId)
                .WithTenantId(graphConfig.TenantId)
                .WithClientSecret(graphConfig.ClientSecret)
                .Build();
                
            ClientCredentialProvider authenticationProvider = new ClientCredentialProvider(confidentialClientApplication);
            GraphServiceClient graphServiceClient = new GraphServiceClient(authenticationProvider);     

        }
    }

and in my controller I have this:

 public class UserController : ControllerBase
    {
        private TelemetryClient telemetry;
        private readonly ICosmosStore<Partner> _partnerCosmosStore;
        private readonly GraphServiceClient _graphServiceClient;


        // Use constructor injection to get a TelemetryClient instance.
        public UserController(TelemetryClient telemetry,ICosmosStore<Partner> partnerCosmosStore, GraphServiceClient graphServiceClient)
        {
            this.telemetry = telemetry;
             _partnerCosmosStore = partnerCosmosStore; 
             _graphServiceClient = graphServiceClient;          
        }

        /// <summary>
        /// Gets all partners
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public async Task<ActionResult> GetUsers()
        {
            this.telemetry.TrackEvent("GetPartners");
        
            try
            {
                var me = await _graphServiceClient.Me.Request().WithForceRefresh(true).GetAsync();
                return Ok(me);
            }
            catch (Exception ex)
            {
                string guid = Guid.NewGuid().ToString();
                var dt = new Dictionary<string, string>
                {
                    { "Error Lulo: ", guid }
                };
                telemetry.TrackException(ex, dt);
                return BadRequest("Error Lulo: " + guid);
            }
        }      
    }

But I think I am missing a step here, in Startupcs. how do I actually inject it in all controllers?

3 Answers

I would suggest registering the GraphServiceClient with the DI container

public static class GraphConfiguration {
    //Called in Startup - services.ConfigureGraphComponent(configuration)
    public static IServiceCollection ConfigureGraphComponent(this IServiceCollection services, IConfiguration configuration) {
        //Look at appsettings.Development.json | https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-2.1
        
        AppSettingsSection graphConfig = configuration.GetSection("AzureAD").Get<AppSettingsSection>();

        IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
            .Create(graphConfig.ClientId)
            .WithTenantId(graphConfig.TenantId)
            .WithClientSecret(graphConfig.ClientSecret)
            .Build();
            
        ClientCredentialProvider authenticationProvider = new ClientCredentialProvider(confidentialClientApplication);

        //you can use a single client instance for the lifetime of the application
        services.AddSingleton<GraphServiceClient>(sp => {
            return new GraphServiceClient(authenticationProvider);
        });

        return services;
    }
}

That way the container will know how to inject the desired dependency when resolving your controller

I wanted to expand on the answer by @Nkosi with what worked for me in .NET 6.0

Packages required:

Microsoft.Graph 4.x.x
Microsoft.Graph.Auth 1.0.0-preview (not production-ready if that's an issue)

My GraphConfiguration class is slightly different since AppSettingsSection did not expose .ClientId, .TenantId, etc.

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Graph;
using Microsoft.Graph.Auth;
using Microsoft.Identity.Client;

namespace EMF.Events.Business.Providers.Graph
{
    public static class GraphConfiguration
    {
        public static IServiceCollection ConfigureGraphClient( this IServiceCollection services, IConfiguration configuration )
        {
            var graphConfig = configuration.GetSection( "AzureAd" );

            var confidentialClientApplication = ConfidentialClientApplicationBuilder
                .Create( graphConfig["ClientId"] )
                .WithTenantId( graphConfig["TenantId"] )
                .WithClientSecret( graphConfig["ClientSecret"] )
                .Build();

            ClientCredentialProvider authenticationProvider = new ClientCredentialProvider( confidentialClientApplication );

            services.AddScoped( sp =>
            {
                return new GraphServiceClient( authenticationProvider );
            } );

            return services;
        }
    }
}

And then in my Program.cs...

builder.Services.ConfigureGraphClient( builder.Configuration );

And your appsettings.json should have the following (probably along with some other keys which you should not remove:

"AzureAd": {
    ...
    "TenantId": "<TENANT_ID>",
    "ClientId": "<CLIENT_ID>",
    "ClientSecret": "<CLIENT_SECRET>",
  },

One other note for that section is that it is case-sensitive so if yours is "AzureAD" instead of "AzureAd", adjust accordingly.

I think you should make your method return an IServiceCollection and then call it in your startup.cs.

    public class GraphConfiguration
        {
            public static IServiceCollection AddGraphComponent(this IServiceCollection services, IConfiguration configuration)
            {
                //your code

                services.AddSingleton<ClientCredentialProvider>(x =>
                {
                     return new GraphServiceClient(authenticationProvider);
                });

                services.AddSingleton<GraphServiceClient>(x =>
                {
                     return new ClientCredentialProvider(confidentialClientApplication);
                });    

                return services    
            }
        }

Then call this method in the ConfigureServices method in your startup.cs.

 services.AddGraphComponent(yourConfiguration);
Related