diff --git a/docs/en/Dependency-Injection.md b/docs/en/Dependency-Injection.md index 147ef1689a..47e60700bc 100644 --- a/docs/en/Dependency-Injection.md +++ b/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(); + // 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 diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/AbpLazyServiceProvider.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/AbpLazyServiceProvider.cs index 3aff8a78ad..a180592a01 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/AbpLazyServiceProvider.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/AbpLazyServiceProvider.cs @@ -1,15 +1,23 @@ using System; +using Microsoft.Extensions.DependencyInjection; namespace Volo.Abp.DependencyInjection; +/// +/// This class is equivalent of the . +/// Use instead of this class, for new projects. +/// [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() { 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() @@ -32,24 +40,21 @@ public class AbpLazyServiceProvider : CachedServiceProviderBase, IAbpLazyService public virtual T LazyGetService(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(Func factory) { - return (T)LazyGetService(typeof(T), factory); + return GetService(factory); } public virtual object LazyGetService(Type serviceType, Func factory) { - return CachedServices.GetOrAdd( - serviceType, - _ => new Lazy(() => factory(ServiceProvider)) - ).Value; + return GetService(serviceType, factory); } } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/CachedServiceProvider.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/CachedServiceProvider.cs index 870e1f8faf..ef0fed3541 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/CachedServiceProvider.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/CachedServiceProvider.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; namespace Volo.Abp.DependencyInjection; diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/CachedServiceProviderBase.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/CachedServiceProviderBase.cs index c261d3a3bb..21f2b1c516 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/CachedServiceProviderBase.cs +++ b/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> CachedServices { get; } @@ -22,4 +22,27 @@ public abstract class CachedServiceProviderBase _ => new Lazy(() => ServiceProvider.GetService(serviceType)) ).Value; } + + public T GetService(T defaultValue) + { + return (T)GetService(typeof(T), defaultValue); + } + + public object GetService(Type serviceType, object defaultValue) + { + return GetService(serviceType) ?? defaultValue; + } + + public T GetService(Func factory) + { + return (T)GetService(typeof(T), factory); + } + + public object GetService(Type serviceType, Func factory) + { + return CachedServices.GetOrAdd( + serviceType, + _ => new Lazy(() => factory(ServiceProvider)) + ).Value; + } } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/IAbpLazyServiceProvider.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/IAbpLazyServiceProvider.cs index 610bb40dfa..cd5f1da67f 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/IAbpLazyServiceProvider.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/IAbpLazyServiceProvider.cs @@ -2,21 +2,57 @@ namespace Volo.Abp.DependencyInjection; -public interface IAbpLazyServiceProvider +/// +/// This service is equivalent of the . +/// Use instead of this interface, for new projects. +/// +public interface IAbpLazyServiceProvider : ICachedServiceProviderBase { + /// + /// This method is equivalent of the GetRequiredService method. + /// It does exists for backward compatibility. + /// T LazyGetRequiredService(); + /// + /// This method is equivalent of the GetRequiredService method. + /// It does exists for backward compatibility. + /// object LazyGetRequiredService(Type serviceType); + /// + /// This method is equivalent of the GetService method. + /// It does exists for backward compatibility. + /// T LazyGetService(); + /// + /// This method is equivalent of the GetService method. + /// It does exists for backward compatibility. + /// object LazyGetService(Type serviceType); + /// + /// This method is equivalent of the method. + /// It does exists for backward compatibility. + /// T LazyGetService(T defaultValue); - + + /// + /// This method is equivalent of the method. + /// It does exists for backward compatibility. + /// object LazyGetService(Type serviceType, object defaultValue); + /// + /// This method is equivalent of the method. + /// It does exists for backward compatibility. + /// object LazyGetService(Type serviceType, Func factory); + /// + /// This method is equivalent of the method. + /// It does exists for backward compatibility. + /// T LazyGetService(Func factory); } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ICachedServiceProvider.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ICachedServiceProvider.cs index 073daa135a..4a923901f9 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ICachedServiceProvider.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ICachedServiceProvider.cs @@ -1,6 +1,4 @@ -using System; - -namespace Volo.Abp.DependencyInjection; +namespace Volo.Abp.DependencyInjection; /// /// 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. /// -public interface ICachedServiceProvider : IServiceProvider +public interface ICachedServiceProvider : ICachedServiceProviderBase { } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ICachedServiceProviderBase.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ICachedServiceProviderBase.cs new file mode 100644 index 0000000000..2e15a45a46 --- /dev/null +++ b/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 defaultValue); + + object GetService(Type serviceType, object defaultValue); + + T GetService(Func factory); + + object GetService(Type serviceType, Func factory); +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ITransientCachedServiceProvider.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ITransientCachedServiceProvider.cs index 55c43e7c94..7b57b54e07 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ITransientCachedServiceProvider.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ITransientCachedServiceProvider.cs @@ -1,5 +1,3 @@ -using System; - namespace Volo.Abp.DependencyInjection; /// @@ -8,7 +6,7 @@ namespace Volo.Abp.DependencyInjection; /// This service's lifetime is transient. /// for the one with scoped lifetime. /// -public interface ITransientCachedServiceProvider : IServiceProvider +public interface ITransientCachedServiceProvider : ICachedServiceProviderBase { } \ No newline at end of file