diff --git a/Autofac.sln.DotSettings b/Autofac.sln.DotSettings
index 8c213009d..164b29a23 100644
--- a/Autofac.sln.DotSettings
+++ b/Autofac.sln.DotSettings
@@ -19,4 +19,5 @@
True
True
True
- True
\ No newline at end of file
+ True
+ True
\ No newline at end of file
diff --git a/src/Autofac/Features/Decorators/InstanceDecorator.cs b/src/Autofac/Features/Decorators/InstanceDecorator.cs
index ae170033c..913332e17 100644
--- a/src/Autofac/Features/Decorators/InstanceDecorator.cs
+++ b/src/Autofac/Features/Decorators/InstanceDecorator.cs
@@ -26,6 +26,7 @@
using System.Collections.Generic;
using System.Linq;
using Autofac.Core;
+using Autofac.Core.Registration;
namespace Autofac.Features.Decorators
{
@@ -37,6 +38,11 @@ internal static class InstanceDecorator
IComponentContext context,
IEnumerable parameters)
{
+ var instanceType = instance.GetType();
+
+ // Issue #965: Do not apply the decorator if already applied, or if the registration is for an adapter.
+ if (registration.IsAdapting() || registration.Activator.LimitType != instanceType) return instance;
+
var decoratorRegistrations = context.ComponentRegistry.DecoratorsFor(registration);
// ReSharper disable once PossibleMultipleEnumeration
@@ -56,7 +62,7 @@ internal static class InstanceDecorator
var serviceType = decorators[0].Service.ServiceType;
var resolveParameters = parameters as Parameter[] ?? parameters.ToArray();
- var decoratorContext = DecoratorContext.Create(instance.GetType(), serviceType, instance);
+ var decoratorContext = DecoratorContext.Create(instanceType, serviceType, instance);
foreach (var decorator in decorators)
{
diff --git a/test/Autofac.Specification.Test/Features/DecoratorTests.cs b/test/Autofac.Specification.Test/Features/DecoratorTests.cs
index 5859dcc35..ed16e69a6 100644
--- a/test/Autofac.Specification.Test/Features/DecoratorTests.cs
+++ b/test/Autofac.Specification.Test/Features/DecoratorTests.cs
@@ -98,7 +98,9 @@ public void CanResolveDecoratorWithFunc()
var factory = container.Resolve>();
- Assert.IsType(factory());
+ var decoratedService = factory();
+ Assert.IsType(decoratedService);
+ Assert.IsType(decoratedService.Decorated);
}
[Fact]
@@ -111,7 +113,9 @@ public void CanResolveDecoratorWithLazy()
var lazy = container.Resolve>();
- Assert.IsType(lazy.Value);
+ var decoratedService = lazy.Value;
+ Assert.IsType(decoratedService);
+ Assert.IsType(decoratedService.Decorated);
}
[Fact]
@@ -259,13 +263,12 @@ public void DecoratorAndDecoratedBothDisposedWhenSingleInstance()
Assert.Equal(1, decorated.DisposeCallCount);
}
- [Fact(Skip = "Issue #963: Need to figure out how to track which decorators have already been applied.")]
- public void DecoratorAppliedOnlyOnceToComponent()
+ [Fact]
+ public void DecoratorAppliedOnlyOnceToComponentWithExternalRegistrySource()
{
// #965: A nested lifetime scope that has a registration lambda
// causes the decorator to be applied twice - once for the container
- // level, once for the scope level. This doesn't seem to happen
- // if there's no registration lambda.
+ // level, and once for the scope level.
var builder = new ContainerBuilder();
builder.RegisterType().As();
builder.RegisterDecorator();
@@ -288,6 +291,7 @@ public void DecoratorCanBeAppliedToServiceRegisteredInChildLifetimeScope()
var instance = scope.Resolve();
Assert.IsType(instance);
+ Assert.IsType(instance.Decorated);
}
[Fact]
@@ -301,6 +305,7 @@ public void DecoratorCanBeRegisteredInChildLifetimeScope()
var scopedInstance = scope.Resolve();
Assert.IsType(scopedInstance);
+ Assert.IsType(scopedInstance.Decorated);
var rootInstance = container.Resolve();
Assert.IsType(rootInstance);
@@ -602,7 +607,7 @@ private abstract class Decorator : IDecoratedService
{
protected Decorator(IDecoratedService decorated)
{
- this.Decorated = decorated;
+ Decorated = decorated;
}
public IDecoratedService Decorated { get; }
@@ -635,6 +640,7 @@ public DecoratorWithContextA(IDecoratedService decorated, IDecoratorContext cont
public IDecoratorContext Context { get; }
}
+ // ReSharper disable once ClassNeverInstantiated.Local
private class DecoratorWithContextB : Decorator, IDecoratorWithContext
{
public DecoratorWithContextB(IDecoratedService decorated, IDecoratorContext context)
@@ -657,6 +663,7 @@ public DecoratorWithParameter(IDecoratedService decorated, string parameter)
public string Parameter { get; }
}
+ // ReSharper disable once ClassNeverInstantiated.Local
private class DisposableDecorator : Decorator, IDisposable
{
public DisposableDecorator(IDecoratedService decorated)
@@ -672,6 +679,7 @@ public void Dispose()
}
}
+ // ReSharper disable once ClassNeverInstantiated.Local
private class DisposableImplementor : IDecoratedService, IDisposable
{
public IDecoratedService Decorated => this;
@@ -689,11 +697,13 @@ private class ImplementorA : IDecoratedService
public IDecoratedService Decorated => this;
}
+ // ReSharper disable once ClassNeverInstantiated.Local
private class ImplementorB : IDecoratedService
{
public IDecoratedService Decorated => this;
}
+ // ReSharper disable once ClassNeverInstantiated.Local
private class ImplementorWithParameters : IDecoratedService
{
public ImplementorWithParameters(string parameter)
@@ -706,11 +716,13 @@ public ImplementorWithParameters(string parameter)
public string Parameter { get; }
}
+ // ReSharper disable once ClassNeverInstantiated.Local
private class ImplementorWithSomeOtherService : IDecoratedService, ISomeOtherService
{
public IDecoratedService Decorated => this;
}
+ // ReSharper disable once ClassNeverInstantiated.Local
private class StartableImplementation : IDecoratedService, IStartable
{
public IDecoratedService Decorated => this;
diff --git a/test/Autofac.Test/Features/Decorators/OpenGenericDecoratorTests.cs b/test/Autofac.Test/Features/Decorators/OpenGenericDecoratorTests.cs
index 670379fe8..d3c8aa874 100644
--- a/test/Autofac.Test/Features/Decorators/OpenGenericDecoratorTests.cs
+++ b/test/Autofac.Test/Features/Decorators/OpenGenericDecoratorTests.cs
@@ -14,6 +14,7 @@ public interface IService
{
}
+ // ReSharper disable once UnusedTypeParameter
public interface ISomeOtherService
{
}
@@ -295,7 +296,9 @@ public void CanResolveDecoratorWithFunc()
var factory = container.Resolve>>();
- Assert.IsType>(factory());
+ var decoratedService = factory();
+ Assert.IsType>(decoratedService);
+ Assert.IsType>(decoratedService.Decorated);
}
[Fact]
@@ -308,7 +311,9 @@ public void CanResolveDecoratorWithLazy()
var lazy = container.Resolve>>();
- Assert.IsType>(lazy.Value);
+ var decoratedService = lazy.Value;
+ Assert.IsType>(decoratedService);
+ Assert.IsType>(decoratedService.Decorated);
}
[Fact]
@@ -528,6 +533,23 @@ public void CanResolveClosedGenericDecoratorOverOpenGeneric()
Assert.IsType>(instance.Decorated);
}
+ [Fact]
+ public void DecoratorAppliedOnlyOnceToComponentWithExternalRegistrySource()
+ {
+ // #965: A nested lifetime scope that has a registration lambda
+ // causes the decorator to be applied twice - once for the container
+ // level, and once for the scope level.
+ var builder = new ContainerBuilder();
+ builder.RegisterGeneric(typeof(ImplementorA<>)).As(typeof(IDecoratedService<>));
+ builder.RegisterGenericDecorator(typeof(DecoratorA<>), typeof(IDecoratedService<>));
+ var container = builder.Build();
+
+ var scope = container.BeginLifetimeScope(b => { });
+ var service = scope.Resolve>();
+ Assert.IsType>(service);
+ Assert.IsType>(service.Decorated);
+ }
+
[Fact]
public void DecoratorCanBeAppliedToServiceRegisteredInChildLifetimeScope()
{
@@ -539,6 +561,7 @@ public void DecoratorCanBeAppliedToServiceRegisteredInChildLifetimeScope()
var instance = scope.Resolve>();
Assert.IsType>(instance);
+ Assert.IsType>(instance.Decorated);
}
[Fact]
@@ -552,6 +575,7 @@ public void DecoratorCanBeRegisteredInChildLifetimeScope()
var scopedInstance = scope.Resolve>();
Assert.IsType>(scopedInstance);
+ Assert.IsType>(scopedInstance.Decorated);
var rootInstance = container.Resolve>();
Assert.IsType>(rootInstance);