Rystem.Content.Infrastructure.M365.Sharepoint
10.0.7
dotnet add package Rystem.Content.Infrastructure.M365.Sharepoint --version 10.0.7
NuGet\Install-Package Rystem.Content.Infrastructure.M365.Sharepoint -Version 10.0.7
<PackageReference Include="Rystem.Content.Infrastructure.M365.Sharepoint" Version="10.0.7" />
<PackageVersion Include="Rystem.Content.Infrastructure.M365.Sharepoint" Version="10.0.7" />
<PackageReference Include="Rystem.Content.Infrastructure.M365.Sharepoint" />
paket add Rystem.Content.Infrastructure.M365.Sharepoint --version 10.0.7
#r "nuget: Rystem.Content.Infrastructure.M365.Sharepoint, 10.0.7"
#:package Rystem.Content.Infrastructure.M365.Sharepoint@10.0.7
#addin nuget:?package=Rystem.Content.Infrastructure.M365.Sharepoint&version=10.0.7
#tool nuget:?package=Rystem.Content.Infrastructure.M365.Sharepoint&version=10.0.7
Rystem.Content.Infrastructure.M365.Sharepoint
This provider adds SharePoint Online support to the Content framework through Microsoft Graph.
It models one SharePoint document library as one IContentRepository registration.
Installation
dotnet add package Rystem.Content.Infrastructure.M365.Sharepoint
Authentication model
The current implementation uses Microsoft Graph with client credentials.
You need an app registration with application permissions such as:
Files.ReadWrite.All- or
Sites.ReadWrite.All
And you need:
TenantIdClientIdClientSecret
Architecture
Async registration does real setup work:
- it creates a
GraphServiceClient - resolves the target site and document library ids
- when a site-scoped library name is configured and the library is missing, it creates the document library
The registration extensions live in BuilderExtensions/ContentRepositoryBuilderExtensions.cs, the mapping rules in Options/SharepointConnectionSettings.cs, and the runtime behavior in SharepointRepository/SharepointRepository.cs plus SharepointServiceClient/SharepointServiceClientFactory.cs.
Registration API
| Method | Default lifetime | Notes |
|---|---|---|
WithSharepointIntegrationAsync(options, name, serviceLifetime) |
Transient |
preferred path because setup is async |
WithSharepointIntegration(options, name, serviceLifetime) |
Transient |
sync wrapper over the async implementation |
Mapping methods
Call exactly one of these on SharepointConnectionSettings:
| Method | Meaning |
|---|---|
MapWithSiteIdAndDocumentLibraryId(siteId, documentLibraryId) |
use exact ids |
MapWithSiteIdAndDocumentLibraryName(siteId, documentLibraryName) |
site id plus library name |
MapWithSiteNameAndDocumentLibraryName(siteName, documentLibraryName) |
find the site by name, then find or create the library |
MapWithRootSiteAndDocumentLibraryName(documentLibraryName) |
use the root site and a library name |
MapOnlyDocumentLibraryId(documentLibraryId) |
search by library id only |
MapOnlyDocumentLibraryName(documentLibraryName) |
search by library name only |
If your tenant has ambiguous site names, prefer the id-based methods.
Example
This matches src/Content/Rystem.Content.Tests/Rystem.Content.UnitTest/Startup.cs.
var repositories = builder.Services.AddContentRepository();
await repositories.WithSharepointIntegrationAsync(options =>
{
options.TenantId = builder.Configuration["Sharepoint:TenantId"];
options.ClientId = builder.Configuration["Sharepoint:ClientId"];
options.ClientSecret = builder.Configuration["Sharepoint:ClientSecret"];
options.MapWithSiteNameAndDocumentLibraryName(
builder.Configuration["Sharepoint:SiteName"]!,
builder.Configuration["Sharepoint:DocumentLibraryName"]!);
}, "sharepoint");
Resolve and use it:
public sealed class SharepointContentService
{
private readonly IContentRepository _repository;
public SharepointContentService(IFactory<IContentRepository> factory)
=> _repository = factory.Create("sharepoint");
public ValueTask<bool> UploadAsync(string path, byte[] data)
=> _repository.UploadAsync(path, data, new ContentRepositoryOptions
{
HttpHeaders = new ContentRepositoryHttpHeaders
{
ContentType = "application/pdf"
},
Metadata = new Dictionary<string, string>
{
["department"] = "legal"
},
Tags = new Dictionary<string, string>
{
["version"] = "1"
}
});
}
Provider behavior
UploadAsyncwrites file content throughItemWithPath(path).Content.PutAsync(...)ExistAsyncandGetPropertiesAsyncquery the file through GraphListAsyncrecursively enumerates foldersDeleteAsyncreturnstruewhen the item does not exist
This provider is the most conceptually different from Blob and File Share because it does not map ContentRepositoryOptions to native SharePoint storage primitives.
Important caveats
Options are stored in DriveItem.Description
SetPropertiesAsync(...) serializes the whole ContentRepositoryOptions object into DriveItem.Description.
GetPropertiesAsync(...) reads that description back and deserializes it.
Practical consequence:
- headers are not written as real SharePoint HTTP response headers
- metadata is not mapped to list columns by this provider
- tags are not native SharePoint tags
They round-trip because the provider stores its own JSON payload in the description field.
Site-name lookup is fuzzy
When you use MapWithSiteNameAndDocumentLibraryName(...), the provider searches Graph sites and takes the first match.
If you have multiple similarly named sites, use the id-based mapping methods instead.
Missing libraries can be created automatically
When a site is known and the requested document library name does not exist, the async setup path creates a document library for you.
Listing is recursive
Unlike the File Share provider, ListAsync(...) descends into child folders.
When to use this provider
Use it when you want:
- SharePoint document libraries behind the shared content API
- Microsoft 365 app-registration authentication
- recursive listing over folder structures
- convenient cross-provider migration targets
Be careful if you need native SharePoint field or header semantics, because this provider currently stores repository options in the item description instead.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | 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. |
-
net10.0
- Azure.Identity (>= 1.19.0)
- Microsoft.Graph (>= 5.103.0)
- Rystem.Content.Abstractions (>= 10.0.7)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 10.0.7 | 77 | 3/26/2026 |
| 10.0.6 | 182,046 | 3/3/2026 |
| 10.0.5 | 98 | 2/22/2026 |
| 10.0.4 | 106 | 2/9/2026 |
| 10.0.3 | 147,901 | 1/28/2026 |
| 10.0.1 | 209,075 | 11/12/2025 |
| 9.1.3 | 246 | 9/2/2025 |
| 9.1.2 | 764,493 | 5/29/2025 |
| 9.1.1 | 97,813 | 5/2/2025 |
| 9.0.32 | 186,718 | 4/15/2025 |
| 9.0.31 | 5,809 | 4/2/2025 |
| 9.0.30 | 88,837 | 3/26/2025 |
| 9.0.29 | 9,033 | 3/18/2025 |
| 9.0.28 | 242 | 3/17/2025 |
| 9.0.27 | 273 | 3/16/2025 |
| 9.0.26 | 289 | 3/13/2025 |
| 9.0.25 | 52,120 | 3/9/2025 |
| 9.0.21 | 339 | 3/6/2025 |
| 9.0.20 | 19,618 | 3/6/2025 |
| 9.0.19 | 345 | 3/6/2025 |