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

Use in Web API #40

Open
Tandis5 opened this issue Apr 26, 2021 · 10 comments
Open

Use in Web API #40

Tandis5 opened this issue Apr 26, 2021 · 10 comments

Comments

@Tandis5
Copy link

Tandis5 commented Apr 26, 2021

Just saw a demo of this on YouTube (shoutout to Nick Chapsas... love his channel!) and it looks very interesting! However, 95% of what I do is in a web API. Do you have plans to support a clean way of using Jab in a web API? I could use the default DI to inject my Jab "service provider" into controllers/classes and request what I need from there, but that's not a clean solution IMO.

@LechuckThePirate
Copy link

+1

@pakrym
Copy link
Owner

pakrym commented Apr 27, 2021

This (Jab fully replacing the default DI) is something I would love to implement. But unfortunately there are two technical issues that stand in the way:

  1. In a typical ASP.NET Core app service configuration happens during runtime via the AddSingleton/Transient calls and might be based on the runtime logic (if (environment.IsDevelopment)). It's very hard to get a correct service graph during compilation time as it requires executing the app.
  2. Source generators are governed by the C# accessibility rules while dynamic methods used in default DI are not. What it means is that even if I was able to get the full and correct service graph I can't generate new ImplementationType() calls because the ImplementationType is, in many cases, internal.

@pakrym
Copy link
Owner

pakrym commented Apr 27, 2021

So while fully replacing the default service provider is hard short term I'll still try to think if there is any way to integrate them in a clean way.

I'm also open to ideas of how you would like to see it.

@Tandis5
Copy link
Author

Tandis5 commented Apr 27, 2021

Hi Pavel... thanks for the reply! I have no doubt that it's well beyond my current knowledge to understand how it all works under the covers. As far as ideas on how we would see it (at least for the environment logic)...

[ServiceProvider]
[Scoped(typeof(IService), typeof(DevelopmentServiceImplementation), Environment = Environments.Development)]
[Scoped(typeof(IService), typeof(IntegrationServiceImplementation), Environment = "Integration")]
[Scoped(typeof(IService), typeof(ServiceImplementation))]
internal partial class MyServiceProvider { }

So Jab would generate the injection code for all 3 scenarios (development, integration, default) and use the appropriate injection based on the current running environment at runtime. A "default" (no environment specified) would be required (build would fail without it) to cover any environments not listed.

Just my thoughts. Thanks again for the response!

@LechuckThePirate
Copy link

Maybe you could have a look at https://github.com/YairHalberstadt/stronginject/tree/main/Samples/AspNetCore .. it's a similar solution to yours, and someone managed to workaround ASP.NET controllers dependency injection.

I would like even to have a look myself at it when I'm a little more free, but right now I don't have time... maybe in 15 days I can have a look at it... if I can apply the same solution as theirs I would make a PR for you ;)

@pakrym
Copy link
Owner

pakrym commented May 3, 2021

The linked approach has a problem: now you have 2 DI containers managing the same set of objects causing problems like multiple disposal. It also makes it hard to resolve outer DI services from the source generated container.

I have a branch where I try to marry Microsoft.Extensions.DependencyInjection with Jab and make them aware of each other and cooperating. It's very much WIP but might result in something interesting.

@dazinator
Copy link

dazinator commented May 8, 2021

This (Jab fully replacing the default DI) is something I would love to implement. But unfortunately there are two technical issues that stand in the way:

  1. In a typical ASP.NET Core app service configuration happens during runtime via the AddSingleton/Transient calls and might be based on the runtime logic (if (environment.IsDevelopment)). It's very hard to get a correct service graph during compilation time as it requires executing the app.
  2. Source generators are governed by the C# accessibility rules while dynamic methods used in default DI are not. What it means is that even if I was able to get the full and correct service graph I can't generate new ImplementationType() calls because the ImplementationType is, in many cases, internal.

Just chucking out some ideas:

  • Decorate the typical asp.net core host IServiceProvider but intercept calls for jab services and forward to jab container instead.

I think if you wanted to instead build a jab container for all of the asp.net core dependencies it would be a lot of work to figure out the various runtime conditions like environment or config checks like you have mentioned.

Another approach:

  • Allow jab container to "import" the runtime asp.net core IServiceProvider as a fallback for resolving services. In other words if you try to resolve a service from the jab container but it isn't registered in jab container, it can be resolved from the fallback runtime IServiceProvider. Then supply some asp.net core middleware that can swap out the IServiceProvider at the start of a request, to the jab container. This would then mean jab container is used to inject mvc controllers etc and and user will get the benefit of being able to inject compile time (from jab) or runtime (via jab's ability to fallback to the host IServiceProvider) dependencies?

Forgive me if you've already established a way forward with this!

@MisinformedDNA
Copy link

AutoFaq can also consume Microsoft's DI. I agree that not everything needs to be compile-time.

@dotnetprofessional
Copy link

Any update on this? Just discovered this project and I like the concept. Though like others backend work is my bread and butter and honestly that's where the perf gains are most useful, don't really care too much about saving a few ns on a client, but on the server that = $$ saved.

@R2D221
Copy link

R2D221 commented Oct 2, 2023

I made a prototype integration with Microsoft.Extensions.DependencyInjection:

https://github.com/R2D221/Jab.MediIntegration/blob/master/WebApplication1/MyServiceProvider.Medi.cs

It may be rough and not optimized beyond the obvious, but for the moment I think it gets the job done.

Essentialy what I made was the following:

  • Used the UseServiceProviderFactory method to acquire MEDI's ServiceCollection
  • Added wrappers for both the ServiceProvider and the Scope, so whenever MEDI requests a service provider it gets the wrapper
  • In the wrappers, we have the resolution logic, both for MEDI services and our own.
    • The MEDI services are registered directly with Jab's disposables, so that we don't have the multiple disposal problem mentioned before.
  • Additionally, if one of our own services needs to inject one of MEDI services, we have to add a [Singleton]/[Scoped]/[Transient] attribute, but with a Factory that points to our MEDI service logic.

I don't know if this still aligns with the spirit of the project, but I think this could be useful to be able to handle the dynamic nature of Microsoft.Extensions.DependencyInjection while still allowing us to add our compile-time dependencies.

If you'd want to refine this and include it officially in the project, I'd be happy to help. Even if not, I'm happy to share my solution with anyone who needs it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants