This guide provides a comprehensive overview of migrating from v1.x of the Microsoft Graph .NET SDK to the current v5. If you’re upgrading from v1.x, you’ll need to account for breaking changes introduced in v3, v4, and v5.
Migrating from v1.x to v5 involves significant breaking changes across multiple versions. Plan adequate time for testing and validation.
// Using DelegateAuthenticationProvidervar authenticationProvider = new DelegateAuthenticationProvider( async (requestMessage) => { var accessToken = await GetAccessTokenAsync(); requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); });var graphServiceClient = new GraphServiceClient(authenticationProvider);
v5 Approach:
using Azure.Identity;using Microsoft.Graph;// Using TokenCredential from Azure.Identityvar interactiveBrowserCredential = new InteractiveBrowserCredential( new InteractiveBrowserCredentialOptions { ClientId = clientId, TenantId = tenantId });var graphServiceClient = new GraphServiceClient(interactiveBrowserCredential);
Custom authentication in v5:
using Microsoft.Kiota.Abstractions.Authentication;public class TokenProvider : IAccessTokenProvider{ public Task<string> GetAuthorizationTokenAsync( Uri uri, Dictionary<string, object> additionalAuthenticationContext = default, CancellationToken cancellationToken = default) { var token = "your-access-token"; return Task.FromResult(token); } public AllowedHostsValidator AllowedHostsValidator { get; }}var authenticationProvider = new BaseBearerTokenAuthenticationProvider(new TokenProvider());var graphServiceClient = new GraphServiceClient(authenticationProvider);
using Microsoft.Graph.Models;// Get uservar user = await graphServiceClient .Me .GetAsync();// Update user (UpdateAsync renamed to PatchAsync)await graphServiceClient .Users[userId] .PatchAsync(user);// Add group (AddAsync renamed to PostAsync)await graphServiceClient .Groups .PostAsync(group);
using Microsoft.Graph;// All types were in Microsoft.Graph namespaceUser user = new User();GraphServiceClient client = new GraphServiceClient(authProvider);
v5:
using Microsoft.Graph;using Microsoft.Graph.Models; // Model types in separate namespaceusing Microsoft.Graph.Me.SendMail; // Request body types in operation namespacesUser user = new User();GraphServiceClient client = new GraphServiceClient(authProvider);
try{ var user = await graphServiceClient.Me.Request().GetAsync();}catch (ServiceException ex){ Console.WriteLine($"Error: {ex.Error.Code}"); Console.WriteLine($"Message: {ex.Error.Message}"); Console.WriteLine($"Status Code: {ex.StatusCode}");}
v5:
using Microsoft.Graph.Models.ODataErrors;try{ var user = await graphServiceClient.Me.GetAsync();}catch (ODataError odataError){ Console.WriteLine($"Error: {odataError.Error.Code}"); Console.WriteLine($"Message: {odataError.Error.Message}"); // Status code available in the exception details throw;}
var users = await graphServiceClient .Users .Request() .GetAsync();// Iterate through current pageforeach (var user in users){ Console.WriteLine(user.DisplayName);}// Get next pageif (users.NextPageRequest != null){ var nextPageUsers = await users.NextPageRequest.GetAsync();}
v5:
var usersResponse = await graphServiceClient .Users .GetAsync();// Access the collection via Value propertyforeach (var user in usersResponse.Value){ Console.WriteLine(user.DisplayName);}// Next page link is in OdataNextLink propertyif (!string.IsNullOrEmpty(usersResponse.OdataNextLink)){ // Use PageIterator or create new request builder with the link}
If you were using Newtonsoft.Json for custom serialization:v1.x/v3 (Newtonsoft.Json):
using Newtonsoft.Json.Linq;var customData = user.AdditionalData["customField"] as JObject;var value = customData["property"].ToString();
v4/v5 (System.Text.Json):
using System.Text.Json;if (user.AdditionalData["customField"] is JsonElement jsonElement){ if (jsonElement.ValueKind == JsonValueKind.Object) { var property = jsonElement.GetProperty("property"); var value = property.GetString(); }}
using Microsoft.Graph.Me.SendMail;// Send mail with parameter objectvar body = new SendMailPostRequestBody{ Message = message, SaveToSentItems = saveToSentItems};await graphServiceClient.Me .SendMail .PostAsync(body);
Automatically track and send only changed properties:
// Get the objectvar event = await graphServiceClient .Me.Events["event-id"] .GetAsync();// Only modified properties will be sentevent.Subject = "Updated Subject";event.Recurrence = null; // Can now set to null directlyawait graphServiceClient.Me.Events["event-id"] .PatchAsync(event);
// Get only users from group membersvar usersInGroup = await graphServiceClient .Groups["group-id"] .Members .GraphUser .GetAsync();// Get only applications from group membersvar applicationsInGroup = await graphServiceClient .Groups["group-id"] .Members .GraphApplication .GetAsync();
'IUserItemRequestBuilder' does not contain a definition for 'UpdateAsync'
Solution:
// Use PatchAsync instead of UpdateAsyncawait graphServiceClient.Users[userId].PatchAsync(user);// Use PostAsync instead of AddAsyncawait graphServiceClient.Groups.PostAsync(group);
Cannot convert from 'UserCollectionResponse' to 'IEnumerable<User>'
Solution:
var usersResponse = await graphServiceClient.Users.GetAsync();// Access collection via .Value propertyforeach (var user in usersResponse.Value){ // Process user}
Open a new issue if you find a bug or need assistance
This migration guide is comprehensive but may not cover every edge case. Always test thoroughly in a development environment before deploying to production.