diff --git a/src/Dapr.Actors.AspNetCore/ActorsServiceCollectionExtensions.cs b/src/Dapr.Actors.AspNetCore/ActorsServiceCollectionExtensions.cs
index 11f05f4c1..9b80975db 100644
--- a/src/Dapr.Actors.AspNetCore/ActorsServiceCollectionExtensions.cs
+++ b/src/Dapr.Actors.AspNetCore/ActorsServiceCollectionExtensions.cs
@@ -34,17 +34,18 @@ public static class ActorsServiceCollectionExtensions
///
/// The .
/// A delegate used to configure actor options and register actor types.
- public static void AddActors(this IServiceCollection? services, Action? configure)
+ /// The lifetime of the registered services.
+ public static void AddActors(this IServiceCollection? services, Action? configure, ServiceLifetime lifetime = ServiceLifetime.Singleton)
{
ArgumentNullException.ThrowIfNull(services, nameof(services));
- // Routing and health checks are required dependencies.
+ // Routing, health checks and logging are required dependencies.
services.AddRouting();
services.AddHealthChecks();
+ services.AddLogging();
- services.TryAddSingleton();
- services.TryAddSingleton(s =>
- {
+ var actorRuntimeRegistration = new Func(s =>
+ {
var options = s.GetRequiredService>().Value;
ConfigureActorOptions(s, options);
@@ -53,11 +54,10 @@ public static void AddActors(this IServiceCollection? services, Action();
return new ActorRuntime(options, loggerFactory, activatorFactory, proxyFactory);
});
-
- services.TryAddSingleton(s =>
+ var proxyFactoryRegistration = new Func(serviceProvider =>
{
- var options = s.GetRequiredService>().Value;
- ConfigureActorOptions(s, options);
+ var options = serviceProvider.GetRequiredService>().Value;
+ ConfigureActorOptions(serviceProvider, options);
var factory = new ActorProxyFactory()
{
@@ -72,6 +72,26 @@ public static void AddActors(this IServiceCollection? services, Action();
+ services.TryAddScoped(actorRuntimeRegistration);
+ services.TryAddScoped(proxyFactoryRegistration);
+ break;
+ case ServiceLifetime.Transient:
+ services.TryAddTransient();
+ services.TryAddTransient(actorRuntimeRegistration);
+ services.TryAddTransient(proxyFactoryRegistration);
+ break;
+ default:
+ case ServiceLifetime.Singleton:
+ services.TryAddSingleton();
+ services.TryAddSingleton(actorRuntimeRegistration);
+ services.TryAddSingleton(proxyFactoryRegistration);
+ break;
+ }
+
if (configure != null)
{
services.Configure(configure);
diff --git a/src/Dapr.Jobs/Extensions/DaprJobsServiceCollectionExtensions.cs b/src/Dapr.Jobs/Extensions/DaprJobsServiceCollectionExtensions.cs
index 67e718985..93265837b 100644
--- a/src/Dapr.Jobs/Extensions/DaprJobsServiceCollectionExtensions.cs
+++ b/src/Dapr.Jobs/Extensions/DaprJobsServiceCollectionExtensions.cs
@@ -26,25 +26,40 @@ public static class DaprJobsServiceCollectionExtensions
///
/// The .
/// Optionally allows greater configuration of the .
- public static IServiceCollection AddDaprJobsClient(this IServiceCollection serviceCollection, Action? configure = null)
+ /// The lifetime of the registered services.
+ public static IServiceCollection AddDaprJobsClient(this IServiceCollection serviceCollection, Action? configure = null, ServiceLifetime lifetime = ServiceLifetime.Singleton)
{
ArgumentNullException.ThrowIfNull(serviceCollection, nameof(serviceCollection));
//Register the IHttpClientFactory implementation
serviceCollection.AddHttpClient();
- serviceCollection.TryAddSingleton(serviceProvider =>
+ var registration = new Func(serviceProvider =>
{
var httpClientFactory = serviceProvider.GetRequiredService();
var builder = new DaprJobsClientBuilder();
builder.UseHttpClientFactory(httpClientFactory);
-
+
configure?.Invoke(builder);
return builder.Build();
});
+ switch (lifetime)
+ {
+ case ServiceLifetime.Scoped:
+ serviceCollection.TryAddScoped(registration);
+ break;
+ case ServiceLifetime.Transient:
+ serviceCollection.TryAddTransient(registration);
+ break;
+ case ServiceLifetime.Singleton:
+ default:
+ serviceCollection.TryAddSingleton(registration);
+ break;
+ }
+
return serviceCollection;
}
@@ -53,8 +68,9 @@ public static IServiceCollection AddDaprJobsClient(this IServiceCollection servi
///
/// The .
/// Optionally allows greater configuration of the using injected services.
+ /// The lifetime of the registered services.
///
- public static IServiceCollection AddDaprJobsClient(this IServiceCollection serviceCollection, Action? configure)
+ public static IServiceCollection AddDaprJobsClient(this IServiceCollection serviceCollection, Action? configure, ServiceLifetime lifetime = ServiceLifetime.Singleton)
{
ArgumentNullException.ThrowIfNull(serviceCollection, nameof(serviceCollection));
diff --git a/src/Dapr.Messaging/PublishSubscribe/Extensions/PublishSubscribeServiceCollectionExtensions.cs b/src/Dapr.Messaging/PublishSubscribe/Extensions/PublishSubscribeServiceCollectionExtensions.cs
index bc60c5880..fe9b7c417 100644
--- a/src/Dapr.Messaging/PublishSubscribe/Extensions/PublishSubscribeServiceCollectionExtensions.cs
+++ b/src/Dapr.Messaging/PublishSubscribe/Extensions/PublishSubscribeServiceCollectionExtensions.cs
@@ -13,15 +13,16 @@ public static class PublishSubscribeServiceCollectionExtensions
///
/// The .
/// Optionally allows greater configuration of the using injected services.
+ /// The lifetime of the registered services.
///
- public static IServiceCollection AddDaprPubSubClient(this IServiceCollection services, Action? configure = null)
+ public static IServiceCollection AddDaprPubSubClient(this IServiceCollection services, Action? configure = null, ServiceLifetime lifetime = ServiceLifetime.Singleton)
{
ArgumentNullException.ThrowIfNull(services, nameof(services));
//Register the IHttpClientFactory implementation
services.AddHttpClient();
- services.TryAddSingleton(serviceProvider =>
+ var registration = new Func(serviceProvider =>
{
var httpClientFactory = serviceProvider.GetRequiredService();
@@ -33,6 +34,20 @@ public static IServiceCollection AddDaprPubSubClient(this IServiceCollection ser
return builder.Build();
});
+ switch (lifetime)
+ {
+ case ServiceLifetime.Scoped:
+ services.TryAddScoped(registration);
+ break;
+ case ServiceLifetime.Transient:
+ services.TryAddTransient(registration);
+ break;
+ default:
+ case ServiceLifetime.Singleton:
+ services.TryAddSingleton(registration);
+ break;
+ }
+
return services;
}
}
diff --git a/test/Dapr.Actors.AspNetCore.Test/DaprActorServiceCollectionExtensionsTest.cs b/test/Dapr.Actors.AspNetCore.Test/DaprActorServiceCollectionExtensionsTest.cs
new file mode 100644
index 000000000..3255fb785
--- /dev/null
+++ b/test/Dapr.Actors.AspNetCore.Test/DaprActorServiceCollectionExtensionsTest.cs
@@ -0,0 +1,60 @@
+using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
+using Xunit;
+
+namespace Dapr.Actors.AspNetCore.Test;
+
+public sealed class DaprActorServiceCollectionExtensionsTest
+{
+ [Fact]
+ public void RegisterActorsClient_ShouldRegisterSingleton_WhenLifetimeIsSingleton()
+ {
+ var services = new ServiceCollection();
+
+ services.AddActors(options => { }, ServiceLifetime.Singleton);
+ var serviceProvider = services.BuildServiceProvider();
+
+ var daprClient1 = serviceProvider.GetService();
+ var daprClient2 = serviceProvider.GetService();
+
+ Assert.NotNull(daprClient1);
+ Assert.NotNull(daprClient2);
+
+ Assert.Same(daprClient1, daprClient2);
+ }
+
+ [Fact]
+ public async Task RegisterActorsClient_ShouldRegisterScoped_WhenLifetimeIsScoped()
+ {
+ var services = new ServiceCollection();
+
+ services.AddActors(options => { }, ServiceLifetime.Scoped);
+ var serviceProvider = services.BuildServiceProvider();
+
+ await using var scope1 = serviceProvider.CreateAsyncScope();
+ var daprClient1 = scope1.ServiceProvider.GetService();
+
+ await using var scope2 = serviceProvider.CreateAsyncScope();
+ var daprClient2 = scope2.ServiceProvider.GetService();
+
+ Assert.NotNull(daprClient1);
+ Assert.NotNull(daprClient2);
+ Assert.NotSame(daprClient1, daprClient2);
+ }
+
+ [Fact]
+ public void RegisterActorsClient_ShouldRegisterTransient_WhenLifetimeIsTransient()
+ {
+ var services = new ServiceCollection();
+
+ services.AddActors(options => { }, ServiceLifetime.Transient);
+ var serviceProvider = services.BuildServiceProvider();
+
+ var daprClient1 = serviceProvider.GetService();
+ var daprClient2 = serviceProvider.GetService();
+
+ Assert.NotNull(daprClient1);
+ Assert.NotNull(daprClient2);
+ Assert.NotSame(daprClient1, daprClient2);
+ }
+}
diff --git a/test/Dapr.AspNetCore.Test/DaprServiceCollectionExtensionsTest.cs b/test/Dapr.AspNetCore.Test/DaprServiceCollectionExtensionsTest.cs
index 4a340e22a..2028a9fbb 100644
--- a/test/Dapr.AspNetCore.Test/DaprServiceCollectionExtensionsTest.cs
+++ b/test/Dapr.AspNetCore.Test/DaprServiceCollectionExtensionsTest.cs
@@ -15,67 +15,120 @@
using System;
using System.Text.Json;
+using System.Threading.Tasks;
using Dapr.Client;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
-namespace Dapr.AspNetCore.Test
+namespace Dapr.AspNetCore.Test;
+
+public class DaprServiceCollectionExtensionsTest
{
- public class DaprServiceCollectionExtensionsTest
+ [Fact]
+ public void AddDaprClient_RegistersDaprClientOnlyOnce()
{
- [Fact]
- public void AddDaprClient_RegistersDaprClientOnlyOnce()
- {
- var services = new ServiceCollection();
+ var services = new ServiceCollection();
- var clientBuilder = new Action(
- builder => builder.UseJsonSerializationOptions(
- new JsonSerializerOptions()
- {
- PropertyNameCaseInsensitive = false
- }
- )
- );
+ var clientBuilder = new Action(
+ builder => builder.UseJsonSerializationOptions(
+ new JsonSerializerOptions()
+ {
+ PropertyNameCaseInsensitive = false
+ }
+ )
+ );
- // register with JsonSerializerOptions.PropertyNameCaseInsensitive = true (default)
- services.AddDaprClient();
+ // register with JsonSerializerOptions.PropertyNameCaseInsensitive = true (default)
+ services.AddDaprClient();
- // register with PropertyNameCaseInsensitive = false
- services.AddDaprClient(clientBuilder);
+ // register with PropertyNameCaseInsensitive = false
+ services.AddDaprClient(clientBuilder);
- var serviceProvider = services.BuildServiceProvider();
+ var serviceProvider = services.BuildServiceProvider();
- DaprClientGrpc? daprClient = serviceProvider.GetService() as DaprClientGrpc;
+ DaprClientGrpc? daprClient = serviceProvider.GetService() as DaprClientGrpc;
- Assert.NotNull(daprClient);
- Assert.True(daprClient?.JsonSerializerOptions.PropertyNameCaseInsensitive);
- }
+ Assert.NotNull(daprClient);
+ Assert.True(daprClient?.JsonSerializerOptions.PropertyNameCaseInsensitive);
+ }
- [Fact]
- public void AddDaprClient_RegistersUsingDependencyFromIServiceProvider()
+ [Fact]
+ public void AddDaprClient_RegistersUsingDependencyFromIServiceProvider()
+ {
+
+ var services = new ServiceCollection();
+ services.AddSingleton();
+ services.AddDaprClient((provider, builder) =>
{
+ var configProvider = provider.GetRequiredService();
+ var caseSensitivity = configProvider.GetCaseSensitivity();
- var services = new ServiceCollection();
- services.AddSingleton();
- services.AddDaprClient((provider, builder) =>
+ builder.UseJsonSerializationOptions(new JsonSerializerOptions
{
- var configProvider = provider.GetRequiredService();
- var caseSensitivity = configProvider.GetCaseSensitivity();
-
- builder.UseJsonSerializationOptions(new JsonSerializerOptions
- {
- PropertyNameCaseInsensitive = caseSensitivity
- });
+ PropertyNameCaseInsensitive = caseSensitivity
});
+ });
- var serviceProvider = services.BuildServiceProvider();
+ var serviceProvider = services.BuildServiceProvider();
- DaprClientGrpc? client = serviceProvider.GetRequiredService() as DaprClientGrpc;
+ DaprClientGrpc? client = serviceProvider.GetRequiredService() as DaprClientGrpc;
- //Registers with case-insensitive as true by default, but we set as false above
- Assert.NotNull(client);
- Assert.False(client?.JsonSerializerOptions.PropertyNameCaseInsensitive);
- }
+ //Registers with case-insensitive as true by default, but we set as false above
+ Assert.NotNull(client);
+ Assert.False(client?.JsonSerializerOptions.PropertyNameCaseInsensitive);
+ }
+
+ [Fact]
+ public void RegisterClient_ShouldRegisterSingleton_WhenLifetimeIsSingleton()
+ {
+ var services = new ServiceCollection();
+
+ services.AddDaprClient(options => { }, ServiceLifetime.Singleton);
+ var serviceProvider = services.BuildServiceProvider();
+
+ var daprClient1 = serviceProvider.GetService();
+ var daprClient2 = serviceProvider.GetService();
+
+ Assert.NotNull(daprClient1);
+ Assert.NotNull(daprClient2);
+
+ Assert.Same(daprClient1, daprClient2);
+ }
+
+ [Fact]
+ public async Task RegisterDaprClient_ShouldRegisterScoped_WhenLifetimeIsScoped()
+ {
+ var services = new ServiceCollection();
+
+ services.AddDaprClient(options => { }, ServiceLifetime.Scoped);
+ var serviceProvider = services.BuildServiceProvider();
+
+ await using var scope1 = serviceProvider.CreateAsyncScope();
+ var daprClient1 = scope1.ServiceProvider.GetService();
+
+ await using var scope2 = serviceProvider.CreateAsyncScope();
+ var daprClient2 = scope2.ServiceProvider.GetService();
+
+ Assert.NotNull(daprClient1);
+ Assert.NotNull(daprClient2);
+ Assert.NotSame(daprClient1, daprClient2);
+ }
+
+ [Fact]
+ public void RegisterDaprClient_ShouldRegisterTransient_WhenLifetimeIsTransient()
+ {
+ var services = new ServiceCollection();
+
+ services.AddDaprClient(options => { }, ServiceLifetime.Transient);
+ var serviceProvider = services.BuildServiceProvider();
+
+ var daprClient1 = serviceProvider.GetService();
+ var daprClient2 = serviceProvider.GetService();
+
+ Assert.NotNull(daprClient1);
+ Assert.NotNull(daprClient2);
+ Assert.NotSame(daprClient1, daprClient2);
+ }
#if NET8_0_OR_GREATER
@@ -96,9 +149,8 @@ public void AddDaprClient_WithKeyedServices()
}
#endif
- private class TestConfigurationProvider
- {
- public bool GetCaseSensitivity() => false;
- }
+ private class TestConfigurationProvider
+ {
+ public bool GetCaseSensitivity() => false;
}
}
diff --git a/test/Dapr.Jobs.Test/Extensions/DaprJobsServiceCollectionExtensionsTests.cs b/test/Dapr.Jobs.Test/Extensions/DaprJobsServiceCollectionExtensionsTests.cs
index 34d900aeb..281477d4e 100644
--- a/test/Dapr.Jobs.Test/Extensions/DaprJobsServiceCollectionExtensionsTests.cs
+++ b/test/Dapr.Jobs.Test/Extensions/DaprJobsServiceCollectionExtensionsTests.cs
@@ -13,9 +13,9 @@
using System;
using System.Net.Http;
+using System.Threading.Tasks;
using Dapr.Jobs.Extensions;
using Microsoft.Extensions.DependencyInjection;
-using Xunit;
namespace Dapr.Jobs.Test.Extensions;
@@ -77,6 +77,58 @@ public void AddDaprJobsClient_RegistersUsingDependencyFromIServiceProvider()
Assert.Equal("dapr-api-token", client.apiTokenHeader.Value.Key);
Assert.Equal("abcdef", client.apiTokenHeader.Value.Value);
}
+
+ [Fact]
+ public void RegisterJobsClient_ShouldRegisterSingleton_WhenLifetimeIsSingleton()
+ {
+ var services = new ServiceCollection();
+
+ services.AddDaprJobsClient(options => { }, ServiceLifetime.Singleton);
+ var serviceProvider = services.BuildServiceProvider();
+
+ var daprJobsClient1 = serviceProvider.GetService();
+ var daprJobsClient2 = serviceProvider.GetService();
+
+ Assert.NotNull(daprJobsClient1);
+ Assert.NotNull(daprJobsClient2);
+
+ Assert.Same(daprJobsClient1, daprJobsClient2);
+ }
+
+ [Fact]
+ public async Task RegisterJobsClient_ShouldRegisterScoped_WhenLifetimeIsScoped()
+ {
+ var services = new ServiceCollection();
+
+ services.AddDaprJobsClient(options => { }, ServiceLifetime.Scoped);
+ var serviceProvider = services.BuildServiceProvider();
+
+ await using var scope1 = serviceProvider.CreateAsyncScope();
+ var daprJobsClient1 = scope1.ServiceProvider.GetService();
+
+ await using var scope2 = serviceProvider.CreateAsyncScope();
+ var daprJobsClient2 = scope2.ServiceProvider.GetService();
+
+ Assert.NotNull(daprJobsClient1);
+ Assert.NotNull(daprJobsClient2);
+ Assert.NotSame(daprJobsClient1, daprJobsClient2);
+ }
+
+ [Fact]
+ public void RegisterJobsClient_ShouldRegisterTransient_WhenLifetimeIsTransient()
+ {
+ var services = new ServiceCollection();
+
+ services.AddDaprJobsClient(options => { }, ServiceLifetime.Transient);
+ var serviceProvider = services.BuildServiceProvider();
+
+ var daprJobsClient1 = serviceProvider.GetService();
+ var daprJobsClient2 = serviceProvider.GetService();
+
+ Assert.NotNull(daprJobsClient1);
+ Assert.NotNull(daprJobsClient2);
+ Assert.NotSame(daprJobsClient1, daprJobsClient2);
+ }
private class TestSecretRetriever
{
diff --git a/test/Dapr.Messaging.Test/Extensions/PublishSubscribeServiceCollectionExtensionsTests.cs b/test/Dapr.Messaging.Test/Extensions/PublishSubscribeServiceCollectionExtensionsTests.cs
new file mode 100644
index 000000000..d239fb86d
--- /dev/null
+++ b/test/Dapr.Messaging.Test/Extensions/PublishSubscribeServiceCollectionExtensionsTests.cs
@@ -0,0 +1,76 @@
+using Dapr.Messaging.PublishSubscribe;
+using Dapr.Messaging.PublishSubscribe.Extensions;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Dapr.Messaging.Test.Extensions;
+
+public sealed class PublishSubscribeServiceCollectionExtensionsTests
+{
+ [Fact]
+ public void AddDaprPubSubClient_RegistersIHttpClientFactory()
+ {
+ var services = new ServiceCollection();
+
+ services.AddDaprPubSubClient();
+
+ var serviceProvider = services.BuildServiceProvider();
+
+ var httpClientFactory = serviceProvider.GetService();
+ Assert.NotNull(httpClientFactory);
+
+ var daprPubSubClient = serviceProvider.GetService();
+ Assert.NotNull(daprPubSubClient);
+ }
+
+ [Fact]
+ public void RegisterPubsubClient_ShouldRegisterSingleton_WhenLifetimeIsSingleton()
+ {
+ var services = new ServiceCollection();
+
+ services.AddDaprPubSubClient(lifetime: ServiceLifetime.Singleton);
+ var serviceProvider = services.BuildServiceProvider();
+
+ var daprPubSubClient1 = serviceProvider.GetService();
+ var daprPubSubClient2 = serviceProvider.GetService();
+
+ Assert.NotNull(daprPubSubClient1);
+ Assert.NotNull(daprPubSubClient2);
+
+ Assert.Same(daprPubSubClient1, daprPubSubClient2);
+ }
+
+ [Fact]
+ public async Task RegisterPubsubClient_ShouldRegisterScoped_WhenLifetimeIsScoped()
+ {
+ var services = new ServiceCollection();
+
+ services.AddDaprPubSubClient(lifetime: ServiceLifetime.Scoped);
+ var serviceProvider = services.BuildServiceProvider();
+
+ await using var scope1 = serviceProvider.CreateAsyncScope();
+ var daprPubSubClient1 = scope1.ServiceProvider.GetService();
+
+ await using var scope2 = serviceProvider.CreateAsyncScope();
+ var daprPubSubClient2 = scope2.ServiceProvider.GetService();
+
+ Assert.NotNull(daprPubSubClient1);
+ Assert.NotNull(daprPubSubClient2);
+ Assert.NotSame(daprPubSubClient1, daprPubSubClient2);
+ }
+
+ [Fact]
+ public void RegisterPubsubClient_ShouldRegisterTransient_WhenLifetimeIsTransient()
+ {
+ var services = new ServiceCollection();
+
+ services.AddDaprPubSubClient(lifetime: ServiceLifetime.Transient);
+ var serviceProvider = services.BuildServiceProvider();
+
+ var daprPubSubClient1 = serviceProvider.GetService();
+ var daprPubSubClient2 = serviceProvider.GetService();
+
+ Assert.NotNull(daprPubSubClient1);
+ Assert.NotNull(daprPubSubClient2);
+ Assert.NotSame(daprPubSubClient1, daprPubSubClient2);
+ }
+}