diff --git a/src/Autofac/Autofac.csproj b/src/Autofac/Autofac.csproj
index 20dc66fc3..f3fc244ef 100644
--- a/src/Autofac/Autofac.csproj
+++ b/src/Autofac/Autofac.csproj
@@ -112,11 +112,6 @@
True
ContainerResources.resx
-
- True
- True
- DependencyResolutionExceptionResources.resx
-
True
True
@@ -309,10 +304,6 @@
ResXFileCodeGenerator
ContainerResources.Designer.cs
-
- ResXFileCodeGenerator
- DependencyResolutionExceptionResources.Designer.cs
-
ResXFileCodeGenerator
LifetimeScopeResources.Designer.cs
diff --git a/src/Autofac/Core/DependencyResolutionException.cs b/src/Autofac/Core/DependencyResolutionException.cs
index 084293340..cb5f82e05 100644
--- a/src/Autofac/Core/DependencyResolutionException.cs
+++ b/src/Autofac/Core/DependencyResolutionException.cs
@@ -24,7 +24,6 @@
// OTHER DEALINGS IN THE SOFTWARE.
using System;
-using System.Globalization;
namespace Autofac.Core
{
@@ -57,27 +56,5 @@ public DependencyResolutionException(string message, Exception innerException)
: base(message, innerException)
{
}
-
- ///
- /// Gets a message that describes the current exception.
- ///
- ///
- /// The error message that explains the reason for the exception, or an empty string("").
- ///
- public override string Message
- {
- get
- {
- // Issue 343: Including the inner exception message with the
- // main message for easier debugging.
- var message = base.Message;
- if (InnerException == null)
- return message;
-
- var inner = InnerException.Message;
- message = string.Format(CultureInfo.CurrentCulture, DependencyResolutionExceptionResources.MessageNestingFormat, message, inner);
- return message;
- }
- }
}
}
diff --git a/src/Autofac/Core/DependencyResolutionExceptionResources.Designer.cs b/src/Autofac/Core/DependencyResolutionExceptionResources.Designer.cs
deleted file mode 100644
index 5e3043c33..000000000
--- a/src/Autofac/Core/DependencyResolutionExceptionResources.Designer.cs
+++ /dev/null
@@ -1,73 +0,0 @@
-//------------------------------------------------------------------------------
-//
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-//
-//------------------------------------------------------------------------------
-
-namespace Autofac.Core {
- using System;
- using System.Reflection;
-
-
- ///
- /// A strongly-typed resource class, for looking up localized strings, etc.
- ///
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class DependencyResolutionExceptionResources {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal DependencyResolutionExceptionResources() {
- }
-
- ///
- /// Returns the cached ResourceManager instance used by this class.
- ///
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Autofac.Core.DependencyResolutionExceptionResources", typeof(DependencyResolutionExceptionResources).GetTypeInfo().Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- ///
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- ///
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- ///
- /// Looks up a localized string similar to {0} ---> {1} (See inner exception for details.).
- ///
- internal static string MessageNestingFormat {
- get {
- return ResourceManager.GetString("MessageNestingFormat", resourceCulture);
- }
- }
- }
-}
diff --git a/src/Autofac/Core/DependencyResolutionExceptionResources.resx b/src/Autofac/Core/DependencyResolutionExceptionResources.resx
deleted file mode 100644
index da3c9db0e..000000000
--- a/src/Autofac/Core/DependencyResolutionExceptionResources.resx
+++ /dev/null
@@ -1,123 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- text/microsoft-resx
-
-
- 2.0
-
-
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- {0} ---> {1} (See inner exception for details.)
-
-
\ No newline at end of file
diff --git a/src/Autofac/Core/Resolving/ActivatorExtensions.cs b/src/Autofac/Core/Resolving/ActivatorExtensions.cs
new file mode 100644
index 000000000..5003f6f24
--- /dev/null
+++ b/src/Autofac/Core/Resolving/ActivatorExtensions.cs
@@ -0,0 +1,43 @@
+// This software is part of the Autofac IoC container
+// Copyright © 2011 Autofac Contributors
+// https://autofac.org
+//
+// 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 without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+
+using Autofac.Core.Activators.Delegate;
+
+namespace Autofac.Core.Resolving
+{
+ internal static class ActivatorExtensions
+ {
+ // This shorthand name for the activator is used in exception messages; for activator types
+ // where the limit type generally describes the activator exactly, we use that; for delegate
+ // activators, a variation on the type name is used to indicate this.
+ public static string DisplayName(this IInstanceActivator activator)
+ {
+ var fullName = activator.LimitType.FullName ?? "";
+ return activator is DelegateActivator ?
+ $"λ:{fullName}" :
+ fullName;
+ }
+ }
+}
diff --git a/src/Autofac/Core/Resolving/CircularDependencyDetector.cs b/src/Autofac/Core/Resolving/CircularDependencyDetector.cs
index 3e5c24e06..d83902b58 100644
--- a/src/Autofac/Core/Resolving/CircularDependencyDetector.cs
+++ b/src/Autofac/Core/Resolving/CircularDependencyDetector.cs
@@ -52,7 +52,7 @@ private static string CreateDependencyGraphTo(IComponentRegistration registratio
private static string Display(IComponentRegistration registration)
{
- return registration.Activator.LimitType.FullName ?? string.Empty;
+ return registration.Activator.DisplayName();
}
public static void CheckForCircularDependency(IComponentRegistration registration, Stack activationStack, int callDepth)
diff --git a/src/Autofac/Core/Resolving/ComponentActivationResources.Designer.cs b/src/Autofac/Core/Resolving/ComponentActivationResources.Designer.cs
index 2b6a4733a..d8ce5035b 100644
--- a/src/Autofac/Core/Resolving/ComponentActivationResources.Designer.cs
+++ b/src/Autofac/Core/Resolving/ComponentActivationResources.Designer.cs
@@ -8,9 +8,10 @@
//
//------------------------------------------------------------------------------
+using System.Reflection;
+
namespace Autofac.Core.Resolving {
using System;
- using System.Reflection;
///
@@ -71,7 +72,7 @@ internal class ComponentActivationResources {
}
///
- /// Looks up a localized string similar to An error occurred during the activation of a particular registration. See the inner exception for details. Registration: {0}.
+ /// Looks up a localized string similar to An exception was thrown while activating {0}..
///
internal static string ErrorDuringActivation {
get {
diff --git a/src/Autofac/Core/Resolving/ComponentActivationResources.resx b/src/Autofac/Core/Resolving/ComponentActivationResources.resx
index 36576edc3..28895a5b7 100644
--- a/src/Autofac/Core/Resolving/ComponentActivationResources.resx
+++ b/src/Autofac/Core/Resolving/ComponentActivationResources.resx
@@ -121,7 +121,7 @@
The activation has already been executed: {0}
- An error occurred during the activation of a particular registration. See the inner exception for details. Registration: {0}
+ An exception was thrown while activating {0}.
Unable to resolve the type '{0}' because the lifetime scope it belongs in can't be located. The following services are exposed by this registration:
diff --git a/src/Autofac/Core/Resolving/InstanceLookup.cs b/src/Autofac/Core/Resolving/InstanceLookup.cs
index d1ee8485e..9c6b27ff2 100644
--- a/src/Autofac/Core/Resolving/InstanceLookup.cs
+++ b/src/Autofac/Core/Resolving/InstanceLookup.cs
@@ -30,6 +30,8 @@
using System.Linq;
using System.Text;
using Autofac.Builder;
+using Autofac.Core.Activators;
+using Autofac.Core.Activators.Delegate;
using Autofac.Features.Decorators;
namespace Autofac.Core.Resolving
@@ -42,6 +44,7 @@ internal class InstanceLookup : IComponentContext, IInstanceLookup
private readonly ISharingLifetimeScope _activationScope;
private object _newInstance;
private bool _executed;
+ private const string ActivatorChainExceptionData = "ActivatorChain";
public InstanceLookup(
IComponentRegistration registration,
@@ -130,7 +133,7 @@ private object Activate(IEnumerable parameters, out object decoratorT
}
catch (Exception ex)
{
- throw new DependencyResolutionException(String.Format(CultureInfo.CurrentCulture, ComponentActivationResources.ErrorDuringActivation, this.ComponentRegistration), ex);
+ throw PropagateActivationException(this.ComponentRegistration.Activator, ex);
}
if (ComponentRegistration.Ownership == InstanceOwnership.OwnedByLifetimeScope)
@@ -148,6 +151,23 @@ private object Activate(IEnumerable parameters, out object decoratorT
return _newInstance;
}
+ private static DependencyResolutionException PropagateActivationException(IInstanceActivator activator, Exception exception)
+ {
+ var activatorChain = activator.DisplayName();
+ var innerException = exception;
+
+ if (exception.Data.Contains(ActivatorChainExceptionData) &&
+ exception.Data[ActivatorChainExceptionData] is string innerChain)
+ {
+ activatorChain = activatorChain + " -> " + innerChain;
+ innerException = exception.InnerException;
+ }
+
+ var result = new DependencyResolutionException(String.Format(CultureInfo.CurrentCulture, ComponentActivationResources.ErrorDuringActivation, activatorChain), innerException);
+ result.Data[ActivatorChainExceptionData] = activatorChain;
+ return result;
+ }
+
public void Complete()
{
if (!NewInstanceActivated) return;
diff --git a/test/Autofac.Test/Core/DependencyResolutionExceptionTests.cs b/test/Autofac.Test/Core/DependencyResolutionExceptionTests.cs
index 7cf50a8fb..d757268a0 100644
--- a/test/Autofac.Test/Core/DependencyResolutionExceptionTests.cs
+++ b/test/Autofac.Test/Core/DependencyResolutionExceptionTests.cs
@@ -1,52 +1,75 @@
using System;
-using System.Globalization;
using Autofac.Core;
using Xunit;
namespace Autofac.Test.Core
{
+ // ReSharper disable ClassNeverInstantiated.Local, UnusedParameter.Local
public class DependencyResolutionExceptionTests
{
- [Fact]
- public void Message_InnerExceptionMessageIncluded()
+ public class A
{
- // Issue 343: The inner exception message should be included in the main exception message.
- var inner = new Exception("Can't find file.");
- var dre = new DependencyResolutionException("Unable to resolve component.", inner);
- Assert.True(dre.Message.Contains("Can't find file."), "The exception message should include the inner exception message.");
- }
+ public const string Message = "This is the original exception.";
- [Fact]
- public void Message_NoInnerException()
- {
- // Issue 343: If there is no inner exception specified, the main exception message should not be modified.
- var dre = new DependencyResolutionException("Unable to resolve component.");
- Assert.Equal("Unable to resolve component.", dre.Message);
+ public A()
+ {
+ throw new InvalidOperationException(Message);
+ }
}
- [Fact]
- public void Message_NoMessageOrInnerException()
+ public class B
{
- // Issue 343: If there is no message or inner exception specified, the main exception message should not be modified.
- var dre = new DependencyResolutionException(null);
- Assert.True(dre.Message.Contains("Autofac.Core.DependencyResolutionException"), "The exception message should be the default exception message.");
+ public B(A a)
+ {
+ }
}
- [Fact]
- public void Message_NullInnerException()
+ public class C
{
- // Issue 343: If there is a null inner exception specified, the main exception message should not be modified.
- var dre = new DependencyResolutionException("Unable to resolve component.", null);
- Assert.Equal("Unable to resolve component.", dre.Message);
+ public C(B b)
+ {
+ }
}
[Fact]
- public void Message_NullMessageWithInnerException()
+ public void ExceptionMessageUnwrapsNestedResolutionFailures()
{
- // Issue 343: If there is no message but there is an inner exception specified, the main exception message should be modified.
- var inner = new Exception("Can't find file.");
- var dre = new DependencyResolutionException(null, inner);
- Assert.True(dre.Message.Contains("Can't find file."), "The exception message should include the inner exception message.");
+ var builder = new ContainerBuilder();
+ builder.RegisterType();
+ builder.Register(c => new B(c.Resolve()));
+ builder.RegisterType();
+
+ Exception ex;
+ using (var container = builder.Build())
+ {
+ ex = Assert.Throws(() => container.Resolve());
+ }
+
+ // Without unwrapping, the exception message is:
+ //
+ // An error occurred during the activation of a particular registration. See the inner exception
+ // for details. Registration: Activator = C (ReflectionActivator), Services =
+ // [Autofac.Test.ExceptionReportingTests+C], Lifetime = Autofac.Core.Lifetime.CurrentScopeLifetime,
+ // Sharing = None, Ownership = OwnedByLifetimeScope ---> An error occurred during the activation of
+ // a particular registration. See the inner exception for details. Registration: Activator = B
+ // (DelegateActivator), Services = [Autofac.Test.ExceptionReportingTests+B], Lifetime =
+ // Autofac.Core.Lifetime.CurrentScopeLifetime, Sharing = None, Ownership = OwnedByLifetimeScope --->
+ // An error occurred during the activation of a particular registration. See the inner exception for
+ // details. Registration: Activator = A (ReflectionActivator), Services =
+ // [Autofac.Test.ExceptionReportingTests+A], Lifetime = Autofac.Core.Lifetime.CurrentScopeLifetime,
+ // Sharing = None, Ownership = OwnedByLifetimeScope ---> An exception was thrown while invoking the
+ // constructor 'Void .ctor()' on type 'A'. ---> This is the original exception. (See inner exception
+ // for details.) (See inner exception for details.) (See inner exception for details.) (See inner
+ // exception for details.)
+ var n = GetType().FullName;
+ Assert.Equal($"An exception was thrown while activating {n}+C -> λ:{n}+B -> {n}+A.", ex.Message);
+
+ var inner = ex.InnerException;
+ Assert.IsType(inner);
+ Assert.Equal("An exception was thrown while invoking the constructor 'Void .ctor()' on type 'A'.", inner.Message);
+
+ Assert.IsType(inner.InnerException);
+ Assert.Equal(A.Message, inner.InnerException.Message);
}
}
}
diff --git a/test/Autofac.Test/Core/Resolving/CircularDependencyDetectorTests.cs b/test/Autofac.Test/Core/Resolving/CircularDependencyDetectorTests.cs
index c5ca13975..079dff59b 100644
--- a/test/Autofac.Test/Core/Resolving/CircularDependencyDetectorTests.cs
+++ b/test/Autofac.Test/Core/Resolving/CircularDependencyDetectorTests.cs
@@ -1,10 +1,6 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Linq;
-using System.Text;
-using System.Text.RegularExpressions;
using Autofac.Core;
-using Autofac.Core.Resolving;
using Xunit;
namespace Autofac.Test.Core.Resolving
@@ -19,11 +15,11 @@ public void OnCircularDependency_MessageDescribesCycle()
var target = builder.Build();
var de = Assert.Throws(() => target.Resolve