From 5138fe4b892f184c8c70158d0f9a656a1aa9748f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=88=E5=85=89=E5=8F=8C=E5=88=80?= <89009143@qq.com> Date: Sat, 2 Feb 2019 17:53:39 +0800 Subject: [PATCH] add interceptor and dynamicProxy support for LightInject (#151) --- extras/AspectCore.Extras.sln | 18 +- .../AspectCore.Extensions.LightInject.csproj | 19 +++ .../ContainerBuilderExtensions.cs | 158 ++++++++++++++++++ .../LightInjectServiceResolver.cs | 36 ++++ ...ectCore.Extensions.LightInject.Test.csproj | 22 +++ .../AsyncIncreamentAttribute.cs | 31 ++++ .../AsyncInterceptorTests.cs | 105 ++++++++++++ .../AwaitBeforeInvokeNextTests.cs | 65 +++++++ .../ExcuteTimesTests.cs | 89 ++++++++++ .../RegistryTest.cs | 83 +++++++++ 10 files changed, 624 insertions(+), 2 deletions(-) create mode 100644 extras/src/AspectCore.Extensions.LightInject/AspectCore.Extensions.LightInject.csproj create mode 100644 extras/src/AspectCore.Extensions.LightInject/ContainerBuilderExtensions.cs create mode 100644 extras/src/AspectCore.Extensions.LightInject/LightInjectServiceResolver.cs create mode 100644 extras/test/AspectCore.Extensions.LightInject.Test/AspectCore.Extensions.LightInject.Test.csproj create mode 100644 extras/test/AspectCore.Extensions.LightInject.Test/AsyncIncreamentAttribute.cs create mode 100644 extras/test/AspectCore.Extensions.LightInject.Test/AsyncInterceptorTests.cs create mode 100644 extras/test/AspectCore.Extensions.LightInject.Test/AwaitBeforeInvokeNextTests.cs create mode 100644 extras/test/AspectCore.Extensions.LightInject.Test/ExcuteTimesTests.cs create mode 100644 extras/test/AspectCore.Extensions.LightInject.Test/RegistryTest.cs diff --git a/extras/AspectCore.Extras.sln b/extras/AspectCore.Extras.sln index de9efd9a..835c4b3f 100644 --- a/extras/AspectCore.Extras.sln +++ b/extras/AspectCore.Extras.sln @@ -43,9 +43,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspectCore.Extensions.DataA EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspectCore.Extensions.DependencyInjection.ConsoleSample", "sample\AspectCore.Extensions.DependencyInjection.ConsoleSample\AspectCore.Extensions.DependencyInjection.ConsoleSample.csproj", "{EBCFF74A-4787-4A3E-A83D-0DC0329CFD2E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspectCore.Extensions.Hosting", "src\AspectCore.Extensions.Hosting\AspectCore.Extensions.Hosting.csproj", "{96DC1BED-E5E0-4E68-AF04-05115BE39B7A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspectCore.Extensions.Hosting", "src\AspectCore.Extensions.Hosting\AspectCore.Extensions.Hosting.csproj", "{96DC1BED-E5E0-4E68-AF04-05115BE39B7A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspectCore.Extensions.Hosting.Tests", "test\AspectCore.Extensions.Hosting.Tests\AspectCore.Extensions.Hosting.Tests.csproj", "{9547A839-70F0-4FAA-B574-9A61082FE567}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspectCore.Extensions.Hosting.Tests", "test\AspectCore.Extensions.Hosting.Tests\AspectCore.Extensions.Hosting.Tests.csproj", "{9547A839-70F0-4FAA-B574-9A61082FE567}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspectCore.Extensions.LightInject", "src\AspectCore.Extensions.LightInject\AspectCore.Extensions.LightInject.csproj", "{E3973799-7370-4D17-882F-D48CFB8C10F3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspectCore.Extensions.LightInject.Test", "test\AspectCore.Extensions.LightInject.Test\AspectCore.Extensions.LightInject.Test.csproj", "{8E8A9BBB-0168-4A90-9477-FF2C325ECDCB}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -125,6 +129,14 @@ Global {9547A839-70F0-4FAA-B574-9A61082FE567}.Debug|Any CPU.Build.0 = Debug|Any CPU {9547A839-70F0-4FAA-B574-9A61082FE567}.Release|Any CPU.ActiveCfg = Release|Any CPU {9547A839-70F0-4FAA-B574-9A61082FE567}.Release|Any CPU.Build.0 = Release|Any CPU + {E3973799-7370-4D17-882F-D48CFB8C10F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E3973799-7370-4D17-882F-D48CFB8C10F3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E3973799-7370-4D17-882F-D48CFB8C10F3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E3973799-7370-4D17-882F-D48CFB8C10F3}.Release|Any CPU.Build.0 = Release|Any CPU + {8E8A9BBB-0168-4A90-9477-FF2C325ECDCB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8E8A9BBB-0168-4A90-9477-FF2C325ECDCB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8E8A9BBB-0168-4A90-9477-FF2C325ECDCB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8E8A9BBB-0168-4A90-9477-FF2C325ECDCB}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -148,6 +160,8 @@ Global {EBCFF74A-4787-4A3E-A83D-0DC0329CFD2E} = {E933A059-A6AC-46AC-928D-33656D7FE93A} {96DC1BED-E5E0-4E68-AF04-05115BE39B7A} = {EF35FB72-D934-45A8-9856-E60EA2623135} {9547A839-70F0-4FAA-B574-9A61082FE567} = {BA3DCC4B-D8B9-4761-AAD9-4B1363140049} + {E3973799-7370-4D17-882F-D48CFB8C10F3} = {EF35FB72-D934-45A8-9856-E60EA2623135} + {8E8A9BBB-0168-4A90-9477-FF2C325ECDCB} = {BA3DCC4B-D8B9-4761-AAD9-4B1363140049} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {B7A2B7A3-2AF0-4769-8E22-4FAA41E80591} diff --git a/extras/src/AspectCore.Extensions.LightInject/AspectCore.Extensions.LightInject.csproj b/extras/src/AspectCore.Extensions.LightInject/AspectCore.Extensions.LightInject.csproj new file mode 100644 index 00000000..a84079eb --- /dev/null +++ b/extras/src/AspectCore.Extensions.LightInject/AspectCore.Extensions.LightInject.csproj @@ -0,0 +1,19 @@ + + + + + + Interceptor and dynamicProxy support for LightInject via AspectCore Framework. + DynamicProxy;Aop;LightInject;AspectCore + Interceptor and dynamicProxy support for LightInject via AspectCore Framework. + false + netstandard2.0 + + + + + + + + + diff --git a/extras/src/AspectCore.Extensions.LightInject/ContainerBuilderExtensions.cs b/extras/src/AspectCore.Extensions.LightInject/ContainerBuilderExtensions.cs new file mode 100644 index 00000000..d2f60bc7 --- /dev/null +++ b/extras/src/AspectCore.Extensions.LightInject/ContainerBuilderExtensions.cs @@ -0,0 +1,158 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using AspectCore.Configuration; +using AspectCore.DynamicProxy; +using AspectCore.DynamicProxy.Parameters; +using AspectCore.Injector; +using LightInject; +using IServiceContainer = LightInject.IServiceContainer; + +namespace AspectCore.Extensions.LightInject +{ + public enum RegistryType + { + ByType, + ByInstance, + ByFactory + } + + public static class ContainerBuilderExtensions + { + private static readonly string[] _nonAspect = + { + "LightInject.*", + "LightInject" + }; + + private static readonly string[] _excepts = new[] + { + "Microsoft.Extensions.Logging", + "Microsoft.Extensions.Options", + "System", + "System.*", + "IHttpContextAccessor", + "ITelemetryInitializer", + "IHostingEnvironment", + }.Concat(_nonAspect).ToArray(); + + public static IServiceContainer RegisterDynamicProxy(this IServiceContainer container, + IAspectConfiguration aspectConfig = null, + Action configure = null) + { + if (container == null) + { + throw new ArgumentNullException(nameof(container)); + } + aspectConfig = aspectConfig ?? new AspectConfiguration(); + + foreach (var m in _nonAspect) + { + aspectConfig.NonAspectPredicates.AddNamespace(m); + } + + configure?.Invoke(aspectConfig); + + container.RegisterInstance(aspectConfig) + .Register(typeof(IManyEnumerable<>), typeof(ManyEnumerable<>)) + .RegisterInstance(container) + .Register() + .Register() + .Register() + .Register() + .Register() + .Register() + .Register() + .Register() + .Register() + .Register() + .Register() + .Register() + .Register() + .Register() + .Register() + .Register(); + + container.Decorate(aspectConfig.CreateDecorator()); + + return container; + } + + private static RegistryType GetRegistryType(this ServiceRegistration registration) + { + if (registration.FactoryExpression != null) return RegistryType.ByFactory; + else if (registration.Value != null) return RegistryType.ByInstance; + else return RegistryType.ByType; + } + + private static Type GetImplType(this ServiceRegistration registration) + { + switch (registration.GetRegistryType()) + { + case RegistryType.ByType: return registration.ImplementingType; + case RegistryType.ByInstance: return registration.Value.GetType(); + case RegistryType.ByFactory: return registration.FactoryExpression.Method.ReturnType; + default: throw new ArgumentOutOfRangeException(); + } + } + + private static DecoratorRegistration CreateDecorator(this IAspectConfiguration aspectConfiguration) + { + var reg = new DecoratorRegistration() + { + CanDecorate = s => CanDecorate(s, aspectConfiguration), + ImplementingTypeFactory = CreateProxyType + }; + return reg; + } + + private static Type CreateProxyType(IServiceFactory factory, ServiceRegistration registration) + { + var serviceType = registration.ServiceType.GetTypeInfo(); + var implType = registration.GetImplType(); + var proxyTypeGenerator = factory.GetInstance(); + + if (serviceType.IsClass) + { + return proxyTypeGenerator.CreateClassProxyType(serviceType, implType); + } + else if (serviceType.IsGenericTypeDefinition) + { + return proxyTypeGenerator.CreateClassProxyType(implType, implType); + } + else + { + return proxyTypeGenerator.CreateInterfaceProxyType(serviceType, implType); + } + } + + private static bool CanDecorate(ServiceRegistration registration, IAspectConfiguration aspectConfiguration) + { + var serviceType = registration.ServiceType.GetTypeInfo(); + var implType = registration.GetImplType().GetTypeInfo(); + + if (implType.IsProxy() || !implType.CanInherited()) + { + return false; + } + if (_excepts.Any(x => implType.Name.Matches(x)) || _excepts.Any(x => implType.Namespace.Matches(x))) + { + return false; + } + if (!serviceType.CanInherited() || serviceType.IsNonAspect()) + { + return false; + } + + var aspectValidator = new AspectValidatorBuilder(aspectConfiguration).Build(); + if (!aspectValidator.Validate(serviceType, true) && !aspectValidator.Validate(implType, false)) + { + return false; + } + return true; + } + + } +} diff --git a/extras/src/AspectCore.Extensions.LightInject/LightInjectServiceResolver.cs b/extras/src/AspectCore.Extensions.LightInject/LightInjectServiceResolver.cs new file mode 100644 index 00000000..a24bc4a4 --- /dev/null +++ b/extras/src/AspectCore.Extensions.LightInject/LightInjectServiceResolver.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Text; +using AspectCore.DynamicProxy; +using AspectCore.Injector; +using LightInject; +using IServiceContainer = LightInject.IServiceContainer; + +namespace AspectCore.Extensions.LightInject +{ + [NonAspect] + internal class LightInjectServiceResolver : IServiceResolver + { + private readonly IServiceContainer _container; + + public LightInjectServiceResolver(IServiceContainer container) + { + _container = container; + } + + public object GetService(Type serviceType) + { + return _container.TryGetInstance(serviceType); + } + + public void Dispose() + { + _container.Dispose(); + } + + public object Resolve(Type serviceType) + { + return _container.TryGetInstance(serviceType); + } + } +} diff --git a/extras/test/AspectCore.Extensions.LightInject.Test/AspectCore.Extensions.LightInject.Test.csproj b/extras/test/AspectCore.Extensions.LightInject.Test/AspectCore.Extensions.LightInject.Test.csproj new file mode 100644 index 00000000..78c1bfec --- /dev/null +++ b/extras/test/AspectCore.Extensions.LightInject.Test/AspectCore.Extensions.LightInject.Test.csproj @@ -0,0 +1,22 @@ + + + + netcoreapp2.0 + portable + true + false + false + false + + + + + + + + + + + + + diff --git a/extras/test/AspectCore.Extensions.LightInject.Test/AsyncIncreamentAttribute.cs b/extras/test/AspectCore.Extensions.LightInject.Test/AsyncIncreamentAttribute.cs new file mode 100644 index 00000000..b335a04b --- /dev/null +++ b/extras/test/AspectCore.Extensions.LightInject.Test/AsyncIncreamentAttribute.cs @@ -0,0 +1,31 @@ +using System; +using System.Threading.Tasks; +using AspectCore.DynamicProxy; + +namespace AspectCoreTest.LightInject +{ + [AttributeUsage(AttributeTargets.Method)] + public class AsyncIncreamentAttribute : AbstractInterceptorAttribute + { + public override async Task Invoke(AspectContext context, AspectDelegate next) + { + await context.Invoke(next); + await Task.Delay(100); // 此处模拟一个真.异步方法,用于测试线程上下文切换 + + if (context.ReturnValue is Task task) + { + var result = await task; + context.ReturnValue = Task.FromResult(result + 1); + } + else if (context.ReturnValue is ValueTask valueTask) + { + var result = await valueTask; + context.ReturnValue = new ValueTask(result + 1); + } + else if (context.ReturnValue is int result) + { + context.ReturnValue = result + 1; + } + } + } +} \ No newline at end of file diff --git a/extras/test/AspectCore.Extensions.LightInject.Test/AsyncInterceptorTests.cs b/extras/test/AspectCore.Extensions.LightInject.Test/AsyncInterceptorTests.cs new file mode 100644 index 00000000..d9e8456f --- /dev/null +++ b/extras/test/AspectCore.Extensions.LightInject.Test/AsyncInterceptorTests.cs @@ -0,0 +1,105 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using AspectCore.Extensions.LightInject; +using LightInject; +using Xunit; + +namespace AspectCoreTest.LightInject +{ + public class AsyncService + { + [AsyncIncreament] + public virtual void DonotGet(int num) + { + } + + [AsyncIncreament] + public virtual Task DonotGetAsync(int num) + { + return Task.CompletedTask; + } + + [AsyncIncreament] + public virtual int Get(int num) + { + return num; + } + + [AsyncIncreament] + public virtual async Task GetAsyncWithTask(int num) + { + await Task.Delay(100); + return num; + } + + [AsyncIncreament] + public virtual async ValueTask GetAsyncWithValueTask(int num) + { + await Task.Delay(100); + return num; + } + } + + public class AsyncMethodTests + { + public static IEnumerable GetNumbers() + { + yield return new object[] { 1 }; + yield return new object[] { 10 }; + yield return new object[] { 100 }; + } + + private static IServiceContainer CreateContainer() + { + var c = new ServiceContainer(); + c.RegisterDynamicProxy() + .Register(); + return c; + } + + [Theory] + [MemberData(nameof(GetNumbers))] + public void TestIncreamentForVoid(int input) + { + var container = CreateContainer(); + var service = container.GetInstance(); + service.DonotGet(input); + } + + [Theory] + [MemberData(nameof(GetNumbers))] + public async Task TestIncreamentForTask(int input) + { + var container = CreateContainer(); + var service = container.GetInstance(); + await service.DonotGetAsync(input); + } + + [Theory] + [MemberData(nameof(GetNumbers))] + public void TestIncreamentForResult(int input) + { + var container = CreateContainer(); + var service = container.GetInstance(); + Assert.Equal(input + 1, service.Get(input)); + } + + [Theory] + [MemberData(nameof(GetNumbers))] + public async Task TestIncreamentForTaskResult(int input) + { + var container = CreateContainer(); + var service = container.GetInstance(); + Assert.Equal(input + 1, await service.GetAsyncWithTask(input)); + } + + [Theory] + [MemberData(nameof(GetNumbers))] + public async Task TestIncreamentForValueTaskResult(int input) + { + var container = CreateContainer(); + var service = container.GetInstance(); + Assert.Equal(input + 1, await service.GetAsyncWithValueTask(input)); + } + } +} diff --git a/extras/test/AspectCore.Extensions.LightInject.Test/AwaitBeforeInvokeNextTests.cs b/extras/test/AspectCore.Extensions.LightInject.Test/AwaitBeforeInvokeNextTests.cs new file mode 100644 index 00000000..730bc97e --- /dev/null +++ b/extras/test/AspectCore.Extensions.LightInject.Test/AwaitBeforeInvokeNextTests.cs @@ -0,0 +1,65 @@ +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using AspectCore.DynamicProxy; +using AspectCore.Extensions.LightInject; +using LightInject; +using Xunit; + +namespace AspectCoreTest.LightInject +{ + [AttributeUsage(AttributeTargets.Method)] + public class AwaitBeforeInvokeAttribute : AbstractInterceptorAttribute + { + public override async Task Invoke(AspectContext context, AspectDelegate next) + { + await Task.Delay(100); + if (context.Proxy is AwaitBeforeInvokeTester tester + && tester.Cts.IsCancellationRequested) + { + context.ReturnValue = Task.FromResult(-1); + return; + } + await context.Invoke(next); + } + } + + public class AwaitBeforeInvokeTester + { + public CancellationTokenSource Cts { get; set; } + + [AwaitBeforeInvoke] + public virtual async Task ExecuteAsync() + { + await Task.Delay(100); + return 1; + } + } + + public class AwaitBeforeInvokeNextTests + { + private static IServiceContainer CreateContainer() + { + var c = new ServiceContainer(new ContainerOptions + { + DefaultServiceSelector = s => s.Last(), + EnablePropertyInjection = false + }); + c.RegisterDynamicProxy() + .Register(); + return c; + } + + [Fact] + public async Task Test() + { + var container = CreateContainer(); + var service = container.GetInstance(); + service.Cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); + var result = await service.ExecuteAsync(); + Assert.Equal(1, result); + Assert.False(service.Cts.IsCancellationRequested); + } + } +} diff --git a/extras/test/AspectCore.Extensions.LightInject.Test/ExcuteTimesTests.cs b/extras/test/AspectCore.Extensions.LightInject.Test/ExcuteTimesTests.cs new file mode 100644 index 00000000..c54d5d6b --- /dev/null +++ b/extras/test/AspectCore.Extensions.LightInject.Test/ExcuteTimesTests.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using AspectCore.DynamicProxy; +using AspectCore.Extensions.LightInject; +using LightInject; +using Xunit; + +namespace AspectCoreTest.LightInject +{ + [AttributeUsage(AttributeTargets.Method)] + public class ExcuteMultiTimesAttribute : AbstractInterceptorAttribute + { + public override async Task Invoke(AspectContext context, AspectDelegate next) + { + await context.Invoke(next); + var para = context.Parameters?.FirstOrDefault(); + if (para is int num) + { + for (var i = 0; i < num - 1; i++) + { + await context.Invoke(next); + } + } + } + } + + public class ExcuteTimesTester + { + private int _excuteTimesOfFoo; + public int ExcuteTimesOfFoo => _excuteTimesOfFoo; + + private int _excuteTimesOfFooAsync; + public int ExcuteTimesOfFooAsync => _excuteTimesOfFooAsync; + + [ExcuteMultiTimes] + public virtual void Foo(int times) + { + Interlocked.Increment(ref _excuteTimesOfFoo); + } + + [ExcuteMultiTimes] + public virtual Task FooAsync(int times) + { + Interlocked.Increment(ref _excuteTimesOfFooAsync); + return Task.CompletedTask; + } + } + + public class ExcuteTimesTests + { + public static IEnumerable Numbers { get; } + = new[] { 1, 10, 100 }.Select(m => new object[] { m }); + + private static IServiceContainer CreateContainer() + { + var c = new ServiceContainer(new ContainerOptions + { + DefaultServiceSelector = s => s.Last(), + EnablePropertyInjection = false + }); + c.RegisterDynamicProxy() + .Register(); + return c; + } + + [Theory] + [MemberData(nameof(Numbers))] + public void Test(int times) + { + var container = CreateContainer(); + var service = container.GetInstance(); + service.Foo(times); + Assert.Equal(times, service.ExcuteTimesOfFoo); + } + + [Theory] + [MemberData(nameof(Numbers))] + public async Task TestAsync(int times) + { + var container = CreateContainer(); + var service = container.GetInstance(); + await service.FooAsync(times); + Assert.Equal(times, service.ExcuteTimesOfFooAsync); + } + } +} diff --git a/extras/test/AspectCore.Extensions.LightInject.Test/RegistryTest.cs b/extras/test/AspectCore.Extensions.LightInject.Test/RegistryTest.cs new file mode 100644 index 00000000..9282a421 --- /dev/null +++ b/extras/test/AspectCore.Extensions.LightInject.Test/RegistryTest.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using AspectCore.DynamicProxy; +using AspectCore.Extensions.LightInject; +using LightInject; +using Xunit; + +namespace AspectCoreTest.LightInject +{ + public class RegistryTest + { + public const int Result = 9; + + public interface IService + { + [AsyncIncreament] + int Foo(); + } + public class Service : IService + { + [AsyncIncreament] + public virtual int Foo() => Result; + } + + private static IServiceContainer CreateContainer() + { + return new ServiceContainer().RegisterDynamicProxy(); + } + + [Fact] + public void TestRegisterByInterface() + { + var container = CreateContainer(); + container.Register(new PerRequestLifeTime()); + var inter = container.GetInstance(); + Assert.Equal(Result + 1, inter.Foo()); + } + + [Fact] + public void TestRegisterBySelf() + { + var container = CreateContainer(); + container.Register(new PerRequestLifeTime()); + var obj = container.GetInstance(); + Assert.Equal(Result + 1, obj.Foo()); + } + + [Fact] + public void TestRegisterByInstance() + { + var container = CreateContainer(); + var service = new Service(); + container.RegisterInstance(service); + container.RegisterInstance(service); + + var inter = container.GetInstance(); + Assert.Equal(Result + 1, inter.Foo()); + Assert.Same(inter, container.GetInstance()); + + var obj = container.GetInstance(); + Assert.Equal(Result + 1, obj.Foo()); + Assert.Same(obj, container.GetInstance()); + } + + [Fact] + public void TestRegisterByFactory() + { + var container = CreateContainer(); + container.Register(s => new Service(), new PerRequestLifeTime()); + container.Register(s => new Service(), new PerRequestLifeTime()); + + var inter = container.GetInstance(); + Assert.Equal(Result + 1, inter.Foo()); + + var obj = container.GetInstance(); + Assert.Equal(Result + 1, obj.Foo()); + + Assert.NotSame(inter, obj); + } + } +}