Browse Source

Resolved #12918: Introduce ITransientCachedServiceProvider.

pull/12921/head
Halil İbrahim Kalkan 4 years ago
parent
commit
db4d40a374
  1. 26
      framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/CachedServiceProvider.cs
  2. 25
      framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/CachedServiceProviderBase.cs
  3. 2
      framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ICachedServiceProvider.cs
  4. 14
      framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ITransientCachedServiceProvider.cs
  5. 15
      framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/TransientCachedServiceProvider.cs
  6. 102
      framework/test/Volo.Abp.Core.Tests/Volo/Abp/DependencyInjection/CachedServiceProvider_Tests.cs

26
framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/CachedServiceProvider.cs

@ -4,27 +4,13 @@ using System.Collections.Generic;
namespace Volo.Abp.DependencyInjection;
[ExposeServices(typeof(ICachedServiceProvider))]
public class CachedServiceProvider : ICachedServiceProvider, IScopedDependency
public class CachedServiceProvider :
CachedServiceProviderBase,
ICachedServiceProvider,
IScopedDependency
{
protected IServiceProvider ServiceProvider { get; }
protected IDictionary<Type, object> CachedServices { get; }
public CachedServiceProvider(IServiceProvider serviceProvider)
: base(serviceProvider)
{
ServiceProvider = serviceProvider;
CachedServices = new Dictionary<Type, object>
{
{typeof(IServiceProvider), serviceProvider}
};
}
public object GetService(Type serviceType)
{
return CachedServices.GetOrAdd(
serviceType,
() => ServiceProvider.GetService(serviceType)
);
}
}
}

25
framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/CachedServiceProviderBase.cs

@ -0,0 +1,25 @@
using System;
using System.Collections.Concurrent;
namespace Volo.Abp.DependencyInjection;
public abstract class CachedServiceProviderBase
{
private readonly IServiceProvider _serviceProvider;
private readonly ConcurrentDictionary<Type, Lazy<object>> _cachedServices;
protected CachedServiceProviderBase(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
_cachedServices = new ConcurrentDictionary<Type, Lazy<object>>();
_cachedServices.TryAdd(typeof(IServiceProvider), new Lazy<object>(() => this));
}
public object GetService(Type serviceType)
{
return _cachedServices.GetOrAdd(
serviceType,
_ => new Lazy<object>(() => _serviceProvider.GetService(serviceType))
).Value;
}
}

2
framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ICachedServiceProvider.cs

@ -11,4 +11,4 @@ namespace Volo.Abp.DependencyInjection;
public interface ICachedServiceProvider : IServiceProvider
{
}
}

14
framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ITransientCachedServiceProvider.cs

@ -0,0 +1,14 @@
using System;
namespace Volo.Abp.DependencyInjection;
/// <summary>
/// Provides services by caching the resolved services.
/// It caches all type of services including transients.
/// This service's lifetime is transient.
/// <see cref="ICachedServiceProvider"/> for the one with scoped lifetime.
/// </summary>
public interface ITransientCachedServiceProvider : IServiceProvider
{
}

15
framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/TransientCachedServiceProvider.cs

@ -0,0 +1,15 @@
using System;
namespace Volo.Abp.DependencyInjection;
[ExposeServices(typeof(ITransientCachedServiceProvider))]
public class TransientCachedServiceProvider :
CachedServiceProviderBase,
ITransientCachedServiceProvider,
ITransientDependency
{
public TransientCachedServiceProvider(IServiceProvider serviceProvider)
: base(serviceProvider)
{
}
}

102
framework/test/Volo.Abp.Core.Tests/Volo/Abp/DependencyInjection/CachedServiceProvider_Tests.cs

@ -0,0 +1,102 @@
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using Volo.Abp.Modularity;
using Volo.Abp.Testing.Utils;
using Xunit;
namespace Volo.Abp.DependencyInjection;
public class CachedServiceProvider_Tests
{
[Fact]
public void CachedServiceProvider_Should_Cache_Services()
{
void TestResolvingServices(IServiceScope scope)
{
var cachedServiceProvider1 = scope.ServiceProvider.GetRequiredService<ICachedServiceProvider>();
var cachedServiceProvider2 = scope.ServiceProvider.GetRequiredService<ICachedServiceProvider>();
cachedServiceProvider1.ShouldBeSameAs(cachedServiceProvider2);
var transientTestService1 = cachedServiceProvider1.GetRequiredService<TransientTestService>();
var transientTestService2 = cachedServiceProvider2.GetRequiredService<TransientTestService>();
transientTestService1.ShouldBeSameAs(transientTestService2);
}
using (var application = AbpApplicationFactory.Create<TestModule>())
{
application.Initialize();
using (var scope1 = application.ServiceProvider.CreateScope())
{
TestResolvingServices(scope1);
var testCounter = scope1.ServiceProvider.GetRequiredService<ITestCounter>();
testCounter.GetValue(nameof(TransientTestService)).ShouldBe(1);
}
using (var scope2 = application.ServiceProvider.CreateScope())
{
TestResolvingServices(scope2);
var testCounter = scope2.ServiceProvider.GetRequiredService<ITestCounter>();
//Resolved in a different scope, so should not cache the service!
testCounter.GetValue(nameof(TransientTestService)).ShouldBe(2);
}
}
}
[Fact]
public void TransientCachedServiceProvider_Should_Cache_Services()
{
void TestResolvingServices(IServiceScope scope)
{
var cachedServiceProvider1 = scope.ServiceProvider.GetRequiredService<ITransientCachedServiceProvider>();
var transientTestService1_1 = cachedServiceProvider1.GetRequiredService<TransientTestService>();
var transientTestService1_2 = cachedServiceProvider1.GetRequiredService<TransientTestService>();
transientTestService1_1.ShouldBeSameAs(transientTestService1_2);
var cachedServiceProvider2 = scope.ServiceProvider.GetRequiredService<ITransientCachedServiceProvider>();
cachedServiceProvider1.ShouldNotBeSameAs(cachedServiceProvider2);
var transientTestService2_1 = cachedServiceProvider2.GetRequiredService<TransientTestService>();
var transientTestService2_2 = cachedServiceProvider2.GetRequiredService<TransientTestService>();
transientTestService2_1.ShouldBeSameAs(transientTestService2_2);
transientTestService1_1.ShouldNotBeSameAs(transientTestService2_1);
}
using (var application = AbpApplicationFactory.Create<TestModule>())
{
application.Initialize();
using (var scope1 = application.ServiceProvider.CreateScope())
{
TestResolvingServices(scope1);
var testCounter = scope1.ServiceProvider.GetRequiredService<ITestCounter>();
testCounter.GetValue(nameof(TransientTestService)).ShouldBe(2);
}
}
}
[DependsOn(typeof(AbpTestBaseModule))]
private class TestModule : AbpModule
{
public TestModule()
{
SkipAutoServiceRegistration = true;
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddType<TransientTestService>();
}
}
private class TransientTestService : ITransientDependency
{
public TransientTestService(ITestCounter counter)
{
counter.Increment(nameof(TransientTestService));
}
}
}
Loading…
Cancel
Save