Com.H.GraphAPI
10.0.0
dotnet add package Com.H.GraphAPI --version 10.0.0
NuGet\Install-Package Com.H.GraphAPI -Version 10.0.0
<PackageReference Include="Com.H.GraphAPI" Version="10.0.0" />
<PackageVersion Include="Com.H.GraphAPI" Version="10.0.0" />
<PackageReference Include="Com.H.GraphAPI" />
paket add Com.H.GraphAPI --version 10.0.0
#r "nuget: Com.H.GraphAPI, 10.0.0"
#:package Com.H.GraphAPI@10.0.0
#addin nuget:?package=Com.H.GraphAPI&version=10.0.0
#tool nuget:?package=Com.H.GraphAPI&version=10.0.0
Com.H.GraphAPI
Simplified way to use Microsoft Graph API in .NET applications. In its current form it supports app-only authentication and sending email.
- Authentication —
Com.H.GraphAPI.Identity.GIExtensions, OAuth 2.0 client credentials (app-only, no signed-in user), wrapping MSAL. - Email —
Com.H.GraphAPI.Mail.Message, posting to the GraphsendMailendpoint.
Kindly visit the project's github page for full documentation https://github.com/H7O/Com.H.GraphAPI
A single package multi-targets netstandard2.0, net8.0, net9.0 and net10.0. That
covers .NET 8 and later, and — through .NET Standard 2.0 — .NET Framework 4.6.2+ (4.7.2 or
later recommended). Earlier releases shipped as two separate lines; from 10.0.0 there is
just one package.
Sending requires an app registration with the Mail.Send application permission and
admin consent granted.
Installation
Nuget
Install-Package Com.H.GraphAPI
.NET CLI
dotnet add package Com.H.GraphAPI
That's all — there is nothing to add to your project file, on any framework.
Quick start
using Com.H.GraphAPI.Identity;
using Com.H.GraphAPI.Mail;
string clientId = "Your Client ID";
string clientSecret = "Your Client Secret";
string tenantId = "Your Tenant ID";
var accessToken = await GIExtensions.GetAccessTokenAsync(clientId, clientSecret, tenantId);
var msg = new Message
{
From = "yourName@yourCompany.com",
Subject = "Testing MS Graph API",
Body = "This is a test email sent via <strong>MS Graph API</strong>"
};
msg.To.Add("yourName@yourCompany.com");
// read from a file path
msg.Attachments.Add(@"c:\temp\email\test1.txt");
// add content directly
msg.Attachments.AddContent("direct content test", "test2.txt");
// read from an IO Stream
using (var fs = new FileStream(@"c:\temp\email\test3.txt", FileMode.Open, FileAccess.Read))
{
await msg.Attachments.AddAsync(fs, "test3.txt");
// the stream can be safely closed here as it is read immediately.
}
using var result = await msg.SendAsync(accessToken);
Console.WriteLine(result.IsSuccessStatusCode
? "Email sent successfully"
: "Email failed to send");
Every async method has a synchronous counterpart — GetAccessToken, Send.
Re-using an access token
Tokens last about an hour. Set a delegate and it is called during send, but only when no
token was passed to SendAsync:
msg.GetAccessTokenAsyncDelegate = async cancellationToken =>
{
var token = await GIExtensions.GetAccessTokenWithExpiryDateAsync(
clientId, clientSecret, tenantId, cancellationToken);
Console.WriteLine($"access token expires on {token.ExpiresOn}");
return token.AccessToken;
};
using var result = await msg.SendAsync(); // no token argument
GIExtensions also keeps one MSAL application per credential set, so repeated calls are
served from MSAL's token cache rather than hitting Entra ID every time.
Error handling
msg.ThrowOnFailure = true; // default is false: SendAsync returns the response instead
try
{
using var result = await msg.SendAsync(accessToken, cancellationToken);
}
catch (GraphApiException ex) // Com.H.GraphAPI
{
// Graph explains *why* it refused in the body, not the status line.
Console.WriteLine($"{ex.StatusCode}: {ex.ResponseBody}");
}
Supplying your own HttpClient
You don't have to do anything here — the library creates one HttpClient and reuses it for
every message. Swap in your own if you need a different timeout, a proxy, or
IHttpClientFactory:
// used by every message from now on
Message.SharedHttpClient = new HttpClient { Timeout = TimeSpan.FromMinutes(2) };
// ...or just by this one message
msg.HttpClient = httpClientFactory.CreateClient("graph");
If you're on .NET Framework: mentioning HttpClient or HttpResponseMessage by name
in your own code means adding using System.Net.Http; to the top of that file. .NET 8 and
later add that line for you behind the scenes; .NET Framework doesn't. If the compiler says
it can't find the type HttpClient, that's why — and letting var figure out the types
avoids it entirely.
API at a glance
Message — From (required) · To / Cc / Bcc / ReplyTo (List<string>) ·
Subject · Body · DisableHtmlBody · Importance (Low/Normal/High) ·
SaveToSentItems · Attachments · GetAccessTokenDelegate ·
GetAccessTokenAsyncDelegate · ThrowOnFailure · MaxRequestSizeBytes (default 4 MB,
0 disables) · GraphApiBaseAddress · HttpClient and static SharedHttpClient ·
SendAsync(accessToken?, cancellationToken?) · Send(accessToken?, cancellationToken?).
Message.Attachments (MailAttachmentCollection, an IReadOnlyList<MailAttachment>) —
Add(filePath, fileName?) · Add(stream, fileName) · AddContent(string|byte[], fileName) ·
AddAsync(...) variants of each · Remove(fileNameOrFilePath) · Clear() · Count ·
indexer · TotalContentLength. Each MailAttachment has FileName, FilePath,
Content (byte[]), ContentType, IsInline and ContentId for cid: inline images.
GIExtensions — GetAccessTokenAsync · GetAccessTokenWithExpiryDateAsync
(returns (AccessToken, ExpiresOn)) · RequestAccessTokenAsync (MSAL
AuthenticationResult) · ClearApplicationCache(), each with a synchronous counterpart.
All take (clientId, clientSecret, tenantId, cancellationToken = default, scopes = null, authorityHost = "https://login.microsoftonline.com").
MailExtensions — bool IsEmail(this string?).
Notes — a Message is a builder, not a one-shot: sending doesn't modify it, so the
same instance can be sent repeatedly. Malformed recipients are dropped from the payload.
Graph caps sendMail at 4 MB including base64-expanded attachments.
Upgrading from 2.0.0.x
10.0.0 fixes payload corruption when a subject, body or attachment name contained a quote,
backslash or newline; adds CancellationToken support; shares one HttpClient instead of
creating one per send; and stops mutating the message while sending. See the GitHub page
for the full list, including the handful of breaking changes.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Microsoft.Identity.Client (>= 4.87.0 && < 5.0.0)
- System.Text.Json (>= 10.0.10)
-
net10.0
- Microsoft.Identity.Client (>= 4.87.0 && < 5.0.0)
-
net8.0
- Microsoft.Identity.Client (>= 4.87.0 && < 5.0.0)
-
net9.0
- Microsoft.Identity.Client (>= 4.87.0 && < 5.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Single multi-targeted package (netstandard2.0 / net8.0 / net9.0 / net10.0) replacing the separate 2.0.0.x .NET Standard line. Payloads are now built with Utf8JsonWriter, so subjects, bodies and file names containing quotes, backslashes or newlines are escaped correctly instead of corrupting the request. Adds CancellationToken support, shared HttpClient reuse, MSAL token-cache reuse, and an injectable HttpClient for testing.