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 · 3 comments
Closed

Support for intercepting/replacing constructed services. #1294

pauldotknopf opened this issue Mar 22, 2019 · 3 comments

Comments

@pauldotknopf
Copy link

pauldotknopf 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
Copy link
Author

pauldotknopf 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.

@analogrelay
Copy link

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.

@f135ta
Copy link

f135ta commented Apr 30, 2019

You're in luck - I wrote something to do AOP in Net Core (using the standard DI container) using Castle Proxy ;-) => https://github.com/f135ta/SimpleProxy

@ghost ghost locked as resolved and limited conversation to collaborators Dec 2, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants