Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
ASP.NET MVC and ServiceCollection sample
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");
}
}
}
@aaronmbos

This comment has been minimized.

Copy link

@aaronmbos aaronmbos commented Sep 26, 2019

@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?

@davidfowl

This comment has been minimized.

Copy link
Owner Author

@davidfowl davidfowl commented Sep 26, 2019

Your gist is complexly broken for scoped services. Everything is a singleton.

@aaronmbos

This comment has been minimized.

Copy link

@aaronmbos aaronmbos commented Sep 26, 2019

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 Microsoft.Extensions.DependencyInjection package in a .NET Framework 4.6+ project is appealing to me because it would simplify a migration of the app to .NET Core even if just slightly. At the same time I don't want to misuse the functionality due to my lack of knowledge/understanding.

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 Microsoft.Extensions.DependencyInjection package for the reasons I mentioned above.

@davidfowl

This comment has been minimized.

Copy link
Owner Author

@davidfowl davidfowl commented Sep 26, 2019

@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 GetService on that scope will give you the same instance throughout the request.

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

@aaronmbos

This comment has been minimized.

Copy link

@aaronmbos aaronmbos commented Sep 27, 2019

@davidfowl thanks so much for taking the time to explain that a bit! Super helpful!

@arex388

This comment has been minimized.

Copy link

@arex388 arex388 commented Jan 3, 2020

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!

@davidfowl

This comment has been minimized.

Copy link
Owner Author

@davidfowl davidfowl commented Jan 10, 2020

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

@GivenHansco

This comment has been minimized.

Copy link

@GivenHansco GivenHansco commented Apr 13, 2020

@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!!

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.