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

Support for intercepting/replacing constructed services. #1294

Closed
pauldotknopf opened this Issue Mar 22, 2019 · 2 comments

Comments

Projects
None yet
3 participants
@pauldotknopf
Copy link

commented Mar 22, 2019

I have services registered that have methods that return Task<T> types.

I have a requirement that any caller of these methods needs to run continuations on a single thread (with a custom SynchronizationContext).

Right now, each caller of the service needs to manage the SynchronizationContext before calling the service.

public interface IService1
{
    Task MethodAsync();
}

public class Service1 : IService1
{
    public async Task MethodAsync()
    {
        await Task.Yield();
    }
}

Any time I use IService1, I could manage the SynchronizationContext myself, but I run the risk of having some callers forget to do so.

public class Service2
{
    private readonly Service1 _service1;

    public Service2(Service1 service1)
    {
        _service1 = service1;
    }

    public async Task Invoke()
    {
        using (new SingleThreadContext()) // Manages/restores SynchronizationContext
        {
            await _service1.MethodAsync();
        }
    }
}

I could also implement the SynchronizationContext directly in the implementation Service1, but that would require me to have a lot of boilerplate in every service method that needs this (there is a lot).

public class Service1 : IService1
{
    public async Task MethodAsync()
    {
        using (new SingleThreadContext()) // Manages/restores SynchronizationContext
        {
            await Task.Yield();
        }
    }
}

I'd like to use an aspect-oriented approach to configuring an entire service (IService) to run continouations with a specific SynchronizationContext.

public class SingleThreadedAttribute : Attribute
{
    
}

[SingleThreaded]
public interface IService1
{
    Task MethodAsync();
}

Or maybe even at the method level.

public interface IService1
{
    [SingleThreaded]
    Task MethodAsync();
}

When building my IServiceProvider, I'd like to intercept and potentially replace any resolved service.

I think I could implement this myself if ServiceProviderEngine was public, but it is internal.

Maybe we can add an optional delegate to ServiceProviderOptions?

public class ServiceProviderOptions
{
    public delegate object ServiceInterceptionDelegate(Type serviceType, object service);
    public ServiceInterceptionDelegate Intercept { get; set; }
}

Then, I could implement ServiceInterceptionDelegate to dynamically inspect Type serviceType, then optionally use Reflection.Emit to build a wrapping interface implementation that takes the original service as a parameter, and performing all my aspect-oriented concerns within.

I could see this also being used for diagnostics/timings/etc.

@pauldotknopf

This comment has been minimized.

Copy link
Author

commented Mar 22, 2019

Side question, is there a nifty .NET lib our there that would allow me to generate wrapping proxy classes for interfaces in this manner? I could Reflection.Emit myself, but I have to imagine there is something out there.

edit: Found something.

@anurse

This comment has been minimized.

Copy link
Member

commented Apr 12, 2019

We're not planning to add this kind of feature to our built-in DI container at this time. It sounds like this more advanced scenario might be a good fit for plugging in a different DI container.

@anurse anurse closed this Apr 12, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.