Browse Source

Merge pull request #14879 from abpframework/doc-cached-service-providers

Revise and Document cached/lazy service providers
pull/14880/head
maliming 4 years ago
committed by GitHub
parent
commit
6a870ca713
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 35
      docs/en/Dependency-Injection.md
  2. 25
      framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/AbpLazyServiceProvider.cs
  3. 1
      framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/CachedServiceProvider.cs
  4. 25
      framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/CachedServiceProviderBase.cs
  5. 40
      framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/IAbpLazyServiceProvider.cs
  6. 6
      framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ICachedServiceProvider.cs
  7. 14
      framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ICachedServiceProviderBase.cs
  8. 4
      framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ITransientCachedServiceProvider.cs

35
docs/en/Dependency-Injection.md

@ -403,6 +403,41 @@ using (var scope = _serviceProvider.CreateScope())
Both services are released when the created scope is disposed (at the end of the `using` block).
### Cached Service Providers
ABP provides two special services to optimize resolving services from `IServiceProvider`. `ICachedServiceProvider` and `ITransientCachedServiceProvider` both inherits from the `IServiceProvider` interface and internally caches the resolved services, so you get the same service instance even if you resolve a service multiple times.
The main difference is the `ICachedServiceProvider` is itself registered as scoped, while the `ITransientCachedServiceProvider` is registered as transient to the dependency injection system.
The following example injects the `ICachedServiceProvider` service and resolves a service in the `DoSomethingAsync` method:
````csharp
public class MyService : ITransientDependency
{
private readonly ICachedServiceProvider _serviceProvider;
public MyService(ICachedServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task DoSomethingAsync()
{
var taxCalculator = _serviceProvider.GetRequiredService<ITaxCalculator>();
// TODO: Use the taxCalculator
}
}
````
With such a usage, you don't need to deal with creating service scopes and disposing the resolved services (as explained in the *Releasing/Disposing Services* section above). Because all the services resolved from the `ICachedServiceProvider` will be released once the service scope of the `MyService` instance is disposed. Also, you don't need to care about memory leaks (because of creating too many `ITaxCalculator` instances if we call `DoSomethingAsync` too many times), because only one `ITaxCalculator` instance is created, and it is reused.
Since `ICachedServiceProvider` and `ITransientCachedServiceProvider` extends the standard `IServiceProvider` interface, you can use all the extension method of the `IServiceProvider` interface on them. In addition, they provides some other methods to provide a default value or a factory method for the services that are not found (that means not registered to the dependency injection system). Notice that the default value (or the value returned from your factory method) is also cached and reused.
Use `ICachedServiceProvider` (instead of `ITransientCachedServiceProvider`) unless you need to create the service cache per usage. `ITransientCachedServiceProvider` guarantees that the created service instances are not shared with any other service, even they are in the same service scope. The services resolved from `ICachedServiceProvider` are shared with other services in the same service scope (in the same HTTP Request, for example), so it can be thought as more optimized.
> ABP Framework also provides the `IAbpLazyServiceProvider` service. It does exists for backward compatibility and works exactly same with the `ITransientCachedServiceProvider` service. So, use the `ITransientCachedServiceProvider` since the `IAbpLazyServiceProvider` might be removed in future ABP versions.
## Advanced Features
### IServiceCollection.OnRegistred Event

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

@ -1,15 +1,23 @@
using System;
using Microsoft.Extensions.DependencyInjection;
namespace Volo.Abp.DependencyInjection;
/// <summary>
/// This class is equivalent of the <see cref="TransientCachedServiceProvider"/>.
/// Use <see cref="TransientCachedServiceProvider"/> instead of this class, for new projects.
/// </summary>
[ExposeServices(typeof(IAbpLazyServiceProvider))]
public class AbpLazyServiceProvider : CachedServiceProviderBase, IAbpLazyServiceProvider, ITransientDependency
public class AbpLazyServiceProvider :
CachedServiceProviderBase,
IAbpLazyServiceProvider,
ITransientDependency
{
public AbpLazyServiceProvider(IServiceProvider serviceProvider)
: base(serviceProvider)
{
}
public virtual T LazyGetRequiredService<T>()
{
return (T)LazyGetRequiredService(typeof(T));
@ -17,7 +25,7 @@ public class AbpLazyServiceProvider : CachedServiceProviderBase, IAbpLazyService
public virtual object LazyGetRequiredService(Type serviceType)
{
return GetService(serviceType);
return this.GetRequiredService(serviceType);
}
public virtual T LazyGetService<T>()
@ -32,24 +40,21 @@ public class AbpLazyServiceProvider : CachedServiceProviderBase, IAbpLazyService
public virtual T LazyGetService<T>(T defaultValue)
{
return (T)LazyGetService(typeof(T), defaultValue);
return GetService(defaultValue);
}
public virtual object LazyGetService(Type serviceType, object defaultValue)
{
return LazyGetService(serviceType) ?? defaultValue;
return GetService(serviceType, defaultValue);
}
public virtual T LazyGetService<T>(Func<IServiceProvider, object> factory)
{
return (T)LazyGetService(typeof(T), factory);
return GetService<T>(factory);
}
public virtual object LazyGetService(Type serviceType, Func<IServiceProvider, object> factory)
{
return CachedServices.GetOrAdd(
serviceType,
_ => new Lazy<object>(() => factory(ServiceProvider))
).Value;
return GetService(serviceType, factory);
}
}

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

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
namespace Volo.Abp.DependencyInjection;

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

@ -3,7 +3,7 @@ using System.Collections.Concurrent;
namespace Volo.Abp.DependencyInjection;
public abstract class CachedServiceProviderBase
public abstract class CachedServiceProviderBase : ICachedServiceProviderBase
{
protected IServiceProvider ServiceProvider { get; }
protected ConcurrentDictionary<Type, Lazy<object>> CachedServices { get; }
@ -22,4 +22,27 @@ public abstract class CachedServiceProviderBase
_ => new Lazy<object>(() => ServiceProvider.GetService(serviceType))
).Value;
}
public T GetService<T>(T defaultValue)
{
return (T)GetService(typeof(T), defaultValue);
}
public object GetService(Type serviceType, object defaultValue)
{
return GetService(serviceType) ?? defaultValue;
}
public T GetService<T>(Func<IServiceProvider, object> factory)
{
return (T)GetService(typeof(T), factory);
}
public object GetService(Type serviceType, Func<IServiceProvider, object> factory)
{
return CachedServices.GetOrAdd(
serviceType,
_ => new Lazy<object>(() => factory(ServiceProvider))
).Value;
}
}

40
framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/IAbpLazyServiceProvider.cs

@ -2,21 +2,57 @@
namespace Volo.Abp.DependencyInjection;
public interface IAbpLazyServiceProvider
/// <summary>
/// This service is equivalent of the <see cref="ITransientCachedServiceProvider"/>.
/// Use <see cref="ITransientCachedServiceProvider"/> instead of this interface, for new projects.
/// </summary>
public interface IAbpLazyServiceProvider : ICachedServiceProviderBase
{
/// <summary>
/// This method is equivalent of the GetRequiredService method.
/// It does exists for backward compatibility.
/// </summary>
T LazyGetRequiredService<T>();
/// <summary>
/// This method is equivalent of the GetRequiredService method.
/// It does exists for backward compatibility.
/// </summary>
object LazyGetRequiredService(Type serviceType);
/// <summary>
/// This method is equivalent of the GetService method.
/// It does exists for backward compatibility.
/// </summary>
T LazyGetService<T>();
/// <summary>
/// This method is equivalent of the GetService method.
/// It does exists for backward compatibility.
/// </summary>
object LazyGetService(Type serviceType);
/// <summary>
/// This method is equivalent of the <see cref="ICachedServiceProviderBase.GetService{T}(T)"/> method.
/// It does exists for backward compatibility.
/// </summary>
T LazyGetService<T>(T defaultValue);
/// <summary>
/// This method is equivalent of the <see cref="ICachedServiceProviderBase.GetService(Type, object)"/> method.
/// It does exists for backward compatibility.
/// </summary>
object LazyGetService(Type serviceType, object defaultValue);
/// <summary>
/// This method is equivalent of the <see cref="ICachedServiceProviderBase.GetService(Type, Func{IServiceProvider, object})"/> method.
/// It does exists for backward compatibility.
/// </summary>
object LazyGetService(Type serviceType, Func<IServiceProvider, object> factory);
/// <summary>
/// This method is equivalent of the <see cref="ICachedServiceProviderBase.GetService{T}(Func{IServiceProvider, object})"/> method.
/// It does exists for backward compatibility.
/// </summary>
T LazyGetService<T>(Func<IServiceProvider, object> factory);
}

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

@ -1,6 +1,4 @@
using System;
namespace Volo.Abp.DependencyInjection;
namespace Volo.Abp.DependencyInjection;
/// <summary>
/// Provides services by caching the resolved services.
@ -8,7 +6,7 @@ namespace Volo.Abp.DependencyInjection;
/// This service's lifetime is scoped and it should be used
/// for a limited scope.
/// </summary>
public interface ICachedServiceProvider : IServiceProvider
public interface ICachedServiceProvider : ICachedServiceProviderBase
{
}

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

@ -0,0 +1,14 @@
using System;
namespace Volo.Abp.DependencyInjection;
public interface ICachedServiceProviderBase : IServiceProvider
{
T GetService<T>(T defaultValue);
object GetService(Type serviceType, object defaultValue);
T GetService<T>(Func<IServiceProvider, object> factory);
object GetService(Type serviceType, Func<IServiceProvider, object> factory);
}

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

@ -1,5 +1,3 @@
using System;
namespace Volo.Abp.DependencyInjection;
/// <summary>
@ -8,7 +6,7 @@ namespace Volo.Abp.DependencyInjection;
/// This service's lifetime is transient.
/// <see cref="ICachedServiceProvider"/> for the one with scoped lifetime.
/// </summary>
public interface ITransientCachedServiceProvider : IServiceProvider
public interface ITransientCachedServiceProvider : ICachedServiceProviderBase
{
}
Loading…
Cancel
Save