Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# <img src="img/CSharp-Toolkit-Icon.png" alt="Backend Toolkit" width="64px" />Orleans.Multitenant
Secure, flexible tenant separation for Microsoft Orleans 8
Secure, flexible tenant separation for Microsoft Orleans 9

> [![Nuget (with prereleases)](https://img.shields.io/nuget/vpre/Orleans.Multitenant?color=gold&label=NuGet:%20Orleans.Multitenant&style=plastic)](https://www.nuget.org/packages/Orleans.Multitenant)<br />
> (install in silo client and grain implementation projects)

_(Note: this repo was transferred from Applicita to VincentH-Net on March 17, 2025 to reflect who actively maintains it)_

## Summary
[Microsoft Orleans 8](https://github.com/dotnet/orleans/releases/tag/v8.0.0) is a great technology for building distributed, cloud-native applications. It was designed to reduce the complexity of building this type of applications for C# developers.
[Microsoft Orleans 9](https://github.com/dotnet/orleans/releases/tag/v9.0.0) is a great technology for building distributed, cloud-native applications. It was designed to reduce the complexity of building this type of applications for C# developers.

However, creating multi tenant applications with Orleans out of the box requires careful design, complex coding and significant testing to prevent unintentional leakage of communication or stored data across tenants. Orleans.Multitenant adds this capability to Orleans for free, as an uncomplicated, flexible and extensible API that lets developers:

Expand Down
10 changes: 5 additions & 5 deletions src/Example/Apis/Apis.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Orleans.Persistence.AzureStorage" Version="8.2.0" />
<PackageReference Include="Microsoft.Orleans.Persistence.Memory" Version="8.2.0" />
<PackageReference Include="Microsoft.Orleans.Sdk" Version="8.2.0" />
<PackageReference Include="Microsoft.Orleans.Persistence.AzureStorage" Version="9.1.2" />
<PackageReference Include="Microsoft.Orleans.Persistence.Memory" Version="9.1.2" />
<PackageReference Include="Microsoft.Orleans.Sdk" Version="9.1.2" />
<PackageReference Include="Orleans.Multitenant" Version="2.2.12" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.7" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.7.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.14" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="8.0.0" />
</ItemGroup>

<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion src/Example/Apis/Foundation/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options => {
options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, $"{System.Reflection.Assembly.GetExecutingAssembly().GetName().Name}.xml"));
options.SwaggerDoc("v1", new OpenApiInfo { Title = "Example Orleans 8 Multitenant API", Version = "v1" });
options.SwaggerDoc("v1", new OpenApiInfo { Title = "Example Orleans 9 Multitenant API", Version = "v1" });
options.OperationFilter<TenantHeader.AddAsOpenApiParameter>();
});

Expand Down
2 changes: 1 addition & 1 deletion src/Example/Contracts/Contracts.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Orleans.Sdk" Version="8.2.0" />
<PackageReference Include="Microsoft.Orleans.Sdk" Version="9.1.2" />
</ItemGroup>

</Project>
8 changes: 5 additions & 3 deletions src/Example/Contracts/Foundation/Result.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Version: 1.0.0 (Using https://semver.org/)
// Updated: 2022-11-10
// See https://github.com/Applicita/Orleans.Results for updates to this file.
// See https://github.com/VincentH-Net/Orleans.Results for updates to this file.

using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
Expand All @@ -17,7 +17,7 @@ public class Result : ResultBase<ErrorNr>
public static Result Ok { get; } = new();

public Result(ImmutableArray<Error> errors) : base(errors) { }
public Result(IEnumerable<Error> errors) : base(ImmutableArray.CreateRange(errors)) { }
public Result(IEnumerable<Error> errors) : base([.. errors]) { }
Result() { }
Result(Error error) : base(error) { }

Expand All @@ -34,7 +34,7 @@ public Result(IEnumerable<Error> errors) : base(ImmutableArray.CreateRange(error
public class Result<TValue> : ResultBase<ErrorNr, TValue>
{
public Result(ImmutableArray<Error> errors) : base(errors) { }
public Result(IEnumerable<Error> errors) : base(ImmutableArray.CreateRange(errors)) { }
public Result(IEnumerable<Error> errors) : base([.. errors]) { }
Result(TValue value) : base(value) { }
Result(Error error) : base(error) { }

Expand All @@ -49,6 +49,7 @@ public Result(IEnumerable<Error> errors) : base(ImmutableArray.CreateRange(error
[SuppressMessage("Naming", "CA1724:Type names should not match namespaces", Justification = "Namespace is generated by Orleans")]
public abstract class ResultBase<TErrorNr, TValue> : ResultBase<TErrorNr> where TErrorNr : Enum
{
[SuppressMessage("Style", "IDE0032:Use auto property", Justification = "Backing field must be nullable while property is not nullable, different rules are enforced in code for field access versus property access")]
[Id(0)] TValue? value;

protected ResultBase(TValue value) => this.value = value;
Expand Down Expand Up @@ -116,6 +117,7 @@ public abstract class ResultBase<TErrorNr> where TErrorNr : Enum
/// <param name="validationErrors">If the return value is true, receives all errors in a dictionary suitable for serializing into a https://tools.ietf.org/html/rfc7807 based format; otherwise set to null</param>
/// <returns>True for a failed result that has the <paramref name="validationErrorFlag"/> set in the <typeparamref name="TErrorNr"/> for <b>all</b> errors; false otherwise</returns>
[SuppressMessage("Style", "IDE0001:Simplify Names", Justification = "Full name is necessary to ensure link in inline documentation works independently of global usings")]
[SuppressMessage("Style", "IDE0306:Simplify collection initialization", Justification = "Dictionary<string, string[]> does not have the required Add method")]
public bool TryAsValidationErrors(TErrorNr validationErrorFlag, [NotNullWhen(true)] out Dictionary<string, string[]>? validationErrors)
{
if (IsFailed && Errors.All(error => error.Nr.HasFlag(validationErrorFlag)))
Expand Down
2 changes: 1 addition & 1 deletion src/Example/Services.Tenant/Services.Tenant.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Orleans.Sdk" Version="8.2.0" />
<PackageReference Include="Microsoft.Orleans.Sdk" Version="9.1.2" />
<PackageReference Include="Orleans.Multitenant" Version="2.2.12" />
</ItemGroup>

Expand Down
1 change: 0 additions & 1 deletion src/Orleans.Multitenant/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using Microsoft.Extensions.Options;
using Orleans.Multitenant.Internal;
using Orleans.Providers;
using Orleans.Runtime;
using Orleans.Storage;

namespace Orleans.Multitenant;
Expand Down
1 change: 0 additions & 1 deletion src/Orleans.Multitenant/Internal/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using Microsoft.Extensions.Options;
using Orleans.Configuration;
using Orleans.Providers;
using Orleans.Runtime;
using Orleans.Storage;

namespace Orleans.Multitenant.Internal;
Expand Down
1 change: 0 additions & 1 deletion src/Orleans.Multitenant/Internal/Logging.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Microsoft.Extensions.Logging;
using Orleans.Runtime;
using static Orleans.Multitenant.Internal.LoggingParameter;
using Event = Orleans.Multitenant.Internal.LoggingEvent;

Expand Down
5 changes: 2 additions & 3 deletions src/Orleans.Multitenant/Internal/SiloLifecycle.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System.Collections.Concurrent;
using System.Collections.ObjectModel;
using Microsoft.Extensions.Logging;
using Orleans.Runtime;

namespace Orleans.Multitenant.Internal;

Expand Down Expand Up @@ -130,7 +129,7 @@ async Task OnStart(int lifecycleIndex, CancellationToken ct)
{
int stage = subscriptionsForStage.Key;
logger.ReplayingSiloLifecycleStartForTenant(subscriptionsForStage.Count(), stage);
await Task.WhenAll(subscriptionsForStage.Select(s => s.Observer.OnStart(ct)).ToArray()).ConfigureAwait(false);
await Task.WhenAll([.. subscriptionsForStage.Select(s => s.Observer.OnStart(ct))]).ConfigureAwait(false);
HighestCompletedStage = stage;
}
}
Expand All @@ -148,7 +147,7 @@ public async Task OnStop(int lifecycleIndex, CancellationToken ct)
{
int stage = subscriptionsForStage.Key;
logger.ForwardingSiloLifecycleStopForTenant(subscriptionsForStage.Count(), stage);
await Task.WhenAll(subscriptionsForStage.Select(s => s.Observer.OnStop(ct)).ToArray()).ConfigureAwait(false);
await Task.WhenAll([.. subscriptionsForStage.Select(s => s.Observer.OnStop(ct))]).ConfigureAwait(false);
}
}

Expand Down
3 changes: 1 addition & 2 deletions src/Orleans.Multitenant/Internal/StorageProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Orleans.Runtime;
using Orleans.Storage;

namespace Orleans.Multitenant.Internal;

[System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1812:Avoid uninstantiated internal classes", Justification = "Class is instantiated through DI")]
[SuppressMessage("Performance", "CA1812:Avoid uninstantiated internal classes", Justification = "Class is instantiated through DI")]
sealed class MultitenantStorage : IGrainStorage, ILifecycleParticipant<ISiloLifecycle>
{
readonly string name;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using Orleans.Runtime;

namespace Orleans.Multitenant.Internal;
namespace Orleans.Multitenant.Internal;

[SuppressMessage("Performance", "CA1812:Avoid uninstantiated internal classes", Justification = "Class is instantiated through DI")]
sealed class TenantSeparatingCallFilter(IGrainCallTenantSeparator separator, ICrossTenantAuthorizer authorizer) : IIncomingGrainCallFilter
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
using Microsoft.Extensions.Logging;
using Orleans.Runtime;
using Orleans.Streams.Filtering;

namespace Orleans.Multitenant.Internal;

interface ITenantEvent { }

[System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1812:Avoid uninstantiated internal classes", Justification = "Class is instantiated through DI")]
[SuppressMessage("Performance", "CA1812:Avoid uninstantiated internal classes", Justification = "Class is instantiated through DI")]
sealed class TenantSeparatingStreamFilter : IStreamFilter
{
readonly ILogger logger;
Expand Down
2 changes: 1 addition & 1 deletion src/Orleans.Multitenant/Internal/TenantStreamObservers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ namespace Orleans.Multitenant.Internal;
internal TenantStreamBatchObserver(IAsyncBatchObserver<T> observer) => this.observer = observer;
public Task OnCompletedAsync() => observer.OnCompletedAsync();
public Task OnErrorAsync(Exception ex) => observer.OnErrorAsync(ex);
public Task OnNextAsync(IList<SequentialItem<TenantEvent<T>>> items) => observer.OnNextAsync(items.Select(item => new SequentialItem<T>(item.Item.Event, item.Token)).ToList());
public Task OnNextAsync(IList<SequentialItem<TenantEvent<T>>> items) => observer.OnNextAsync([.. items.Select(item => new SequentialItem<T>(item.Item.Event, item.Token))]);
}
22 changes: 11 additions & 11 deletions src/Orleans.Multitenant/Orleans.Multitenant.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,19 @@
<PropertyGroup>
<IsPackable>true</IsPackable>
<PackageId>Orleans.Multitenant</PackageId>
<PackageVersion>2.2.12</PackageVersion>
<PackageVersion>3.0.0</PackageVersion>
<Title>Orleans Multitenant</Title>
<Description>Secure, flexible tenant separation for Microsoft Orleans 8</Description>
<Authors>VincentH.NET;Applicita</Authors>
<Company>Applicita</Company>
<Copyright>Copyright © Applicita</Copyright>
<Description>Secure, flexible tenant separation for Microsoft Orleans 9</Description>
<Authors>VincentH.NET</Authors>
<Company>InnoWvate.NET</Company>
<Copyright>Copyright © InnoWvate.NET</Copyright>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/Applicita/Orleans.Multitenant</PackageProjectUrl>
<PackageProjectUrl>https://github.com/VincentH-Net/Orleans.Multitenant</PackageProjectUrl>
<PackageIcon>CSharp-Toolkit-Icon.png</PackageIcon>
<PackageReadmeFile>Readme.md</PackageReadmeFile>
<PackageReleaseNotes>See source repository for release notes</PackageReleaseNotes>
<RepositoryUrl>https://github.com/Applicita/Orleans.Multitenant</RepositoryUrl>
<PackageTags>multitenant;multi-tenant;tenant;tenant separation;separation;Orleans;Orleans 8;Microsoft Orleans;Applicita</PackageTags>
<RepositoryUrl>https://github.com/VincentH-Net/Orleans.Multitenant</RepositoryUrl>
<PackageTags>multitenant;multi-tenant;tenant;tenant separation;separation;Orleans;Orleans 9;Microsoft Orleans;InnoWvate.NET</PackageTags>
<GenerateDocumentationFile>true</GenerateDocumentationFile>

<!-- Enable Source Link -->
Expand All @@ -41,9 +41,9 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Orleans.Runtime" Version="8.1.0" />
<PackageReference Include="Microsoft.Orleans.Sdk" Version="8.1.0" />
<PackageReference Include="Microsoft.Orleans.Streaming" Version="8.1.0" />
<PackageReference Include="Microsoft.Orleans.Runtime" Version="9.1.2" />
<PackageReference Include="Microsoft.Orleans.Sdk" Version="9.1.2" />
<PackageReference Include="Microsoft.Orleans.Streaming" Version="9.1.2" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
</ItemGroup>

Expand Down
6 changes: 3 additions & 3 deletions src/Orleans.Multitenant/Readme.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Secure, flexible tenant separation for [Microsoft Orleans 8](https://github.com/dotnet/orleans/releases/tag/v8.0.0)
Secure, flexible tenant separation for [Microsoft Orleans 9](https://github.com/dotnet/orleans/releases/tag/v9.0.0)

Docs: see the [repo readme](https://github.com/Applicita/Orleans.Multitenant#readme) and the inline C# documentation. All public Orleans.Multitenant API's come with full inline documentation.
Docs: see the [repo readme](https://github.com/VincentH-Net/Orleans.Multitenant#readme) and the inline C# documentation. All public Orleans.Multitenant API's come with full inline documentation.

[Release Notes](https://github.com/Applicita/Orleans.Multitenant/releases/tag/2-2-12)
[Release Notes](https://github.com/VincentH-Net/Orleans.Multitenant/releases/tag/3-0-0)
1 change: 0 additions & 1 deletion src/Orleans.Multitenant/TenantGrainFactory.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Orleans.Multitenant.Internal;
using Orleans.Runtime;

namespace Orleans.Multitenant;

Expand Down
1 change: 0 additions & 1 deletion src/Orleans.Multitenant/TenantStreamProvider.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Orleans.Multitenant.Internal;
using Orleans.Runtime;
using Orleans.Streams;

namespace Orleans.Multitenant;
Expand Down
12 changes: 6 additions & 6 deletions src/Tests/Orleans.Multitenant.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="Microsoft.Orleans.Sdk" Version="8.2.0" />
<PackageReference Include="Microsoft.Orleans.TestingHost" Version="8.2.0" />
<PackageReference Include="xunit" Version="2.9.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
<PackageReference Include="Microsoft.Orleans.Sdk" Version="9.1.2" />
<PackageReference Include="Microsoft.Orleans.TestingHost" Version="9.1.2" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.2">
<PackageReference Include="coverlet.collector" Version="6.0.4">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
Expand Down
1 change: 0 additions & 1 deletion src/Tests/UnitTests/NoTenantIdTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ public class NoTenantIdTests(ClusterFixture fixture)
readonly Orleans.TestingHost.TestCluster cluster = fixture.Cluster;

public static TheoryData<string /*key*/, string /*tenantQualifiedKey*/> KeyQualifiedKeys() => new() {
{ "" , "" },
{ "1" , "1" },
{ "Key2" , "Key2" },
{ "|" , "||" },
Expand Down