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

RFC: Add ServiceProviderAccessor to allow access to IServiceProvider from Interceptor #364

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
16 changes: 16 additions & 0 deletions src/Temporalio.Extensions.Hosting/IServiceProviderAccessor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


namespace Temporalio.Extensions.Hosting
{
/// <summary>
/// Provides access to the current, scoped <see cref="IServiceProvider"/> if
/// one is available.
/// </summary>
public interface IServiceProviderAccessor
{
/// <summary>
/// Gets or sets the current service provider.
/// </summary>
IServiceProvider? ServiceProvider { get; set; }
}
}
42 changes: 42 additions & 0 deletions src/Temporalio.Extensions.Hosting/ServiceProviderAccessor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using System.Threading;

namespace Temporalio.Extensions.Hosting
{
/// <summary>
/// Provides an implementation of <see cref="IServiceProvider" /> based on
/// the current execution context.
/// </summary>
public class ServiceProviderAccessor : IServiceProviderAccessor
{
private static readonly AsyncLocal<ServiceProviderHolder> ServiceProviderCurrent = new();
Copy link
Author

@tdg5 tdg5 Nov 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A System.Collections.Concurrent.ConcurrentDictionary that is keyed on the Activity's unique ID could be a simpler replacement for this and would work just as well since it would depend on the AsyncLocal holding the current Activity.


/// <inheritdoc/>
public IServiceProvider? ServiceProvider
{
get => ServiceProviderCurrent.Value?.ServiceProvider;

set
{
var holder = ServiceProviderCurrent.Value;
if (holder != null)
{
// Clear current IServiceProvider trapped in the AsyncLocals, as its done.
holder.ServiceProvider = null;
}

if (value != null)
{
// Use an object indirection to hold the IServiceProvider in the AsyncLocal,
// so it can be cleared in all ExecutionContexts when its cleared.
ServiceProviderCurrent.Value = new ServiceProviderHolder { ServiceProvider = value };
}
}
}

private sealed class ServiceProviderHolder
{
public IServiceProvider? ServiceProvider { get; set; }
}
}
}
12 changes: 12 additions & 0 deletions src/Temporalio.Extensions.Hosting/ServiceProviderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,14 @@ public static ActivityDefinition CreateTemporalActivityDefinition(
#else
var scope = provider.CreateScope();
#endif
IServiceProviderAccessor? serviceProviderAccessor =
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inspiration taken from ASP.NET's DefaultHttpContextFactory.

scope.ServiceProvider.GetService<IServiceProviderAccessor>();

if (serviceProviderAccessor is not null)
{
serviceProviderAccessor.ServiceProvider = scope.ServiceProvider;
}

try
{
object? result;
Expand Down Expand Up @@ -111,6 +119,10 @@ public static ActivityDefinition CreateTemporalActivityDefinition(
}
finally
{
if (serviceProviderAccessor is not null)
{
serviceProviderAccessor.ServiceProvider = null;
}
#if NET6_0_OR_GREATER
await scope.DisposeAsync().ConfigureAwait(false);
#else
Expand Down