diff --git a/docs/en/Dependency-Injection.md b/docs/en/Dependency-Injection.md index 668eac6644..a0ad342c4c 100644 --- a/docs/en/Dependency-Injection.md +++ b/docs/en/Dependency-Injection.md @@ -244,25 +244,35 @@ One restriction of property injection is that you cannot use the dependency in y Property injection is also useful when you want to design a base class that has some common services injected by default. If you're going to use constructor injection, all derived classes should also inject depended services into their own constructors which makes development harder. However, be very careful using property injection for non-optional services as it makes it harder to clearly see the requirements of a class. -### Resolve Service from IServiceProvider +#### DisablePropertyInjectionAttribute -You may want to resolve a service directly from ``IServiceProvider``. In that case, you can inject IServiceProvider into your class and use ``GetService`` method as shown below: +You can use `[DisablePropertyInjection]` attribute on class or properties to disable property injection for the whole class or some specific properties. ````C# +[DisablePropertyInjection] public class MyService : ITransientDependency { - private readonly IServiceProvider _serviceProvider; + public ITaxCalculator TaxCalculator { get; set; } +} - public MyService(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - } +public class MyService : ITransientDependency +{ + public ILogger Logger { get; set; } - public void DoSomething() - { - var taxCalculator = _serviceProvider.GetService(); - //... - } + [DisablePropertyInjection] + public ITaxCalculator TaxCalculator { get; set; } +} + +```` + +### Resolve Service from IServiceProvider + +You may want to resolve a service directly from ``IServiceProvider``. In that case, you can inject IServiceProvider into your class and use ``GetService`` method as shown below: + +````C# +public class MyService : ITransientDependency +{ + public ILogger Logger { get; set; } } ```` diff --git a/framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs b/framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs index 868ecb3fa5..03084d8351 100644 --- a/framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs +++ b/framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using Autofac.Core; using Autofac.Extras.DynamicProxy; +using Volo.Abp.Autofac; using Volo.Abp.Castle.DynamicProxy; using Volo.Abp.DependencyInjection; using Volo.Abp.Modularity; @@ -67,10 +68,11 @@ public static class AbpRegistrationBuilderExtensions Type implementationType) where TActivatorData : ReflectionActivatorData { - //Enable Property Injection only for types in an assembly containing an AbpModule - if (moduleContainer.Modules.Any(m => m.Assembly == implementationType.Assembly)) + // Enable Property Injection only for types in an assembly containing an AbpModule and without a DisablePropertyInjection attribute on class or properties. + if (moduleContainer.Modules.Any(m => m.Assembly == implementationType.Assembly) && + implementationType.GetCustomAttributes(typeof(DisablePropertyInjectionAttribute), true).IsNullOrEmpty()) { - registrationBuilder = registrationBuilder.PropertiesAutowired(); + registrationBuilder = registrationBuilder.PropertiesAutowired(new AbpPropertySelector(false)); } return registrationBuilder; diff --git a/framework/src/Volo.Abp.Autofac/Volo/Abp/Autofac/AbpPropertySelector.cs b/framework/src/Volo.Abp.Autofac/Volo/Abp/Autofac/AbpPropertySelector.cs new file mode 100644 index 0000000000..00d20e9bca --- /dev/null +++ b/framework/src/Volo.Abp.Autofac/Volo/Abp/Autofac/AbpPropertySelector.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using System.Reflection; +using Autofac.Core; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Autofac; + +public class AbpPropertySelector : DefaultPropertySelector +{ + public AbpPropertySelector(bool preserveSetValues) + : base(preserveSetValues) + { + } + + public override bool InjectProperty(PropertyInfo propertyInfo, object instance) + { + return propertyInfo.GetCustomAttributes(typeof(DisablePropertyInjectionAttribute), true).IsNullOrEmpty() && + base.InjectProperty(propertyInfo, instance); + } + +} diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/DisablePropertyInjectionAttribute.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/DisablePropertyInjectionAttribute.cs new file mode 100644 index 0000000000..fad0d7334a --- /dev/null +++ b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/DisablePropertyInjectionAttribute.cs @@ -0,0 +1,9 @@ +using System; + +namespace Volo.Abp.DependencyInjection; + +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property)] +public class DisablePropertyInjectionAttribute : Attribute +{ + +} diff --git a/framework/test/Volo.Abp.Core.Tests/Microsoft/Extensions/DependencyInjection/DependencyInjection_Tests.cs b/framework/test/Volo.Abp.Core.Tests/Microsoft/Extensions/DependencyInjection/DependencyInjection_Tests.cs index 5d77c6a00e..52f07620fb 100644 --- a/framework/test/Volo.Abp.Core.Tests/Microsoft/Extensions/DependencyInjection/DependencyInjection_Tests.cs +++ b/framework/test/Volo.Abp.Core.Tests/Microsoft/Extensions/DependencyInjection/DependencyInjection_Tests.cs @@ -47,19 +47,36 @@ public abstract class DependencyInjection_Standard_Tests : AbpIntegratedTest().ProperyInjectedService.ShouldNotBeNull(); + GetRequiredService().PropertyInjectedService.ShouldNotBeNull(); } [Fact] public void Should_Inject_Services_As_Properties_For_Generic_Classes() { - GetRequiredService>().ProperyInjectedService.ShouldNotBeNull(); + GetRequiredService>().PropertyInjectedService.ShouldNotBeNull(); } [Fact] public void Should_Inject_Services_As_Properties_For_Generic_Concrete_Classes() { - GetRequiredService().ProperyInjectedService.ShouldNotBeNull(); + GetRequiredService().PropertyInjectedService.ShouldNotBeNull(); + } + + [Fact] + public void Should_Not_Inject_Services_As_Properties_When_Class_With_DisablePropertyInjection() + { + GetRequiredService().PropertyInjectedService.ShouldBeNull(); + GetRequiredService>().PropertyInjectedService.ShouldBeNull(); + } + + [Fact] + public void Should_Not_Inject_Services_As_Properties_When_Property_With_DisablePropertyInjection() + { + GetRequiredService().PropertyInjectedService.ShouldNotBeNull(); + GetRequiredService().DisablePropertyInjectionService.ShouldBeNull(); + + GetRequiredService>().PropertyInjectedService.ShouldNotBeNull(); + GetRequiredService>().DisablePropertyInjectionService.ShouldBeNull(); } [Fact] @@ -145,18 +162,19 @@ public abstract class DependencyInjection_Standard_Tests : AbpIntegratedTest(); context.Services.AddType(); context.Services.AddTransient(typeof(GenericServiceWithPropertyInject<>)); - context.Services.AddTransient(typeof(ConcreteGenericServiceWithPropertyInject)); + context.Services.AddTransient(typeof(GenericServiceWithDisablePropertyInjectionOnClass<>)); + context.Services.AddTransient(typeof(GenericServiceWithDisablePropertyInjectionOnProperty<>)); } } public class ServiceWithPropertyInject : ITransientDependency { - public MyEmptyTransientService ProperyInjectedService { get; set; } + public MyEmptyTransientService PropertyInjectedService { get; set; } } public class GenericServiceWithPropertyInject : ITransientDependency { - public MyEmptyTransientService ProperyInjectedService { get; set; } + public MyEmptyTransientService PropertyInjectedService { get; set; } public T Value { get; set; } } @@ -165,4 +183,36 @@ public abstract class DependencyInjection_Standard_Tests : AbpIntegratedTest : ITransientDependency + { + public MyEmptyTransientService PropertyInjectedService { get; set; } + + public T Value { get; set; } + } + + public class GenericServiceWithDisablePropertyInjectionOnProperty : ITransientDependency + { + public MyEmptyTransientService PropertyInjectedService { get; set; } + + [DisablePropertyInjection] + public MyEmptyTransientService DisablePropertyInjectionService { get; set; } + + public T Value { get; set; } + } }