diff --git a/src/Orleans.Core/Hosting/GenericHostExtensions.cs b/src/Orleans.Core/Hosting/OrleansClientGenericHostExtensions.cs
similarity index 59%
rename from src/Orleans.Core/Hosting/GenericHostExtensions.cs
rename to src/Orleans.Core/Hosting/OrleansClientGenericHostExtensions.cs
index 90c5bc2164..55a0432467 100644
--- a/src/Orleans.Core/Hosting/GenericHostExtensions.cs
+++ b/src/Orleans.Core/Hosting/OrleansClientGenericHostExtensions.cs
@@ -1,4 +1,5 @@
using System;
+using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Orleans.Hosting;
using Orleans.Runtime;
@@ -10,6 +11,32 @@ namespace Microsoft.Extensions.Hosting
///
public static class OrleansClientGenericHostExtensions
{
+ private static readonly Type MarkerType = typeof(OrleansBuilderMarker);
+
+ ///
+ /// Configures the host app builder to host an Orleans client.
+ ///
+ /// The host app builder.
+ /// The delegate used to configure the client.
+ /// The host builder.
+ ///
+ /// Calling this method multiple times on the same instance will result in one client being configured.
+ /// However, the effects of will be applied once for each call.
+ /// Note that this method shouldn't be used in conjunction with HostApplicationBuilder.UseOrleans, since UseOrleans includes a client automatically.
+ ///
+ /// was null or was null.
+ public static HostApplicationBuilder UseOrleansClient(
+ this HostApplicationBuilder hostAppBuilder,
+ Action configureDelegate)
+ {
+ ArgumentNullException.ThrowIfNull(hostAppBuilder);
+ ArgumentNullException.ThrowIfNull(configureDelegate);
+
+ hostAppBuilder.Services.AddOrleansClient(configureDelegate);
+
+ return hostAppBuilder;
+ }
+
///
/// Configures the host builder to host an Orleans client.
///
@@ -22,8 +49,8 @@ public static class OrleansClientGenericHostExtensions
/// Note that this method should not be used in conjunction with IHostBuilder.UseOrleans, since UseOrleans includes a client automatically.
///
/// was null or was null.
- public static IHostBuilder UseOrleansClient(this IHostBuilder hostBuilder, Action configureDelegate)
- => hostBuilder.UseOrleansClient((_, clientBuilder) => configureDelegate(clientBuilder));
+ public static IHostBuilder UseOrleansClient(this IHostBuilder hostBuilder, Action configureDelegate) =>
+ hostBuilder.UseOrleansClient((_, clientBuilder) => configureDelegate(clientBuilder));
///
/// Configures the host builder to host an Orleans client.
@@ -39,14 +66,16 @@ public static IHostBuilder UseOrleansClient(this IHostBuilder hostBuilder, Actio
/// was null or was null.
public static IHostBuilder UseOrleansClient(this IHostBuilder hostBuilder, Action configureDelegate)
{
- if (hostBuilder == null) throw new ArgumentNullException(nameof(hostBuilder));
- if (configureDelegate == null) throw new ArgumentNullException(nameof(configureDelegate));
+ ArgumentNullException.ThrowIfNull(hostBuilder);
+ ArgumentNullException.ThrowIfNull(configureDelegate);
+
if (hostBuilder.Properties.ContainsKey("HasOrleansSiloBuilder"))
{
throw GetOrleansSiloAddedException();
}
hostBuilder.Properties["HasOrleansClientBuilder"] = "true";
+
return hostBuilder.ConfigureServices((ctx, services) => configureDelegate(ctx, AddOrleansClient(services)));
}
@@ -64,8 +93,10 @@ public static IHostBuilder UseOrleansClient(this IHostBuilder hostBuilder, Actio
/// was null or was null.
public static IServiceCollection AddOrleansClient(this IServiceCollection services, Action configureDelegate)
{
- if (configureDelegate == null) throw new ArgumentNullException(nameof(configureDelegate));
+ ArgumentNullException.ThrowIfNull(configureDelegate);
+
var clientBuilder = AddOrleansClient(services);
+
configureDelegate(clientBuilder);
return services;
}
@@ -73,45 +104,43 @@ public static IServiceCollection AddOrleansClient(this IServiceCollection servic
private static IClientBuilder AddOrleansClient(IServiceCollection services)
{
IClientBuilder clientBuilder = default;
- foreach (var descriptor in services)
+ foreach (var descriptor in services.Where(d => d.ServiceType.Equals(MarkerType)))
{
- if (descriptor.ServiceType.Equals(typeof(OrleansBuilderMarker)))
+ var instance = (OrleansBuilderMarker)descriptor.ImplementationInstance;
+ clientBuilder = instance.BuilderInstance switch
{
- var instance = (OrleansBuilderMarker)descriptor.ImplementationInstance;
- clientBuilder = instance.Instance switch
- {
- IClientBuilder existingBuilder => existingBuilder,
- _ => throw GetOrleansSiloAddedException()
- };
- }
+ IClientBuilder existingBuilder => existingBuilder,
+ _ => throw GetOrleansSiloAddedException()
+ };
}
if (clientBuilder is null)
{
clientBuilder = new ClientBuilder(services);
- services.Add(new(typeof(OrleansBuilderMarker), new OrleansBuilderMarker(clientBuilder)));
+ services.AddSingleton(new OrleansBuilderMarker(clientBuilder));
}
return clientBuilder;
}
- private static OrleansConfigurationException GetOrleansSiloAddedException() => new("Do not use UseOrleans with UseOrleansClient. If you want a client and server in the same process, only UseOrleans is necessary and the UseOrleansClient call can be removed.");
+ private static OrleansConfigurationException GetOrleansSiloAddedException() =>
+ new("Do not use UseOrleans with UseOrleansClient. If you want a client and server in the same process, only UseOrleans is necessary and the UseOrleansClient call can be removed.");
+ }
+ ///
+ /// Marker type used for storing a builder in a service collection.
+ ///
+ internal sealed class OrleansBuilderMarker
+ {
///
- /// Marker type used for storing a client builder in a service collection.
+ /// Initializes a new instance of the class.
///
- internal class OrleansBuilderMarker
- {
- ///
- /// Initializes a new instance of the class.
- ///
- /// The builder instance.
- public OrleansBuilderMarker(object builderInstance) => Instance = builderInstance;
-
- ///
- /// Gets the builder instance.
- ///
- public object Instance { get; }
- }
+ /// The builder instance.
+ public OrleansBuilderMarker(object builderInstance) => BuilderInstance = builderInstance;
+
+ ///
+ /// Gets the builder instance.
+ ///
+ public object BuilderInstance { get; }
}
}
diff --git a/src/Orleans.Core/Orleans.Core.csproj b/src/Orleans.Core/Orleans.Core.csproj
index 30f3c10dfe..e8b8e5e560 100644
--- a/src/Orleans.Core/Orleans.Core.csproj
+++ b/src/Orleans.Core/Orleans.Core.csproj
@@ -24,7 +24,7 @@
-
+
diff --git a/src/Orleans.Runtime/Hosting/GenericHostExtensions.cs b/src/Orleans.Runtime/Hosting/OrleansSiloGenericHostExtensions.cs
similarity index 58%
rename from src/Orleans.Runtime/Hosting/GenericHostExtensions.cs
rename to src/Orleans.Runtime/Hosting/OrleansSiloGenericHostExtensions.cs
index acb6c728b2..e951a2991c 100644
--- a/src/Orleans.Runtime/Hosting/GenericHostExtensions.cs
+++ b/src/Orleans.Runtime/Hosting/OrleansSiloGenericHostExtensions.cs
@@ -1,17 +1,49 @@
using System;
+using System.Linq;
using Microsoft.Extensions.DependencyInjection;
-using Orleans;
using Orleans.Hosting;
using Orleans.Runtime;
-using static Microsoft.Extensions.Hosting.OrleansClientGenericHostExtensions;
namespace Microsoft.Extensions.Hosting
{
///
/// Extension methods for .
///
- public static class GenericHostExtensions
+ public static class OrleansSiloGenericHostExtensions
{
+ private static readonly Type MarkerType = typeof(OrleansBuilderMarker);
+
+ ///
+ /// Configures the host app builder to host an Orleans silo.
+ ///
+ /// The host app builder.
+ /// The host builder.
+ public static HostApplicationBuilder UseOrleans(
+ this HostApplicationBuilder hostAppBuilder) =>
+ hostAppBuilder.UseOrleans(_ => { });
+
+ ///
+ /// Configures the host builder to host an Orleans silo.
+ ///
+ /// The host app builder.
+ /// The delegate used to configure the silo.
+ /// The host builder.
+ ///
+ /// Calling this method multiple times on the same instance will result in one silo being configured.
+ /// However, the effects of will be applied once for each call.
+ ///
+ public static HostApplicationBuilder UseOrleans(
+ this HostApplicationBuilder hostAppBuilder,
+ Action configureDelegate)
+ {
+ ArgumentNullException.ThrowIfNull(hostAppBuilder);
+ ArgumentNullException.ThrowIfNull(configureDelegate);
+
+ hostAppBuilder.Services.AddOrleans(configureDelegate);
+
+ return hostAppBuilder;
+ }
+
///
/// Configures the host builder to host an Orleans silo.
///
@@ -40,8 +72,8 @@ public static IHostBuilder UseOrleans(
this IHostBuilder hostBuilder,
Action configureDelegate)
{
- if (hostBuilder is null) throw new ArgumentNullException(nameof(hostBuilder));
- if (configureDelegate == null) throw new ArgumentNullException(nameof(configureDelegate));
+ ArgumentNullException.ThrowIfNull(hostBuilder);
+ ArgumentNullException.ThrowIfNull(configureDelegate);
if (hostBuilder.Properties.ContainsKey("HasOrleansClientBuilder"))
{
@@ -67,39 +99,39 @@ public static IServiceCollection AddOrleans(
this IServiceCollection services,
Action configureDelegate)
{
- if (configureDelegate == null) throw new ArgumentNullException(nameof(configureDelegate));
+ ArgumentNullException.ThrowIfNull(configureDelegate);
+
var builder = AddOrleans(services);
configureDelegate(builder);
+
return services;
}
private static ISiloBuilder AddOrleans(IServiceCollection services)
{
ISiloBuilder builder = default;
- foreach (var descriptor in services)
+ foreach (var descriptor in services.Where(d => d.ServiceType.Equals(MarkerType)))
{
- if (descriptor.ServiceType.Equals(typeof(OrleansBuilderMarker)))
+ var marker = (OrleansBuilderMarker)descriptor.ImplementationInstance;
+ builder = marker.BuilderInstance switch
{
- var instance = (OrleansBuilderMarker)descriptor.ImplementationInstance;
- builder = instance.Instance switch
- {
-
- ISiloBuilder existingBuilder => existingBuilder,
- _ => throw GetOrleansClientAddedException()
- };
- }
+
+ ISiloBuilder existingBuilder => existingBuilder,
+ _ => throw GetOrleansClientAddedException()
+ };
}
if (builder is null)
{
builder = new SiloBuilder(services);
- services.Add(new(typeof(OrleansBuilderMarker), new OrleansBuilderMarker(builder)));
+ services.AddSingleton(new OrleansBuilderMarker(builder));
}
return builder;
}
- private static OrleansConfigurationException GetOrleansClientAddedException() => new("Do not call both UseOrleansClient/AddOrleansClient with UseOrleans/AddOrleans. If you want a client and server in the same process, only UseOrleans/AddOrleans is necessary and the UseOrleansClient/AddOrleansClient call can be removed.");
+ private static OrleansConfigurationException GetOrleansClientAddedException() =>
+ new("Do not call both UseOrleansClient/AddOrleansClient with UseOrleans/AddOrleans. If you want a client and server in the same process, only UseOrleans/AddOrleans is necessary and the UseOrleansClient/AddOrleansClient call can be removed.");
}
}
\ No newline at end of file
diff --git a/test/DefaultCluster.Tests/HostedClientTests.cs b/test/DefaultCluster.Tests/HostedClientTests.cs
index 2589109439..24cde25268 100644
--- a/test/DefaultCluster.Tests/HostedClientTests.cs
+++ b/test/DefaultCluster.Tests/HostedClientTests.cs
@@ -1,14 +1,9 @@
-using System;
-using System.Collections.Generic;
using System.Diagnostics;
-using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
-using Orleans;
using Orleans.Concurrency;
using Orleans.Configuration;
-using Orleans.Hosting;
using Orleans.Providers;
using Orleans.Runtime;
using Orleans.Streams;
@@ -39,8 +34,8 @@ public Fixture()
public async Task InitializeAsync()
{
var (siloPort, gatewayPort) = portAllocator.AllocateConsecutivePortPairs(1);
- Host = new HostBuilder()
- .UseOrleans((ctx, siloBuilder) =>
+ Host = Microsoft.Extensions.Hosting.Host.CreateApplicationBuilder()
+ .UseOrleans(siloBuilder =>
{
siloBuilder
.UseLocalhostClustering(siloPort, gatewayPort)
diff --git a/test/NonSilo.Tests/ClientBuilderTests.cs b/test/NonSilo.Tests/ClientBuilderTests.cs
index 46c76a1581..d2ac437b07 100644
--- a/test/NonSilo.Tests/ClientBuilderTests.cs
+++ b/test/NonSilo.Tests/ClientBuilderTests.cs
@@ -174,7 +174,7 @@ public void ClientBuilder_ServiceProviderTest()
Assert.Throws(() => hostBuilder.ConfigureServices(null));
var registeredFirst = new int[1];
-
+
var one = new MyService { Id = 1 };
hostBuilder.ConfigureServices(
services =>
@@ -196,7 +196,7 @@ public void ClientBuilder_ServiceProviderTest()
var client = host.Services.GetRequiredService();
var services = client.ServiceProvider.GetServices()?.ToList();
Assert.NotNull(services);
-
+
// Both services should be registered.
Assert.Equal(2, services.Count);
Assert.NotNull(services.FirstOrDefault(svc => svc.Id == 1));
@@ -226,6 +226,23 @@ public void ClientBuilderThrowsDuringStartupIfSiloBuildersAdded()
});
}
+ [Fact]
+ public void ClientBuilderWithHotApplicationBuilderThrowsDuringStartupIfSiloBuildersAdded()
+ {
+ Assert.Throws(() =>
+ {
+ _ = Host.CreateApplicationBuilder()
+ .UseOrleans(siloBuilder =>
+ {
+ siloBuilder.UseLocalhostClustering();
+ })
+ .UseOrleansClient(clientBuilder =>
+ {
+ clientBuilder.UseLocalhostClustering();
+ });
+ });
+ }
+
private static void RemoveConfigValidators(IServiceCollection services)
{
var validators = services.Where(descriptor => descriptor.ServiceType == typeof(IConfigurationValidator)).ToList();
diff --git a/test/NonSilo.Tests/SiloBuilderTests.cs b/test/NonSilo.Tests/SiloBuilderTests.cs
index 7a748b8a8d..b874de3810 100644
--- a/test/NonSilo.Tests/SiloBuilderTests.cs
+++ b/test/NonSilo.Tests/SiloBuilderTests.cs
@@ -201,6 +201,23 @@ public void SiloBuilderThrowsDuringStartupIfClientBuildersAdded()
});
}
+ [Fact]
+ public void SiloBuilderWithHotApplicationBuilderThrowsDuringStartupIfClientBuildersAdded()
+ {
+ Assert.Throws(() =>
+ {
+ _ = Host.CreateApplicationBuilder()
+ .UseOrleansClient(clientBuilder =>
+ {
+ clientBuilder.UseLocalhostClustering();
+ })
+ .UseOrleans(siloBuilder =>
+ {
+ siloBuilder.UseLocalhostClustering();
+ });
+ });
+ }
+
private class FakeHostEnvironmentStatistics : IHostEnvironmentStatistics
{
public long? TotalPhysicalMemory => 0;