From 4bb22010b16e7f84f337e166d1ba102525e63dfd Mon Sep 17 00:00:00 2001 From: yallie Date: Thu, 25 Feb 2021 02:25:44 +0300 Subject: [PATCH] Undecorated service resolution succeeds, but decorated service resolution fails, #378. --- ...HIssue378_InconsistentResolutionFailure.cs | 128 ++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 test/DryIoc.IssuesTests/GHIssue378_InconsistentResolutionFailure.cs diff --git a/test/DryIoc.IssuesTests/GHIssue378_InconsistentResolutionFailure.cs b/test/DryIoc.IssuesTests/GHIssue378_InconsistentResolutionFailure.cs new file mode 100644 index 000000000..620698d75 --- /dev/null +++ b/test/DryIoc.IssuesTests/GHIssue378_InconsistentResolutionFailure.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Text; +using DryIoc.MefAttributedModel; +using NUnit.Framework; + +namespace DryIoc.IssuesTests +{ + [TestFixture] + public class GHIssue378_InconsistentResolutionFailure + { + [Test, Ignore("Fails on DryIoc v4.7.3")] + public void Nested_scopes_undecorated_resolution_succeeds_but_decorated_resolution_fails() + { + var c = new Container().WithMef() + .With(rules => rules + .WithoutThrowIfDependencyHasShorterReuseLifespan() + .WithDefaultReuse(Reuse.Scoped)); + + // 1. default registrations + c.RegisterExports(typeof(DecoratedService), typeof(UndecoratedService), typeof(DependencyService)); + + // 2. IDecoratedService should be created via a factory method + var decoratorSetup = Setup.DecoratorWith(useDecorateeReuse: false); + var factoryMethod = new Func, IDecoratedService>(CreateDecoratedService).Method; + c.Register(typeof(IDecoratedService), reuse: Reuse.Transient, made: Made.Of(factoryMethod), setup: decoratorSetup); + + // global scope is opened once on application startup + using (var globalScope = c.OpenScope()) + { + // request scopes are created for each request + using (var requestScope = globalScope.OpenScope()) + { + // succeeds + Assert.DoesNotThrow(() => + { + var service = requestScope.Resolve(); + service.Hello(); + }, + "Failed to use the undecorated service!"); + + // fails + Assert.DoesNotThrow(() => + { + var decorated = requestScope.Resolve(); + decorated.Hello(); + }, + "Failed to use the decorated service!"); + } + } + } + + static IDecoratedService CreateDecoratedService(Lazy lazy) + { + WriteLine("CreateDecoratedService is called!"); + return lazy.Value; + } + + private static void WriteLine(string s) => TestContext.Progress.WriteLine(s); + + public interface IUndecoratedService + { + void Hello(); + } + + // Code smell: the singleton service uses a dependency of a shorter lifespan, + // but the container is set up to allow this + [Export(typeof(IUndecoratedService)), PartCreationPolicy(CreationPolicy.Shared)] + public class UndecoratedService : IUndecoratedService + { + public UndecoratedService(Lazy dep) + { + WriteLine("UndecoratedService is created"); + Dependency = dep; + } + + private Lazy Dependency { get; set; } + + public void Hello() + { + WriteLine("UndecoratedService is accessing the Dependency..."); + + var cm = Dependency.Value; + WriteLine("Dependency returned: " + cm.Value); + } + } + + public interface IDecoratedService + { + void Hello(); + } + + // Code smell: the singleton service uses a dependency of a shorter lifespan, + // but the container is set up to allow this + [Export(typeof(IDecoratedService)), PartCreationPolicy(CreationPolicy.Shared)] + public class DecoratedService : IDecoratedService + { + public DecoratedService(Lazy dep) + { + WriteLine("DecoratedService is created"); + Dependency = dep; + } + + private Lazy Dependency { get; set; } + + public void Hello() + { + WriteLine("DecoratedService is accessing the Dependency..."); + + var cm = Dependency.Value; + WriteLine("Dependency returned: " + cm.Value); + } + } + + public interface IDependencyService + { + string Value { get; } + } + + // The only dependency of both decorated and undecorated services + [Export(typeof(IDependencyService))] + public class DependencyService : IDependencyService + { + public string Value => "Hello"; + } + } +}