Last active
September 23, 2020 06:25
-
-
Save Kahbazi/4cad9602fe9381f2b598c4c570a95e5f to your computer and use it in GitHub Desktop.
Integrating SimpleInjector and Microsoft DI in ASP.Net Owin/Katana
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Startup | |
{ | |
public void Configuration(IAppBuilder app) | |
{ | |
IServiceCollection serviceCollection = new ServiceCollection() | |
.AddLogging() | |
.AddAutoMapper() | |
.AddOptions(); | |
IServiceProvider serviceProvider = serviceCollection.EnableSimpleInjectorCrossWiring(container); | |
container.Register<ScopeAccessor>(Lifestyle.Scoped); | |
container.CrossWire<ILoggerFactory>(serviceProvider); | |
container.CrossWire<IMapper>(serviceProvider); | |
app.Use(async (context, next) => | |
{ | |
using (AsyncScopedLifestyle.BeginScope(container)) | |
using (container.GetInstance<ScopeAccessor>().ServiceScope) | |
{ | |
await next(); | |
} | |
}); | |
} | |
} | |
public class ScopeAccessor | |
{ | |
public ScopeAccessor(IServiceProvider serviceProvider) | |
{ | |
IServiceScopeFactory serviceScopeFactory = serviceProvider.GetService<IServiceScopeFactory>(); | |
ServiceScope = serviceScopeFactory.CreateScope(); | |
} | |
public IServiceScope ServiceScope { get; } | |
} | |
public static class SimpleInjectorExtensions | |
{ | |
private static readonly object CrossWireContextKey = new object(); | |
public static void CrossWire<TServiceType>(this Container container, IServiceProvider serviceProvider) | |
{ | |
container.CrossWire(typeof(TServiceType), serviceProvider); | |
} | |
public static void CrossWire(this Container container, Type serviceType, IServiceProvider serviceProvider) | |
{ | |
Registration registration = CreateCrossWireRegistration(container, serviceType, serviceProvider); | |
container.AddRegistration(serviceType, registration); | |
} | |
public static IServiceProvider EnableSimpleInjectorCrossWiring(this IServiceCollection services, Container container) | |
{ | |
if (container == null) | |
{ | |
throw new ArgumentNullException(nameof(container)); | |
} | |
if (services == null) | |
{ | |
throw new ArgumentNullException(nameof(services)); | |
} | |
if (container.GetItem(CrossWireContextKey) == null) | |
{ | |
container.SetItem(CrossWireContextKey, services); | |
} | |
IServiceProvider serviceProvider = services.BuildServiceProvider(); | |
container.RegisterSingleton(serviceProvider); | |
return serviceProvider; | |
} | |
private static Registration CreateCrossWireRegistration(Container container, Type serviceType, IServiceProvider serviceProvider) | |
{ | |
IServiceCollection services = GetServiceCollection(container); | |
Lifestyle lifestyle = DetermineLifestyle(serviceType, services); | |
Registration registration; | |
if (lifestyle == Lifestyle.Singleton) | |
{ | |
registration = lifestyle.CreateRegistration( | |
serviceType, | |
() => serviceProvider.GetRequiredService(serviceType), | |
container); | |
} | |
else | |
{ | |
registration = lifestyle.CreateRegistration( | |
serviceType, | |
() => container.GetInstance<ScopeAccessor>().ServiceScope.ServiceProvider.GetRequiredService(serviceType), | |
container); | |
} | |
if (lifestyle == Lifestyle.Transient && typeof(IDisposable).IsAssignableFrom(serviceType)) | |
{ | |
registration.SuppressDiagnosticWarning(DiagnosticType.DisposableTransientComponent, | |
justification: "This is a cross-wired service. ASP.NET Core will ensure it gets disposed."); | |
} | |
return registration; | |
} | |
private static Lifestyle DetermineLifestyle(Type serviceType, IServiceCollection services) | |
{ | |
var descriptor = FindDescriptor(serviceType, services); | |
// In case the service type is an IEnumerable, a registration can't be found, but collections are | |
// in Core always registered as Transient, so it's safe to fall back to the transient lifestyle. | |
return ToLifestyle(descriptor?.Lifetime ?? ServiceLifetime.Transient); | |
} | |
private static ServiceDescriptor FindDescriptor(Type serviceType, IServiceCollection services) | |
{ | |
var descriptor = services.LastOrDefault(d => d.ServiceType == serviceType); | |
if (descriptor == null && serviceType.GetTypeInfo().IsGenericType) | |
{ | |
var serviceTypeDefinition = serviceType.GetTypeInfo().GetGenericTypeDefinition(); | |
// In case the type is an IEnumerable<T>, no registration can be found and null is returned. | |
return services.LastOrDefault(d => d.ServiceType == serviceTypeDefinition); | |
} | |
else | |
{ | |
return descriptor; | |
} | |
} | |
private static IServiceCollection GetServiceCollection(Container container) | |
{ | |
var context = (IServiceCollection)container.GetItem(CrossWireContextKey); | |
if (context == null) | |
{ | |
throw new InvalidOperationException( | |
"Cross-wiring has to be enabled first. Please make sure the " + | |
$"{nameof(EnableSimpleInjectorCrossWiring)} extension method is called first by " + | |
"adding it to the ConfigureServices method as follows: " + Environment.NewLine + | |
$"services.{nameof(EnableSimpleInjectorCrossWiring)}(container);" + Environment.NewLine + | |
"See: https://simpleinjector.org/aspnetcore"); | |
} | |
return context; | |
} | |
private static Lifestyle ToLifestyle(ServiceLifetime lifetime) | |
{ | |
switch (lifetime) | |
{ | |
case ServiceLifetime.Singleton: return Lifestyle.Singleton; | |
case ServiceLifetime.Scoped: return Lifestyle.Scoped; | |
default: return Lifestyle.Transient; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment