using System; | |
using System.Collections.Generic; | |
using System.Web; | |
using System.Web.Mvc; | |
using System.Web.Optimization; | |
using System.Web.Routing; | |
using Microsoft.Extensions.DependencyInjection; | |
using WebApplication16; | |
using WebApplication16.Controllers; | |
[assembly: PreApplicationStartMethod(typeof(MvcApplication), "InitModule")] | |
namespace WebApplication16 | |
{ | |
public class MvcApplication : System.Web.HttpApplication | |
{ | |
public static void InitModule() | |
{ | |
RegisterModule(typeof(ServiceScopeModule)); | |
} | |
protected void Application_Start() | |
{ | |
var services = new ServiceCollection(); | |
ConfigureServices(services); | |
ServiceScopeModule.SetServiceProvider(services.BuildServiceProvider()); | |
AreaRegistration.RegisterAllAreas(); | |
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); | |
RouteConfig.RegisterRoutes(RouteTable.Routes); | |
BundleConfig.RegisterBundles(BundleTable.Bundles); | |
DependencyResolver.SetResolver(new ServiceProviderDependencyResolver()); | |
} | |
private void ConfigureServices(IServiceCollection services) | |
{ | |
services.AddScoped<ScopedThing>(); | |
services.AddTransient<HomeController>(); | |
} | |
} | |
public class ScopedThing : IDisposable | |
{ | |
public ScopedThing() | |
{ | |
} | |
public void Dispose() | |
{ | |
} | |
} | |
internal class ServiceScopeModule : IHttpModule | |
{ | |
private static ServiceProvider _serviceProvider; | |
public void Dispose() | |
{ | |
} | |
public void Init(HttpApplication context) | |
{ | |
context.BeginRequest += Context_BeginRequest; | |
context.EndRequest += Context_EndRequest; | |
} | |
private void Context_EndRequest(object sender, EventArgs e) | |
{ | |
var context = ((HttpApplication)sender).Context; | |
if (context.Items[typeof(IServiceScope)] is IServiceScope scope) | |
{ | |
scope.Dispose(); | |
} | |
} | |
private void Context_BeginRequest(object sender, EventArgs e) | |
{ | |
var context = ((HttpApplication)sender).Context; | |
context.Items[typeof(IServiceScope)] = _serviceProvider.CreateScope(); | |
} | |
public static void SetServiceProvider(ServiceProvider serviceProvider) | |
{ | |
_serviceProvider = serviceProvider; | |
} | |
} | |
internal class ServiceProviderDependencyResolver : IDependencyResolver | |
{ | |
public object GetService(Type serviceType) | |
{ | |
if (HttpContext.Current?.Items[typeof(IServiceScope)] is IServiceScope scope) | |
{ | |
return scope.ServiceProvider.GetService(serviceType); | |
} | |
throw new InvalidOperationException("IServiceScope not provided"); | |
} | |
public IEnumerable<object> GetServices(Type serviceType) | |
{ | |
if (HttpContext.Current?.Items[typeof(IServiceScope)] is IServiceScope scope) | |
{ | |
return scope.ServiceProvider.GetServices(serviceType); | |
} | |
throw new InvalidOperationException("IServiceScope not provided"); | |
} | |
} | |
} |
This comment has been minimized.
This comment has been minimized.
Your gist is complexly broken for scoped services. Everything is a singleton. |
This comment has been minimized.
This comment has been minimized.
Ok, thanks! I guess I'm trying to understand the "why" on the way you implemented the scoped service (using ScopedServiceModule and assembly attribute to initialize). Using the I could use a 3rd party IoC container (Ninject, Autofac, Unity, etc) as I have in the past and they definitely hold your hand to make misuse a little more difficult, but I'd like to use the functionality provided via the |
This comment has been minimized.
This comment has been minimized.
@aaronmbos Ah! In order to resolve scoped services you need to resolve a service from the scoped container. There should be a DI scope per request and calling Most 3rd party containers do a similar thing, see the Autofac.MVC integration https://github.com/autofac/Autofac.Mvc/blob/c86e656375cd4eec0dfd6aba4a60dd60d44a5789/src/Autofac.Integration.Mvc/RequestLifetimeHttpModule.cs |
This comment has been minimized.
This comment has been minimized.
@davidfowl thanks so much for taking the time to explain that a bit! Super helpful! |
This comment has been minimized.
This comment has been minimized.
Hey @davidfowl , I used your sample code here to build a library that simulates ASP.NET Core's Startup in ASP.NET MVC "classic" and I wanted to get your opinion of it if you'd be willing: https://github.com/arex388/Arex388.AspNet.Mvc.Startup Thanks! |
This comment has been minimized.
This comment has been minimized.
@arex388 it might be interesting to go further and look at what it means to run the generic host in a classic ASP.NET application as well. That way you get configuration, logging and DI. |
This comment has been minimized.
This comment has been minimized.
@davidfowl this helped tremendously! i inherited a .net app with no structure so i wanted to add a container to it and IServiceCollection is the one i'm the most familiar with. I noticed my scoped dependencies were acting like singletons and after about ten hours of pulling out my hair, i found your gist! thanks!! |
This comment has been minimized.
@davidfowl would this implementation be necessary for services that don't require disposal?
Take this gist for a contrived example with a WebAPI2 project. The scoped service doesn't require disposal, so would that implementation be sufficient or would I be better off implementing something similar to your example in the event that an injected service requires disposal?