Graph-first programming in Microsoft 365

Microsoft’s SharePoint client-side object model library (CSOM) was updated to support .Net Standard. This is quite an achievement, and certainly unblocks numerous scenarios. But should you really just copy your code over to .Net Standard and be done with it? Many organizations are using multiple “workloads” in Microsoft 365. While SharePoint usage is quite large, and Lists has rolled out, the adoption of Teams and Planner and To Do and Outlook cannot be ignored. As a developer building solutions for uses on the Microsoft 365 platform, I feel it is incumbent upon me to provide value in areas that are not met by the platform. In many cases, this is in the “gaps” between the workloads.

Programming against the Microsoft Identity Platform and Microsoft Graph allows developers to bridge these gaps quite naturally. In the Identity Platform, I can register an application and request permissions from Microsoft Graph as well as from a long list of Microsoft APIs that includes Office 365, Azure and the PowerPlatform. I can call the Identity Platform requesting the appropriate scopes and I get a token. A common example is an application requesting a token for https://graph.microsoft.com/User.Read and https://[Tenant].sharepoint.com/MyFiles.Read. In the application, it is just a variable containing scopes – the code remains unchanged.

Graph-first programming in Microsoft 365

Similarly, using Microsoft Graph allows the code to access data across the workloads. Calling me/mail or me/files or me/manager will access a service transparently and return the requested data. (Here is a tip – append $whatif to the call in Graph Explorer and you can see what service is providing the data.) In the case of Teams and Groups, the url is almost identical: /v1.0/teams/{GUID} and /v1.0/groups/{GUID}.

Of course, there are scenarios when you need to fall back to the workload-specific API. This happened quite a bit when Microsoft Graph first launched, but the teams have been working hard to address the gaps. A recent example is the SharePoint Managed Metadata API, which is now exposed in Graph as Taxonomy. And user profiles are moving to graph as well.

There are areas of SharePoint that still have a gap. Some newer functionality, such as Site Designs and Hub sites have a REST API that feels like calling the Graph REST endpoint. But what if you use the Graph SDK? This is where things can go sideways quite rapidly.

If you are writing code that calls REST endpoints directly, you can stop here. The rest of this article discusses SDKs. 🙂

Using the CSOM library

If you have code that is consuming the CSOM library, then you are already familiar with the context load/execute paradigm that is uses. (Interesting story – the context load method was my first exposure to lambdas in C#. We’ve come a long way since then!) The following snippet, copied from the CSOM for .Net Standard announcement page, is an example of code written to consume the CSOM library.

using (var authenticationManager = new AuthenticationManager())
using (var context = authenticationManager.GetContext(site, user, password))
{
  context.Load(context.Web, p => p.Title);
  await context.ExecuteQueryAsync();
  Console.WriteLine($"Title: {context.Web.Title}");
}

This multi-line operation is common when accessing data. Whether it is a T-SQL SELECT clause or the Load method, we specify the properties/attributes we need. And we call out to the service to get the properties, handing connection issues (failures, throttling, etc.) along the way.

Using the Microsoft Graph SDK

The following code gets the same information using the Microsoft Graph instead of calling SharePoint directly. The obvious differences are the property name (Title => displayName) and the use of MSAL.Net to create an application object.

var publicClientApplication =
  PublicClientApplicationBuilder.Create(clientId).Build();

var authProvider =
  new InteractiveAuthenticationProvider(publicClientApplication, scopes);
  
var graphClient = new GraphServiceClient(authProvider);
var title = await graphClient.Sites[siteId]
                    .Request()
                    .Select(s => s.DisplayName)
                    .GetAsync()
Console.WriteLine($"Title: {title}");

Mind the gap

The table below summarizes the approaches for calling Microsoft 365.

TopicCSOM approachGraph SDK approach
AuthorizationResponibility of the Context objectResponsibility of the GraphServiceClient object
Property selectionExpressed via the Load method of the contextExpressed via the Select method of the request object
Call to serviceExecuteQuery methodGet/Add/Update methods
C# coding styleCode BlockFluid
context.Load(context.Web, p => p.Title); await context.ExecuteQueryAsync();graphClient.Sites[siteId].Request().Select(s => s.DisplayName).GetAsync()

Both approaches appear similar, with the coding style the biggest difference.

So, in an application that is calling both Microsoft Graph and SharePoint, what do you do?

  • Do you include code using both approaches?
  • Do you use separate libraries?
  • Do you register two different applications in the Identity Platform?
  • Do you share a token cache?

Bridging the gap

In applications that I design and develop, I strive to make life easier for the developer who looks at this code in the future. (Even if that would be me!) I strive to have consistent code, using the same patterns and conventions. I know I’m not alone, since code analysis tools exist to enforce many patterns and conventions.

I wrote and use the Graph.Community library to bridge this gap and provide a consistent approach to calling Microsoft 365 services. This library allows me to call services / endpoints that are not available thru the Microsoft Graph. (While I can call graph directly for a site or a list, what about site designs, or SharePoint search?) I can create a single client application (whether public or confidential) and leverage a single token cache and call services I need.

The library extends the Microsoft.Graph.Core library, which provides these benefits for me:

  • Add a token to a request
  • Manage OData parameters and related URL querystring
  • OData Batching (where supported)
  • Collection iteration and management
  • A middleware pipeline for logging important diagnostic information
  • Handles service issues and throttling

I have even used this same extension approach to build a client that calls a proprietary API.

Common developer approach for disparate APIs

I encourage you to review the Graph.Community library to see if it meets your need. The capabilities are documented on the roadmap page, and samples show how to use the custom requests as well as Identity platform features.

Lastly, the library is open-source on GitHub, so issues and PRs are welcome.

About the Author:

Paul Schaeflein is a solution architect/developer with experience in architecting, designing and developing software solutions. This experience covers a vast range of technologies, languages and industries.

Paul works at AddIn365, helping employees to use the right parts of Office 365 to complete their work. In doing so, we help organisations to utilise Office 365, maximising their return on investment in the Office 365 platform.

Paul is a co-host of the Microsoft 365 Developer Podcast. In addition, Paul is a top-rated speaker, having presented at SharePoint Evolutions, the SharePoint Evolutions Roadshow, the Microsoft SharePoint Conference and TechEd conferences, as well as user groups. In recognition of community efforts, Microsoft has awarded Paul as a Most Valuable Professional (MVP) ten times.

Reference:

Schaeflein, P. (2020). Graph-first programming in Microsoft 365. Available at: https://www.schaeflein.net/graph-first-programming-in-microsoft-365/ [Accessed: 21st December 2020].

Share this on...

Rate this Post:

Share: