Merge pull request #13688 from abpframework/LazyServiceProvider-

Fix concurrency problem of `IAbpLazyServiceProvider`.
pull/13692/head
liangshiwei 3 years ago committed by GitHub
commit 4636d6e05c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,19 +1,13 @@
using System; using System;
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
namespace Volo.Abp.DependencyInjection; namespace Volo.Abp.DependencyInjection;
public class AbpLazyServiceProvider : IAbpLazyServiceProvider, ITransientDependency [ExposeServices(typeof(IAbpLazyServiceProvider))]
public class AbpLazyServiceProvider : CachedServiceProviderBase, IAbpLazyServiceProvider, ITransientDependency
{ {
protected IDictionary<Type, object> CachedServices { get; set; }
protected IServiceProvider ServiceProvider { get; set; }
public AbpLazyServiceProvider(IServiceProvider serviceProvider) public AbpLazyServiceProvider(IServiceProvider serviceProvider)
: base(serviceProvider)
{ {
ServiceProvider = serviceProvider;
CachedServices = new Dictionary<Type, object>();
} }
public virtual T LazyGetRequiredService<T>() public virtual T LazyGetRequiredService<T>()
@ -23,7 +17,7 @@ public class AbpLazyServiceProvider : IAbpLazyServiceProvider, ITransientDepende
public virtual object LazyGetRequiredService(Type serviceType) public virtual object LazyGetRequiredService(Type serviceType)
{ {
return CachedServices.GetOrAdd(serviceType, () => ServiceProvider.GetRequiredService(serviceType)); return GetService(serviceType);
} }
public virtual T LazyGetService<T>() public virtual T LazyGetService<T>()
@ -33,7 +27,7 @@ public class AbpLazyServiceProvider : IAbpLazyServiceProvider, ITransientDepende
public virtual object LazyGetService(Type serviceType) public virtual object LazyGetService(Type serviceType)
{ {
return CachedServices.GetOrAdd(serviceType, () => ServiceProvider.GetService(serviceType)); return GetService(serviceType);
} }
public virtual T LazyGetService<T>(T defaultValue) public virtual T LazyGetService<T>(T defaultValue)
@ -53,6 +47,9 @@ public class AbpLazyServiceProvider : IAbpLazyServiceProvider, ITransientDepende
public virtual object LazyGetService(Type serviceType, Func<IServiceProvider, object> factory) public virtual object LazyGetService(Type serviceType, Func<IServiceProvider, object> factory)
{ {
return CachedServices.GetOrAdd(serviceType, () => factory(ServiceProvider)); return CachedServices.GetOrAdd(
serviceType,
_ => new Lazy<object>(() => factory(ServiceProvider))
).Value;
} }
} }

@ -5,21 +5,21 @@ namespace Volo.Abp.DependencyInjection;
public abstract class CachedServiceProviderBase public abstract class CachedServiceProviderBase
{ {
private readonly IServiceProvider _serviceProvider; protected IServiceProvider ServiceProvider { get; }
private readonly ConcurrentDictionary<Type, Lazy<object>> _cachedServices; protected ConcurrentDictionary<Type, Lazy<object>> CachedServices { get; }
protected CachedServiceProviderBase(IServiceProvider serviceProvider) protected CachedServiceProviderBase(IServiceProvider serviceProvider)
{ {
_serviceProvider = serviceProvider; ServiceProvider = serviceProvider;
_cachedServices = new ConcurrentDictionary<Type, Lazy<object>>(); CachedServices = new ConcurrentDictionary<Type, Lazy<object>>();
_cachedServices.TryAdd(typeof(IServiceProvider), new Lazy<object>(() => _serviceProvider)); CachedServices.TryAdd(typeof(IServiceProvider), new Lazy<object>(() => ServiceProvider));
} }
public virtual object GetService(Type serviceType) public virtual object GetService(Type serviceType)
{ {
return _cachedServices.GetOrAdd( return CachedServices.GetOrAdd(
serviceType, serviceType,
_ => new Lazy<object>(() => _serviceProvider.GetService(serviceType)) _ => new Lazy<object>(() => ServiceProvider.GetService(serviceType))
).Value; ).Value;
} }
} }

@ -0,0 +1,50 @@
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using Volo.Abp.Modularity;
using Volo.Abp.Testing.Utils;
using Xunit;
namespace Volo.Abp.DependencyInjection;
public class AbpLazyServiceProvider_Tests
{
[Fact]
public void LazyServiceProvider_Should_Cache_Services()
{
using (var application = AbpApplicationFactory.Create<TestModule>())
{
application.Initialize();
var lazyServiceProvider = application.ServiceProvider.GetRequiredService<IAbpLazyServiceProvider>();
var transientTestService1 = lazyServiceProvider.LazyGetRequiredService<TransientTestService>();
var transientTestService2 = lazyServiceProvider.LazyGetRequiredService<TransientTestService>();
transientTestService1.ShouldBeSameAs(transientTestService2);
var testCounter = application.ServiceProvider.GetRequiredService<ITestCounter>();
testCounter.GetValue(nameof(TransientTestService)).ShouldBe(1);
}
}
[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