Skip to content

Commit

Permalink
Improved exception messages thrown in case of type initialization err…
Browse files Browse the repository at this point in the history
…ors. Fixes #812
  • Loading branch information
dotnetjunkie committed Jan 7, 2021
1 parent 622428f commit 3e752b2
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 3 deletions.
100 changes: 100 additions & 0 deletions src/SimpleInjector.Tests.Unit/InstanceProducerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,98 @@ public void GetInstance_ForSingletonValueType_CanBeResolved()
Assert.AreEqual(expectedValue, actualValue);
}

// #812
[TestMethod]
public void GetInstance_ResolvingATypeWithTypeInitializationException_ThrowsExpressiveException()
{
// Act
var container = new Container();

container.Register<TopLevelClassWithFailingTypeInitializer>();

// Act
Action action = () => container.GetInstance<TopLevelClassWithFailingTypeInitializer>();

// Assert
AssertThat.ThrowsWithExceptionMessageContains<ActivationException>(
"The type initializer for TopLevelClassWithFailingTypeInitializer threw an " +
"exception. <Inner exception>.",
action);
}

// #812
[TestMethod]
public void GetInstance_ResolvingANestedTypeWithTypeInitializationException_ThrowsExpressiveException()
{
// Act
var container = new Container();

container.Register<NestedClassWithFailingTypeInitializer>();

// Act
Action action = () => container.GetInstance<NestedClassWithFailingTypeInitializer>();

// Assert
AssertThat.ThrowsWithExceptionMessageContains<ActivationException>(
"The type initializer for InstanceProducerTests.NestedClassWithFailingTypeInitializer " +
"threw an exception. <Inner exception>.",
action);
}

// #812
[TestMethod]
public void GetInstance_ResolvingAGenericNestedTypeWithTypeInitializationException_ThrowsExpressiveException()
{
// Act
var container = new Container();

container.Register(typeof(NestedClassWithFailingTypeInitializer<>));

// Act
Action action = () => container.GetInstance<NestedClassWithFailingTypeInitializer<object>>();

// Assert
AssertThat.ThrowsWithExceptionMessageContains<ActivationException>(
"The type initializer for InstanceProducerTests" +
".NestedClassWithFailingTypeInitializer<object> threw an exception. <Inner exception>.",
action);
}

// #812
[TestMethod]
public void GetInstance_ResolvingASingletonWithTypeInitializationException_ThrowsExpressiveException()
{
// Act
var container = new Container();

container.RegisterSingleton<NestedClassWithFailingTypeInitializer>();

// Act
Action action = () => container.GetInstance<NestedClassWithFailingTypeInitializer>();

// Assert
AssertThat.ThrowsWithExceptionMessageContains<ActivationException>(
"The type initializer for InstanceProducerTests.NestedClassWithFailingTypeInitializer " +
"threw an exception. <Inner exception>.",
action);
}

public class NestedClassWithFailingTypeInitializer
{
static NestedClassWithFailingTypeInitializer()
{
throw new Exception("<Inner exception>.");
}
}

public class NestedClassWithFailingTypeInitializer<T>
{
static NestedClassWithFailingTypeInitializer()
{
throw new Exception("<Inner exception>.");
}
}

public class OneAndTwo : IOne, ITwo
{
}
Expand All @@ -381,4 +473,12 @@ public NodeFactory(IEnumerable<INode> nodes)
}
}
}

public class TopLevelClassWithFailingTypeInitializer
{
static TopLevelClassWithFailingTypeInitializer()
{
throw new Exception("<Inner exception>.");
}
}
}
37 changes: 34 additions & 3 deletions src/SimpleInjector/InstanceProducer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,10 @@ public Expression BuildExpression()

throw;
}
catch (TypeInitializationException ex)
{
throw this.MakeMoreExpressiveTypeInitializationException(ex);
}
catch (Exception ex)
{
if (this.MustWrapThrownException(ex))
Expand Down Expand Up @@ -610,9 +614,17 @@ private string BuildActivationExceptionMessage(Exception innerException)
private object BuildAndReplaceInstanceCreatorAndCreateFirstInstance()
{
this.instanceCreator = this.BuildInstanceCreator();
var instance = this.instanceCreator();
this.InstanceSuccessfullyCreated = true;
return instance;

try
{
var instance = this.instanceCreator();
this.InstanceSuccessfullyCreated = true;
return instance;
}
catch (TypeInitializationException ex)
{
throw this.MakeMoreExpressiveTypeInitializationException(ex);
}
}

// Prevents any recursive calls from taking place.
Expand Down Expand Up @@ -674,6 +686,25 @@ private static bool ShouldBeRegisteredAsAnExternalProducer(Registration registra
return !(registration is ExpressionRegistration);
}

private Exception MakeMoreExpressiveTypeInitializationException(
TypeInitializationException ex)
{
Type implementationType = this.Registration.ImplementationType;

// #812 The exceptions thrown by the runtime in case of a type initialization error are
// unproductive. Here we throw a more expressive exception. This is done by appending the
// inner exception's message to the exception message and replacing the type name with a
// 'friendly type name', which is especially useful in the case of generic types.
return new ActivationException(
ex.Message
// When the type in question is nested, the exception will contain just the simple
// type name, while for non-nested types, the full name is used (don't ask why).
.Replace($"'{implementationType.FullName}'", implementationType.ToFriendlyName())
.Replace($"'{implementationType.Name}'", implementationType.ToFriendlyName()) +
" " + ex.InnerException?.Message,
ex);
}

[ExcludeFromCodeCoverage]
internal sealed class InstanceProducerDebugView
{
Expand Down

0 comments on commit 3e752b2

Please sign in to comment.