Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Additional lifecycle registration changes #1410

Merged
merged 10 commits into from
Nov 24, 2024
38 changes: 29 additions & 9 deletions src/Dapr.Actors.AspNetCore/ActorsServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,18 @@ public static class ActorsServiceCollectionExtensions
/// </summary>
/// <param name="services">The <see cref="IServiceCollection" />.</param>
/// <param name="configure">A delegate used to configure actor options and register actor types.</param>
public static void AddActors(this IServiceCollection? services, Action<ActorRuntimeOptions>? configure)
/// <param name="lifetime">The lifetime of the registered services.</param>
public static void AddActors(this IServiceCollection? services, Action<ActorRuntimeOptions>? 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<ActorActivatorFactory, DependencyInjectionActorActivatorFactory>();
services.TryAddSingleton<ActorRuntime>(s =>
{
var actorRuntimeRegistration = new Func<IServiceProvider, ActorRuntime>(s =>
{
var options = s.GetRequiredService<IOptions<ActorRuntimeOptions>>().Value;
ConfigureActorOptions(s, options);

Expand All @@ -53,11 +54,10 @@ public static void AddActors(this IServiceCollection? services, Action<ActorRunt
var proxyFactory = s.GetRequiredService<IActorProxyFactory>();
return new ActorRuntime(options, loggerFactory, activatorFactory, proxyFactory);
});

services.TryAddSingleton<IActorProxyFactory>(s =>
var proxyFactoryRegistration = new Func<IServiceProvider, IActorProxyFactory>(serviceProvider =>
{
var options = s.GetRequiredService<IOptions<ActorRuntimeOptions>>().Value;
ConfigureActorOptions(s, options);
var options = serviceProvider.GetRequiredService<IOptions<ActorRuntimeOptions>>().Value;
ConfigureActorOptions(serviceProvider, options);

var factory = new ActorProxyFactory()
{
Expand All @@ -72,6 +72,26 @@ public static void AddActors(this IServiceCollection? services, Action<ActorRunt
return factory;
});

switch (lifetime)
{
case ServiceLifetime.Scoped:
services.TryAddScoped<ActorActivatorFactory, DependencyInjectionActorActivatorFactory>();
services.TryAddScoped<ActorRuntime>(actorRuntimeRegistration);
services.TryAddScoped<IActorProxyFactory>(proxyFactoryRegistration);
break;
case ServiceLifetime.Transient:
services.TryAddTransient<ActorActivatorFactory, DependencyInjectionActorActivatorFactory>();
services.TryAddTransient<ActorRuntime>(actorRuntimeRegistration);
services.TryAddTransient<IActorProxyFactory>(proxyFactoryRegistration);
break;
default:
case ServiceLifetime.Singleton:
services.TryAddSingleton<ActorActivatorFactory, DependencyInjectionActorActivatorFactory>();
services.TryAddSingleton<ActorRuntime>(actorRuntimeRegistration);
services.TryAddSingleton<IActorProxyFactory>(proxyFactoryRegistration);
break;
}

if (configure != null)
{
services.Configure<ActorRuntimeOptions>(configure);
Expand Down
24 changes: 20 additions & 4 deletions src/Dapr.Jobs/Extensions/DaprJobsServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,25 +26,40 @@ public static class DaprJobsServiceCollectionExtensions
/// </summary>
/// <param name="serviceCollection">The <see cref="IServiceCollection"/>.</param>
/// <param name="configure">Optionally allows greater configuration of the <see cref="DaprJobsClient"/>.</param>
public static IServiceCollection AddDaprJobsClient(this IServiceCollection serviceCollection, Action<DaprJobsClientBuilder>? configure = null)
/// <param name="lifetime">The lifetime of the registered services.</param>
public static IServiceCollection AddDaprJobsClient(this IServiceCollection serviceCollection, Action<DaprJobsClientBuilder>? configure = null, ServiceLifetime lifetime = ServiceLifetime.Singleton)
{
ArgumentNullException.ThrowIfNull(serviceCollection, nameof(serviceCollection));

//Register the IHttpClientFactory implementation
serviceCollection.AddHttpClient();

serviceCollection.TryAddSingleton(serviceProvider =>
var registration = new Func<IServiceProvider, DaprJobsClient>(serviceProvider =>
{
var httpClientFactory = serviceProvider.GetRequiredService<IHttpClientFactory>();

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;
}

Expand All @@ -53,8 +68,9 @@ public static IServiceCollection AddDaprJobsClient(this IServiceCollection servi
/// </summary>
/// <param name="serviceCollection">The <see cref="IServiceCollection"/>.</param>
/// <param name="configure">Optionally allows greater configuration of the <see cref="DaprJobsClient"/> using injected services.</param>
/// <param name="lifetime">The lifetime of the registered services.</param>
/// <returns></returns>
public static IServiceCollection AddDaprJobsClient(this IServiceCollection serviceCollection, Action<IServiceProvider, DaprJobsClientBuilder>? configure)
public static IServiceCollection AddDaprJobsClient(this IServiceCollection serviceCollection, Action<IServiceProvider, DaprJobsClientBuilder>? configure, ServiceLifetime lifetime = ServiceLifetime.Singleton)
{
ArgumentNullException.ThrowIfNull(serviceCollection, nameof(serviceCollection));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,16 @@ public static class PublishSubscribeServiceCollectionExtensions
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/>.</param>
/// <param name="configure">Optionally allows greater configuration of the <see cref="DaprPublishSubscribeClient"/> using injected services.</param>
/// <param name="lifetime">The lifetime of the registered services.</param>
/// <returns></returns>
public static IServiceCollection AddDaprPubSubClient(this IServiceCollection services, Action<IServiceProvider, DaprPublishSubscribeClientBuilder>? configure = null)
public static IServiceCollection AddDaprPubSubClient(this IServiceCollection services, Action<IServiceProvider, DaprPublishSubscribeClientBuilder>? configure = null, ServiceLifetime lifetime = ServiceLifetime.Singleton)
{
ArgumentNullException.ThrowIfNull(services, nameof(services));

//Register the IHttpClientFactory implementation
services.AddHttpClient();

services.TryAddSingleton(serviceProvider =>
var registration = new Func<IServiceProvider, DaprPublishSubscribeClient>(serviceProvider =>
{
var httpClientFactory = serviceProvider.GetRequiredService<IHttpClientFactory>();

Expand All @@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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<Runtime.ActorRuntime>();
var daprClient2 = serviceProvider.GetService<Runtime.ActorRuntime>();

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<Runtime.ActorRuntime>();

await using var scope2 = serviceProvider.CreateAsyncScope();
var daprClient2 = scope2.ServiceProvider.GetService<Runtime.ActorRuntime>();

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<Runtime.ActorRuntime>();
var daprClient2 = serviceProvider.GetService<Runtime.ActorRuntime>();

Assert.NotNull(daprClient1);
Assert.NotNull(daprClient2);
Assert.NotSame(daprClient1, daprClient2);
}
}
142 changes: 97 additions & 45 deletions test/Dapr.AspNetCore.Test/DaprServiceCollectionExtensionsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<DaprClientBuilder>(
builder => builder.UseJsonSerializationOptions(
new JsonSerializerOptions()
{
PropertyNameCaseInsensitive = false
}
)
);
var clientBuilder = new Action<DaprClientBuilder>(
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<DaprClient>() as DaprClientGrpc;
DaprClientGrpc? daprClient = serviceProvider.GetService<DaprClient>() 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<TestConfigurationProvider>();
services.AddDaprClient((provider, builder) =>
{
var configProvider = provider.GetRequiredService<TestConfigurationProvider>();
var caseSensitivity = configProvider.GetCaseSensitivity();

var services = new ServiceCollection();
services.AddSingleton<TestConfigurationProvider>();
services.AddDaprClient((provider, builder) =>
builder.UseJsonSerializationOptions(new JsonSerializerOptions
{
var configProvider = provider.GetRequiredService<TestConfigurationProvider>();
var caseSensitivity = configProvider.GetCaseSensitivity();

builder.UseJsonSerializationOptions(new JsonSerializerOptions
{
PropertyNameCaseInsensitive = caseSensitivity
});
PropertyNameCaseInsensitive = caseSensitivity
});
});

var serviceProvider = services.BuildServiceProvider();
var serviceProvider = services.BuildServiceProvider();

DaprClientGrpc? client = serviceProvider.GetRequiredService<DaprClient>() as DaprClientGrpc;
DaprClientGrpc? client = serviceProvider.GetRequiredService<DaprClient>() 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<DaprClient>();
var daprClient2 = serviceProvider.GetService<DaprClient>();

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<DaprClient>();

await using var scope2 = serviceProvider.CreateAsyncScope();
var daprClient2 = scope2.ServiceProvider.GetService<DaprClient>();

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<DaprClient>();
var daprClient2 = serviceProvider.GetService<DaprClient>();

Assert.NotNull(daprClient1);
Assert.NotNull(daprClient2);
Assert.NotSame(daprClient1, daprClient2);
}


#if NET8_0_OR_GREATER
Expand All @@ -96,9 +149,8 @@ public void AddDaprClient_WithKeyedServices()
}
#endif

private class TestConfigurationProvider
{
public bool GetCaseSensitivity() => false;
}
private class TestConfigurationProvider
{
public bool GetCaseSensitivity() => false;
}
}
Loading
Loading