From dfb383fb82029b43977c648bdb49d09d5af33d77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Mon, 8 May 2017 14:05:34 +0300 Subject: [PATCH] Created BuildServiceProviderFromFactory extension. --- .../AbpDesk/ConsoleDemo/Program.cs | 2 +- .../AbpAutofacServiceCollectionExtensions.cs | 14 +----- .../CastleAbpMethodInvocationAdapter.cs | 12 +++-- src/Volo.Abp.TestBase/AbpIntegratedTest.cs | 2 +- .../ServiceCollectionCommonExtensions.cs | 50 ++++++++++++++++++- .../AbpDesk/AbpDeskApplicationTestBase.cs | 5 -- .../Interception/Autofac_Interception_Test.cs | 12 +---- .../DynamicProxy/AbpInterceptionTestBase.cs | 39 ++++++++++++++- .../Volo/Abp/DynamicProxy/CachedTestObject.cs | 18 +++++++ .../SimpleResultCacheTestInterceptor.cs | 37 ++++++++++++++ 10 files changed, 153 insertions(+), 38 deletions(-) create mode 100644 test/Volo.Abp.Tests/Volo/Abp/DynamicProxy/CachedTestObject.cs create mode 100644 test/Volo.Abp.Tests/Volo/Abp/DynamicProxy/SimpleResultCacheTestInterceptor.cs diff --git a/src/AbpDesk/AbpDesk.ConsoleDemo/AbpDesk/ConsoleDemo/Program.cs b/src/AbpDesk/AbpDesk.ConsoleDemo/AbpDesk/ConsoleDemo/Program.cs index 13d3465558..c4169e9186 100644 --- a/src/AbpDesk/AbpDesk.ConsoleDemo/AbpDesk/ConsoleDemo/Program.cs +++ b/src/AbpDesk/AbpDesk.ConsoleDemo/AbpDesk/ConsoleDemo/Program.cs @@ -31,7 +31,7 @@ namespace AbpDesk.ConsoleDemo AddPlugIns(options); }); - using (var scope = services.BuildAutofacServiceProvider().CreateScope()) + using (var scope = services.BuildServiceProviderFromFactory().CreateScope()) { application.Initialize(scope.ServiceProvider); diff --git a/src/Volo.Abp.Autofac/Microsoft/Extensions/DependencyInjection/AbpAutofacServiceCollectionExtensions.cs b/src/Volo.Abp.Autofac/Microsoft/Extensions/DependencyInjection/AbpAutofacServiceCollectionExtensions.cs index 61e8718653..21d38a2148 100644 --- a/src/Volo.Abp.Autofac/Microsoft/Extensions/DependencyInjection/AbpAutofacServiceCollectionExtensions.cs +++ b/src/Volo.Abp.Autofac/Microsoft/Extensions/DependencyInjection/AbpAutofacServiceCollectionExtensions.cs @@ -24,17 +24,7 @@ namespace Microsoft.Extensions.DependencyInjection public static IServiceProvider BuildAutofacServiceProvider([NotNull] this IServiceCollection services, Action builderAction = null) { - Check.NotNull(services, nameof(services)); - - var serviceProviderFactory = services.GetSingletonInstanceOrNull>(); - if (serviceProviderFactory == null) - { - throw new AbpException($"Could not find {typeof(IServiceProviderFactory).FullName} in {services}. Use {nameof(AbpAutofacAbpApplicationCreationOptionsExtensions.UseAutofac)} before!"); - } - - var builder = serviceProviderFactory.CreateBuilder(services); - builderAction?.Invoke(builder); - return serviceProviderFactory.CreateServiceProvider(builder); + return services.BuildServiceProviderFromFactory(builderAction); } - } + } } diff --git a/src/Volo.Abp.Castle.Core/Volo/Abp/Castle/DynamicProxy/CastleAbpMethodInvocationAdapter.cs b/src/Volo.Abp.Castle.Core/Volo/Abp/Castle/DynamicProxy/CastleAbpMethodInvocationAdapter.cs index 81aee2e5d1..2ce330ec66 100644 --- a/src/Volo.Abp.Castle.Core/Volo/Abp/Castle/DynamicProxy/CastleAbpMethodInvocationAdapter.cs +++ b/src/Volo.Abp.Castle.Core/Volo/Abp/Castle/DynamicProxy/CastleAbpMethodInvocationAdapter.cs @@ -19,10 +19,12 @@ namespace Volo.Abp.Castle.DynamicProxy public object ReturnValue { - get => Invocation.ReturnValue; + get => _actualReturnValue ?? Invocation.ReturnValue; set => Invocation.ReturnValue = value; } + private object _actualReturnValue; + protected IInvocation Invocation { get; } public CastleAbpMethodInvocationAdapter(IInvocation invocation) @@ -43,10 +45,10 @@ namespace Volo.Abp.Castle.DynamicProxy public Task ProceedAsync() { Invocation.Proceed(); - - return Invocation.Method.IsAsync() - ? (Task) Invocation.ReturnValue - : Task.FromResult(Invocation.ReturnValue); + _actualReturnValue = Invocation.ReturnValue; + return Invocation.Method.IsAsync() + ? (Task)_actualReturnValue + : Task.FromResult(_actualReturnValue); } } } \ No newline at end of file diff --git a/src/Volo.Abp.TestBase/AbpIntegratedTest.cs b/src/Volo.Abp.TestBase/AbpIntegratedTest.cs index 6d78afccd9..a036717745 100644 --- a/src/Volo.Abp.TestBase/AbpIntegratedTest.cs +++ b/src/Volo.Abp.TestBase/AbpIntegratedTest.cs @@ -51,7 +51,7 @@ namespace Volo.Abp.TestBase protected virtual IServiceProvider CreateServiceProvider(IServiceCollection services) { - return services.BuildServiceProvider(); + return services.BuildServiceProviderFromFactory(); } public void Dispose() diff --git a/src/Volo.Abp/Microsoft/Extensions/DependencyInjection/ServiceCollectionCommonExtensions.cs b/src/Volo.Abp/Microsoft/Extensions/DependencyInjection/ServiceCollectionCommonExtensions.cs index 7a8f24ec13..0e77097ad2 100644 --- a/src/Volo.Abp/Microsoft/Extensions/DependencyInjection/ServiceCollectionCommonExtensions.cs +++ b/src/Volo.Abp/Microsoft/Extensions/DependencyInjection/ServiceCollectionCommonExtensions.cs @@ -1,5 +1,9 @@ using System; using System.Linq; +using System.Reflection; +using JetBrains.Annotations; +using Volo; +using Volo.Abp; namespace Microsoft.Extensions.DependencyInjection { @@ -22,5 +26,49 @@ namespace Microsoft.Extensions.DependencyInjection return service; } - } + + public static IServiceProvider BuildServiceProviderFromFactory([NotNull] this IServiceCollection services) + { + Check.NotNull(services, nameof(services)); + + foreach (var service in services) + { + var factoryInterface = service.ImplementationInstance?.GetType() + .GetTypeInfo() + .GetInterfaces() + .FirstOrDefault(i => i.GetTypeInfo().IsGenericType && + i.GetGenericTypeDefinition() == typeof(IServiceProviderFactory<>)); + + if (factoryInterface == null) + { + continue; + } + + var containerBuilderType = factoryInterface.GenericTypeArguments[0]; + return (IServiceProvider)typeof(ServiceCollectionCommonExtensions) + .GetTypeInfo() + .GetMethods() + .Single(m => m.Name == nameof(BuildServiceProviderFromFactory) && m.IsGenericMethod) + .MakeGenericMethod(containerBuilderType) + .Invoke(null, new object[] { services, null }); + } + + return services.BuildServiceProvider(); + } + + public static IServiceProvider BuildServiceProviderFromFactory([NotNull] this IServiceCollection services, Action builderAction = null) + { + Check.NotNull(services, nameof(services)); + + var serviceProviderFactory = services.GetSingletonInstanceOrNull>(); + if (serviceProviderFactory == null) + { + throw new AbpException($"Could not find {typeof(IServiceProviderFactory).FullName} in {services}."); + } + + var builder = serviceProviderFactory.CreateBuilder(services); + builderAction?.Invoke(builder); + return serviceProviderFactory.CreateServiceProvider(builder); + } + } } \ No newline at end of file diff --git a/test/AbpDesk/AbpDesk.Application.Tests/AbpDesk/AbpDeskApplicationTestBase.cs b/test/AbpDesk/AbpDesk.Application.Tests/AbpDesk/AbpDeskApplicationTestBase.cs index 7300048891..3e31ae2d6d 100644 --- a/test/AbpDesk/AbpDesk.Application.Tests/AbpDesk/AbpDeskApplicationTestBase.cs +++ b/test/AbpDesk/AbpDesk.Application.Tests/AbpDesk/AbpDeskApplicationTestBase.cs @@ -21,11 +21,6 @@ namespace AbpDesk options.UseAutofac(); } - protected override IServiceProvider CreateServiceProvider(IServiceCollection services) - { - return services.BuildAutofacServiceProvider(); - } - protected virtual void SeedTestData() { using (var scope = ServiceProvider.CreateScope()) diff --git a/test/Volo.Abp.Autofac.Tests/Volo/Abp/Autofac/Interception/Autofac_Interception_Test.cs b/test/Volo.Abp.Autofac.Tests/Volo/Abp/Autofac/Interception/Autofac_Interception_Test.cs index 6dc5847ec7..86ee896d36 100644 --- a/test/Volo.Abp.Autofac.Tests/Volo/Abp/Autofac/Interception/Autofac_Interception_Test.cs +++ b/test/Volo.Abp.Autofac.Tests/Volo/Abp/Autofac/Interception/Autofac_Interception_Test.cs @@ -1,22 +1,12 @@ -using System; -using Microsoft.Extensions.DependencyInjection; -using Volo.Abp.Castle.DynamicProxy; -using Volo.Abp.DynamicProxy; +using Volo.Abp.DynamicProxy; namespace Volo.Abp.Autofac.Interception { public class Autofac_Interception_Test : AbpInterceptionTestBase { - //TODO: Sımplify using autofac in tests! - protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) { options.UseAutofac(); } - - protected override IServiceProvider CreateServiceProvider(IServiceCollection services) - { - return services.BuildAutofacServiceProvider(); - } } } diff --git a/test/Volo.Abp.Tests/Volo/Abp/DynamicProxy/AbpInterceptionTestBase.cs b/test/Volo.Abp.Tests/Volo/Abp/DynamicProxy/AbpInterceptionTestBase.cs index 6e1fab17ae..9d94a9e27a 100644 --- a/test/Volo.Abp.Tests/Volo/Abp/DynamicProxy/AbpInterceptionTestBase.cs +++ b/test/Volo.Abp.Tests/Volo/Abp/DynamicProxy/AbpInterceptionTestBase.cs @@ -17,18 +17,25 @@ namespace Volo.Abp.DynamicProxy services.AddTransient(); services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.OnServiceRegistred(registration => { - //TODO: Create an attribute to add interceptors! if (typeof(SimpleInterceptionTargetClass) == registration.ImplementationType) { registration.Interceptors.Add(); registration.Interceptors.Add(); registration.Interceptors.Add(); } + + if (typeof(CachedTestObject) == registration.ImplementationType) + { + registration.Interceptors.Add(); + } }); } - + [Fact] public async Task Should_Intercept_Async_Method_Without_Return_Value() { @@ -125,5 +132,33 @@ namespace Volo.Abp.DynamicProxy target.Logs[5].ShouldBe("SimpleSyncInterceptor_Intercept_AfterInvocation"); target.Logs[6].ShouldBe("SimpleAsyncInterceptor_Intercept_AfterInvocation"); } + + [Fact] + public void Should_Cache_Results() + { + //Arrange + + var target = ServiceProvider.GetService(); + + //Act & Assert + + target.GetValue(42).ShouldBe(42); //First run, not cached yet + target.GetValue(43).ShouldBe(42); //First run, cached previous value + target.GetValue(44).ShouldBe(42); //First run, cached previous value + } + + [Fact] + public async Task Should_Cache_Results_Async() + { + //Arrange + + var target = ServiceProvider.GetService(); + + //Act & Assert + + (await target.GetValueAsync(42)).ShouldBe(42); //First run, not cached yet + (await target.GetValueAsync(43)).ShouldBe(42); //First run, cached previous value + (await target.GetValueAsync(44)).ShouldBe(42); //First run, cached previous value + } } } \ No newline at end of file diff --git a/test/Volo.Abp.Tests/Volo/Abp/DynamicProxy/CachedTestObject.cs b/test/Volo.Abp.Tests/Volo/Abp/DynamicProxy/CachedTestObject.cs new file mode 100644 index 0000000000..432d91bbb7 --- /dev/null +++ b/test/Volo.Abp.Tests/Volo/Abp/DynamicProxy/CachedTestObject.cs @@ -0,0 +1,18 @@ +using System.Threading.Tasks; + +namespace Volo.Abp.DynamicProxy +{ + public class CachedTestObject + { + public virtual int GetValue(int v) + { + return v; + } + + public virtual async Task GetValueAsync(int v) + { + await Task.Delay(5); + return v; + } + } +} \ No newline at end of file diff --git a/test/Volo.Abp.Tests/Volo/Abp/DynamicProxy/SimpleResultCacheTestInterceptor.cs b/test/Volo.Abp.Tests/Volo/Abp/DynamicProxy/SimpleResultCacheTestInterceptor.cs new file mode 100644 index 0000000000..1712d316a8 --- /dev/null +++ b/test/Volo.Abp.Tests/Volo/Abp/DynamicProxy/SimpleResultCacheTestInterceptor.cs @@ -0,0 +1,37 @@ +using System.Collections.Concurrent; +using System.Reflection; +using System.Threading.Tasks; + +namespace Volo.Abp.DynamicProxy +{ + public class SimpleResultCacheTestInterceptor : AbpInterceptor + { + private readonly ConcurrentDictionary _cache; + + public SimpleResultCacheTestInterceptor() + { + _cache = new ConcurrentDictionary(); + } + + public override void Intercept(IAbpMethodInvocation invocation) + { + invocation.ReturnValue = _cache.GetOrAdd(invocation.Method, m => + { + invocation.Proceed(); + return invocation.ReturnValue; + }); + } + + public override async Task InterceptAsync(IAbpMethodInvocation invocation) + { + if (_cache.ContainsKey(invocation.Method)) + { + invocation.ReturnValue = _cache[invocation.Method]; + return; + } + + await invocation.ProceedAsync(); + _cache[invocation.Method] = invocation.ReturnValue; + } + } +}