Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
…ctor into feature-554
  • Loading branch information
dotnetjunkie committed Mar 14, 2019
2 parents db39214 + 728f548 commit 164b5ae
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ InstanceProducer IDependencyInjectionBehavior.GetInstanceProducer(

if (local != null)
{
if (consumer.Target.TargetType.IsValueType && this.container.IsVerifying())
if (consumer.Target.TargetType.IsValueType && this.container.IsVerifying)
{
throw new InvalidOperationException(
"You can't use Verify() if the factory product contains value types.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ private static IHttpContextAccessor GetHttpContextAccessor(IServiceProvider appS
"Type 'Microsoft.AspNetCore.Http.IHttpContextAccessor' is not available in the " +
"IApplicationBuilder.ApplicationServices collection. Please make sure it is " +
"registered by adding it to the ConfigureServices method as follows: " + Environment.NewLine +
"services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();");
"services.AddHttpContextAccessor();");
}

return accessor;
Expand Down
84 changes: 65 additions & 19 deletions src/SimpleInjector.Integration.Web/WebRequestLifestyle.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#region Copyright Simple Injector Contributors
/* The Simple Injector is an easy-to-use Inversion of Control library for .NET
*
* Copyright (c) 2013-2016 Simple Injector Contributors
* Copyright (c) 2013-2019 Simple Injector Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction, including
Expand All @@ -23,6 +23,7 @@
namespace SimpleInjector.Integration.Web
{
using System;
using System.Collections.Generic;
using System.Web;

/// <summary>
Expand Down Expand Up @@ -88,17 +89,17 @@ internal static void CleanUpWebRequest(HttpContext context)
{
Requires.IsNotNull(context, nameof(context));

var scope = (Scope)context.Items[ScopeKey];
// NOTE: We explicitly don't remove the scopes from the items dictionary, because if anything
// is resolved from the container after this point during the request, that would cause the
// creation of a new Scope that will never be disposed. This would make it seem like the
// application is working, while instead we are failing silently. By not removing the scope,
// this will cause it to throw an ObjectDisposedException once it is accessed after
// this point; effectively making the application to fail fast.
var scopes = (List<Scope>)context.Items[ScopeKey];

if (scope != null)
if (!(scopes is null))
{
// NOTE: We explicitly don't remove the object from the items dictionary, because if anything
// is resolved from the container after this point during the request, that would cause the
// creation of a new Scope, that will never be disposed. This would make it seem like the
// application is working, while instead we are failing silently. By not removing the object,
// this will cause the Scope to throw an ObjectDisposedException once it is accessed after
// this point; effectively making the application to fail fast.
scope.Dispose();
DisposeScopes(scopes);
}
}

Expand Down Expand Up @@ -128,24 +129,69 @@ private static Scope GetOrCreateScope(Container container)
{
HttpContext context = HttpContext.Current;

if (context == null)
if (context is null)
{
return null;
}

var scope = (Scope)context.Items[ScopeKey];
// The assumption is that there will only be a very limited set of containers used per request
// (typically just one), which makes using a List<T> much faster (and less memory intensive)
// than using a dictionary.
var scopes = (List<Scope>)context.Items[ScopeKey];

if (scope == null)
if (scopes is null)
{
// If there are multiple container instances that run on the same request (which is a
// strange but valid scenario), all containers will get the same Scope instance for that
// request. This behavior is correct and even allows all instances that are registered for
// disposal to be disposed in reversed order of creation, independent of the container that
// created them.
context.Items[ScopeKey] = scope = new Scope();
context.Items[ScopeKey] = scopes = new List<Scope>(capacity: 2);
}

var scope = FindScopeForContainer(scopes, container);

if (scope is null)
{
scopes.Add(scope = new Scope(container));
}

return scope;
}

private static Scope FindScopeForContainer(List<Scope> scopes, Container container)
{
foreach (var scope in scopes)
{
if (scope.Container == container)
{
return scope;
}
}

return null;
}

private static void DisposeScopes(List<Scope> scopes)
{
if (scopes.Count != 1)
{
DisposeScopesInReverseOrder(scopes);
}
else
{
// Optimization: don't create a master scope if there is only one scope (the common case).
scopes[0].Dispose();
}
}

private static void DisposeScopesInReverseOrder(List<Scope> scopes)
{
// Here we use a 'master' scope that will hold the real scopes. This allows all scopes
// to be disposed, even if a scope's Dispose method throws an exception. Scopes will
// also be disposed in opposite order of creation.
using (var masterScope = new Scope())
{
foreach (var scope in scopes)
{
masterScope.RegisterForDisposal(scope);
}
}
}
}
}
7 changes: 4 additions & 3 deletions src/SimpleInjector/Advanced/AdvancedExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,14 @@ public static bool IsLocked(this Container container)
/// <param name="container">The container.</param>
/// <returns><c>true</c> if the specified container is verifying; otherwise, <c>false</c>.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="container"/> is null.</exception>
[Obsolete("Please use Container." + nameof(Container.IsVerifying) + " instead. " +
"This method will be removed in a future release.",
error: false)]
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
public static bool IsVerifying(this Container container)
{
Requires.IsNotNull(container, nameof(container));

// Need to check, because IsVerifying will throw when its ThreadLocal<T> is disposed.
container.ThrowWhenDisposed();

return container.IsVerifying;
}

Expand Down
9 changes: 8 additions & 1 deletion src/SimpleInjector/Container.Common.cs
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,14 @@ private interface IInstanceInitializer
/// false.</value>
public bool IsVerifying
{
get { return this.isVerifying.Value; }
get
{
// Need to check, because IsVerifying will throw when its ThreadLocal<T> is disposed.
this.ThrowWhenDisposed();

return this.isVerifying.Value;
}

private set { this.isVerifying.Value = value; }
}

Expand Down
2 changes: 1 addition & 1 deletion src/SimpleInjector/Scope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ private static TImplementation GetScopelessInstance<TImplementation>(
ScopedRegistration<TImplementation> registration)
where TImplementation : class
{
if (registration.Container.IsVerifying())
if (registration.Container.IsVerifying)
{
return registration.Container.VerificationScope.GetInstanceInternal(registration);
}
Expand Down

0 comments on commit 164b5ae

Please sign in to comment.