From e796d03c0ee37e9fb4d1b58070d535a561551252 Mon Sep 17 00:00:00 2001 From: Merdan Gochmuradov Date: Sat, 27 Aug 2022 10:02:00 +0500 Subject: [PATCH 1/4] Added DisablePropertyInjection Attribute with functionality --- .../AbpRegistrationBuilderExtensions.cs | 5 ++- .../DisablePropertyInjectionAttribute.cs | 8 ++++ .../DependencyInjection_Tests.cs | 39 ++++++++++++++++++- 3 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/DisablePropertyInjectionAttribute.cs diff --git a/framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs b/framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs index 868ecb3fa5..b3c5d82c92 100644 --- a/framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs +++ b/framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs @@ -67,8 +67,9 @@ 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 + if (moduleContainer.Modules.Any((IAbpModuleDescriptor m) => m.Assembly == implementationType.Assembly) + && !implementationType.GetCustomAttributes(typeof(DisablePropertyInjectionAttribute), true).Any()) { registrationBuilder = registrationBuilder.PropertiesAutowired(); } 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..0ce3148308 --- /dev/null +++ b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/DisablePropertyInjectionAttribute.cs @@ -0,0 +1,8 @@ +using System; + +namespace Volo.Abp.DependencyInjection; + +[AttributeUsage(AttributeTargets.Class)] +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..808af0aa87 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 @@ -62,6 +62,24 @@ public abstract class DependencyInjection_Standard_Tests : AbpIntegratedTest().ProperyInjectedService.ShouldNotBeNull(); } + [Fact] + public void Should_Not_Inject_Services_As_Properties_When_Disabled() + { + GetRequiredService().ProperyInjectedService.ShouldBeNull(); + } + + [Fact] + public void Should_Not_Inject_Services_As_Properties_For_Generic_Classes_When_Disabled() + { + GetRequiredService>().ProperyInjectedService.ShouldBeNull(); + } + + [Fact] + public void Should_Not_Inject_Services_As_Properties_For_Generic_Concrete_Classes_When_Disabled() + { + GetRequiredService().ProperyInjectedService.ShouldBeNull(); + } + [Fact] public void Singletons_Exposing_Multiple_Services_Should_Returns_The_Same_Instance() { @@ -145,7 +163,7 @@ public abstract class DependencyInjection_Standard_Tests : AbpIntegratedTest(); context.Services.AddType(); context.Services.AddTransient(typeof(GenericServiceWithPropertyInject<>)); - context.Services.AddTransient(typeof(ConcreteGenericServiceWithPropertyInject)); + context.Services.AddTransient(typeof(GenericServiceWithPropertyInjectDisabled<>)); } } @@ -165,4 +183,23 @@ public abstract class DependencyInjection_Standard_Tests : AbpIntegratedTest : ITransientDependency + { + public MyEmptyTransientService ProperyInjectedService { get; set; } + + public T Value { get; set; } + } + + public class ConcreteGenericServiceWithPropertyInjectDisabled : GenericServiceWithPropertyInjectDisabled + { + + } } From 682c7ab65acae5dda1e2c8cfd1016a33deb9cfa7 Mon Sep 17 00:00:00 2001 From: maliming Date: Sun, 28 Aug 2022 14:54:11 +0800 Subject: [PATCH 2/4] Support disable property inject on properties. --- .../AbpRegistrationBuilderExtensions.cs | 9 ++-- .../DisablePropertyInjectionAttribute.cs | 3 +- .../DependencyInjection_Tests.cs | 53 ++++++++++++------- 3 files changed, 40 insertions(+), 25 deletions(-) diff --git a/framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs b/framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs index b3c5d82c92..2031cb5558 100644 --- a/framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs +++ b/framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs @@ -67,11 +67,12 @@ public static class AbpRegistrationBuilderExtensions Type implementationType) where TActivatorData : ReflectionActivatorData { - //Enable Property Injection only for types in an assembly containing an AbpModule and without a DisablePropertyInjection attribute - if (moduleContainer.Modules.Any((IAbpModuleDescriptor m) => m.Assembly == implementationType.Assembly) - && !implementationType.GetCustomAttributes(typeof(DisablePropertyInjectionAttribute), true).Any()) + // 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((propertyInfo, _) => + propertyInfo.GetCustomAttributes(typeof(DisablePropertyInjectionAttribute), true).IsNullOrEmpty()); } return registrationBuilder; diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/DisablePropertyInjectionAttribute.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/DisablePropertyInjectionAttribute.cs index 0ce3148308..fad0d7334a 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/DisablePropertyInjectionAttribute.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/DisablePropertyInjectionAttribute.cs @@ -2,7 +2,8 @@ namespace Volo.Abp.DependencyInjection; -[AttributeUsage(AttributeTargets.Class)] +[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 808af0aa87..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,37 +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_Disabled() + public void Should_Not_Inject_Services_As_Properties_When_Class_With_DisablePropertyInjection() { - GetRequiredService().ProperyInjectedService.ShouldBeNull(); + GetRequiredService().PropertyInjectedService.ShouldBeNull(); + GetRequiredService>().PropertyInjectedService.ShouldBeNull(); } [Fact] - public void Should_Not_Inject_Services_As_Properties_For_Generic_Classes_When_Disabled() + public void Should_Not_Inject_Services_As_Properties_When_Property_With_DisablePropertyInjection() { - GetRequiredService>().ProperyInjectedService.ShouldBeNull(); - } + GetRequiredService().PropertyInjectedService.ShouldNotBeNull(); + GetRequiredService().DisablePropertyInjectionService.ShouldBeNull(); - [Fact] - public void Should_Not_Inject_Services_As_Properties_For_Generic_Concrete_Classes_When_Disabled() - { - GetRequiredService().ProperyInjectedService.ShouldBeNull(); + GetRequiredService>().PropertyInjectedService.ShouldNotBeNull(); + GetRequiredService>().DisablePropertyInjectionService.ShouldBeNull(); } [Fact] @@ -163,18 +162,19 @@ public abstract class DependencyInjection_Standard_Tests : AbpIntegratedTest(); context.Services.AddType(); context.Services.AddTransient(typeof(GenericServiceWithPropertyInject<>)); - context.Services.AddTransient(typeof(GenericServiceWithPropertyInjectDisabled<>)); + 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; } } @@ -185,21 +185,34 @@ public abstract class DependencyInjection_Standard_Tests : AbpIntegratedTest : ITransientDependency + public class GenericServiceWithDisablePropertyInjectionOnClass : ITransientDependency { - public MyEmptyTransientService ProperyInjectedService { get; set; } + public MyEmptyTransientService PropertyInjectedService { get; set; } public T Value { get; set; } } - public class ConcreteGenericServiceWithPropertyInjectDisabled : GenericServiceWithPropertyInjectDisabled + public class GenericServiceWithDisablePropertyInjectionOnProperty : ITransientDependency { + public MyEmptyTransientService PropertyInjectedService { get; set; } + [DisablePropertyInjection] + public MyEmptyTransientService DisablePropertyInjectionService { get; set; } + + public T Value { get; set; } } } From 980f78acd2409feb918c9f2418865544385c1972 Mon Sep 17 00:00:00 2001 From: maliming Date: Sun, 28 Aug 2022 15:03:25 +0800 Subject: [PATCH 3/4] Update Dependency-Injection.md --- docs/en/Dependency-Injection.md | 34 +++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) 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; } } ```` From 800f91db6713718d2e9fa42c00aa9222e644a780 Mon Sep 17 00:00:00 2001 From: maliming Date: Sun, 28 Aug 2022 15:47:57 +0800 Subject: [PATCH 4/4] Add `AbpPropertySelector`. --- .../AbpRegistrationBuilderExtensions.cs | 4 ++-- .../Volo/Abp/Autofac/AbpPropertySelector.cs | 21 +++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 framework/src/Volo.Abp.Autofac/Volo/Abp/Autofac/AbpPropertySelector.cs diff --git a/framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs b/framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs index 2031cb5558..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; @@ -71,8 +72,7 @@ public static class AbpRegistrationBuilderExtensions if (moduleContainer.Modules.Any(m => m.Assembly == implementationType.Assembly) && implementationType.GetCustomAttributes(typeof(DisablePropertyInjectionAttribute), true).IsNullOrEmpty()) { - registrationBuilder = registrationBuilder.PropertiesAutowired((propertyInfo, _) => - propertyInfo.GetCustomAttributes(typeof(DisablePropertyInjectionAttribute), true).IsNullOrEmpty()); + 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); + } + +}