From 4926194b80153eef2ae4af9b557c459ee635bf11 Mon Sep 17 00:00:00 2001 From: maliming Date: Mon, 27 Nov 2023 09:33:55 +0800 Subject: [PATCH] Add `OnActivated` event for `ServiceDescriptor`. Resolves #18241 --- .../AbpRegistrationBuilderExtensions.cs | 12 +++++- .../AutofacRegistration.cs | 7 +++- ...erviceCollectionLifetimeEventExtensions.cs | 31 ++++++++++++++ .../IOnServiceActivatedContext.cs | 6 +++ .../OnServiceActivatedContext.cs | 11 +++++ .../ServiceActivatedActionList.cs | 14 +++++++ .../ServiceCollectionRepositoryExtensions.cs | 12 ++---- .../Abp/Autofac/AutoFac_OnActivated_Tests.cs | 39 ++++++++++++++++++ .../RepositoryRegistration_Tests.cs | 40 +++++++++---------- 9 files changed, 140 insertions(+), 32 deletions(-) create mode 100644 framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionLifetimeEventExtensions.cs create mode 100644 framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/IOnServiceActivatedContext.cs create mode 100644 framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/OnServiceActivatedContext.cs create mode 100644 framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ServiceActivatedActionList.cs create mode 100644 framework/test/Volo.Abp.Autofac.Tests/Volo/Abp/Autofac/AutoFac_OnActivated_Tests.cs diff --git a/framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs b/framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs index 4ee9ba5916..61ea371a0c 100644 --- a/framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs +++ b/framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs @@ -15,7 +15,8 @@ public static class AbpRegistrationBuilderExtensions public static IRegistrationBuilder ConfigureAbpConventions( this IRegistrationBuilder registrationBuilder, IModuleContainer moduleContainer, - ServiceRegistrationActionList registrationActionList) + ServiceRegistrationActionList registrationActionList, + List> serviceActivatedActions) where TActivatorData : ReflectionActivatorData { var serviceType = registrationBuilder.RegistrationData.Services.OfType().FirstOrDefault()?.ServiceType; @@ -24,6 +25,15 @@ public static class AbpRegistrationBuilderExtensions return registrationBuilder; } + registrationBuilder.OnActivated(context => + { + var serviceActivatedContext = new OnServiceActivatedContext(context.Instance!); + foreach (var action in serviceActivatedActions) + { + action.Invoke(serviceActivatedContext); + } + }); + var implementationType = registrationBuilder.ActivatorData.ImplementationType; if (implementationType == null) { diff --git a/framework/src/Volo.Abp.Autofac/Autofac/Extensions/DependencyInjection/AutofacRegistration.cs b/framework/src/Volo.Abp.Autofac/Autofac/Extensions/DependencyInjection/AutofacRegistration.cs index 56a5d7480c..afc528bb90 100644 --- a/framework/src/Volo.Abp.Autofac/Autofac/Extensions/DependencyInjection/AutofacRegistration.cs +++ b/framework/src/Volo.Abp.Autofac/Autofac/Extensions/DependencyInjection/AutofacRegistration.cs @@ -183,11 +183,14 @@ public static class AutofacRegistration { var moduleContainer = services.GetSingletonInstance(); var registrationActionList = services.GetRegistrationActionList(); + var activatedActionList = services.GetServiceActivatedActionList(); foreach (var descriptor in services) { if (descriptor.ImplementationType != null) { + var activatedActions = activatedActionList.GetActions(descriptor); + // Test if the an open generic type is being registered var serviceTypeInfo = descriptor.ServiceType.GetTypeInfo(); if (serviceTypeInfo.IsGenericTypeDefinition) @@ -196,7 +199,7 @@ public static class AutofacRegistration .RegisterGeneric(descriptor.ImplementationType) .As(descriptor.ServiceType) .ConfigureLifecycle(descriptor.Lifetime, lifetimeScopeTagForSingletons) - .ConfigureAbpConventions(moduleContainer, registrationActionList); + .ConfigureAbpConventions(moduleContainer, registrationActionList, activatedActions); } else { @@ -204,7 +207,7 @@ public static class AutofacRegistration .RegisterType(descriptor.ImplementationType) .As(descriptor.ServiceType) .ConfigureLifecycle(descriptor.Lifetime, lifetimeScopeTagForSingletons) - .ConfigureAbpConventions(moduleContainer, registrationActionList); + .ConfigureAbpConventions(moduleContainer, registrationActionList, activatedActions); } } else if (descriptor.ImplementationFactory != null) diff --git a/framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionLifetimeEventExtensions.cs b/framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionLifetimeEventExtensions.cs new file mode 100644 index 0000000000..14e2d28d51 --- /dev/null +++ b/framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionLifetimeEventExtensions.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using Volo.Abp.DependencyInjection; + +namespace Microsoft.Extensions.DependencyInjection; + +public static class ServiceCollectionLifetimeEventExtensions +{ + // OnActivated + public static void OnActivated(this IServiceCollection services, ServiceDescriptor descriptor, Action onActivatedAction) + { + GetOrCreateOnActivatedActionList(services).Add(new KeyValuePair>(descriptor, onActivatedAction)); + } + + public static ServiceActivatedActionList GetServiceActivatedActionList(this IServiceCollection services) + { + return GetOrCreateOnActivatedActionList(services); + } + + private static ServiceActivatedActionList GetOrCreateOnActivatedActionList(IServiceCollection services) + { + var actionList = services.GetSingletonInstanceOrNull>()?.Value; + if (actionList == null) + { + actionList = new ServiceActivatedActionList(); + services.AddObjectAccessor(actionList); + } + + return actionList; + } +} diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/IOnServiceActivatedContext.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/IOnServiceActivatedContext.cs new file mode 100644 index 0000000000..b2e1d10a93 --- /dev/null +++ b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/IOnServiceActivatedContext.cs @@ -0,0 +1,6 @@ +namespace Volo.Abp.DependencyInjection; + +public interface IOnServiceActivatedContext +{ + public object Instance { get; } +} diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/OnServiceActivatedContext.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/OnServiceActivatedContext.cs new file mode 100644 index 0000000000..91af1e130b --- /dev/null +++ b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/OnServiceActivatedContext.cs @@ -0,0 +1,11 @@ +namespace Volo.Abp.DependencyInjection; + +public class OnServiceActivatedContext : IOnServiceActivatedContext +{ + public object Instance { get; set; } + + public OnServiceActivatedContext(object instance) + { + Instance = instance; + } +} diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ServiceActivatedActionList.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ServiceActivatedActionList.cs new file mode 100644 index 0000000000..3e3c4afd9e --- /dev/null +++ b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ServiceActivatedActionList.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Extensions.DependencyInjection; + +namespace Volo.Abp.DependencyInjection; + +public class ServiceActivatedActionList : List>> +{ + public List> GetActions(ServiceDescriptor descriptor) + { + return this.Where(x => x.Key == descriptor).Select(x => x.Value).ToList(); + } +} diff --git a/framework/src/Volo.Abp.Ddd.Domain/Microsoft/Extensions/DependencyInjection/ServiceCollectionRepositoryExtensions.cs b/framework/src/Volo.Abp.Ddd.Domain/Microsoft/Extensions/DependencyInjection/ServiceCollectionRepositoryExtensions.cs index a877c673f1..6e98d442c5 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Microsoft/Extensions/DependencyInjection/ServiceCollectionRepositoryExtensions.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Microsoft/Extensions/DependencyInjection/ServiceCollectionRepositoryExtensions.cs @@ -84,22 +84,16 @@ public static class ServiceCollectionRepositoryExtensions bool replaceExisting, bool isReadOnlyRepository = false) { - ServiceDescriptor descriptor; + var descriptor = ServiceDescriptor.Transient(serviceType, implementationType); if (isReadOnlyRepository) { - services.TryAddTransient(implementationType); - descriptor = ServiceDescriptor.Transient(serviceType, provider => + services.OnActivated(descriptor, context => { - var repository = provider.GetRequiredService(implementationType); + var repository = context.Instance.As(); ObjectHelper.TrySetProperty(repository.As(), x => x.IsChangeTrackingEnabled, _ => false); - return repository; }); } - else - { - descriptor = ServiceDescriptor.Transient(serviceType, implementationType); - } if (replaceExisting) { diff --git a/framework/test/Volo.Abp.Autofac.Tests/Volo/Abp/Autofac/AutoFac_OnActivated_Tests.cs b/framework/test/Volo.Abp.Autofac.Tests/Volo/Abp/Autofac/AutoFac_OnActivated_Tests.cs new file mode 100644 index 0000000000..b2af6d85da --- /dev/null +++ b/framework/test/Volo.Abp.Autofac.Tests/Volo/Abp/Autofac/AutoFac_OnActivated_Tests.cs @@ -0,0 +1,39 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Volo.Abp.Autofac.Interception; +using Xunit; + +namespace Volo.Abp.Autofac; + +public class AutoFac_OnActivated_Tests : Autofac_Interception_Test +{ + protected override Task AfterAddApplicationAsync(IServiceCollection services) + { + var serviceDescriptor = ServiceDescriptor.Transient(); + services.Add(serviceDescriptor); + services.OnActivated(serviceDescriptor, x => + { + x.Instance.As().Name += "1"; + }); + services.OnActivated(serviceDescriptor, x => + { + x.Instance.As().Name += "2"; + }); + + return base.AfterAddApplicationAsync(services); + } + + [Fact] + public void Should_Call_OnActivated() + { + var server = ServiceProvider.GetRequiredService(); + server.Name.ShouldBe("MyServer12"); + } +} + +class MyServer +{ + public string Name { get; set; } = "MyServer"; +} diff --git a/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Repositories/RepositoryRegistration_Tests.cs b/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Repositories/RepositoryRegistration_Tests.cs index ca98017e0a..4a7a9fb28f 100644 --- a/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Repositories/RepositoryRegistration_Tests.cs +++ b/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Repositories/RepositoryRegistration_Tests.cs @@ -31,15 +31,15 @@ public class RepositoryRegistration_Tests //Assert //MyTestAggregateRootWithoutPk - services.ShouldContainTransientImplementationFactory(typeof(IReadOnlyRepository)); + services.ShouldContainTransient(typeof(IReadOnlyRepository), typeof(MyTestDefaultRepository)); services.ShouldContainTransient(typeof(IBasicRepository), typeof(MyTestDefaultRepository)); services.ShouldContainTransient(typeof(IRepository), typeof(MyTestDefaultRepository)); //MyTestAggregateRootWithGuidPk - services.ShouldContainTransientImplementationFactory(typeof(IReadOnlyRepository)); + services.ShouldContainTransient(typeof(IReadOnlyRepository), typeof(MyTestDefaultRepository)); services.ShouldContainTransient(typeof(IBasicRepository), typeof(MyTestDefaultRepository)); services.ShouldContainTransient(typeof(IRepository), typeof(MyTestDefaultRepository)); - services.ShouldContainTransientImplementationFactory(typeof(IReadOnlyRepository)); + services.ShouldContainTransient(typeof(IReadOnlyRepository), typeof(MyTestDefaultRepository)); services.ShouldContainTransient(typeof(IBasicRepository), typeof(MyTestDefaultRepository)); services.ShouldContainTransient(typeof(IRepository), typeof(MyTestDefaultRepository)); @@ -69,24 +69,24 @@ public class RepositoryRegistration_Tests //Assert //MyTestAggregateRootWithoutPk - services.ShouldContainTransientImplementationFactory(typeof(IReadOnlyRepository)); + services.ShouldContainTransient(typeof(IReadOnlyRepository), typeof(MyTestDefaultRepository)); services.ShouldContainTransient(typeof(IBasicRepository), typeof(MyTestDefaultRepository)); services.ShouldContainTransient(typeof(IRepository), typeof(MyTestDefaultRepository)); //MyTestAggregateRootWithGuidPk - services.ShouldContainTransientImplementationFactory(typeof(IReadOnlyRepository)); + services.ShouldContainTransient(typeof(IReadOnlyRepository), typeof(MyTestDefaultRepository)); services.ShouldContainTransient(typeof(IBasicRepository), typeof(MyTestDefaultRepository)); services.ShouldContainTransient(typeof(IRepository), typeof(MyTestDefaultRepository)); - services.ShouldContainTransientImplementationFactory(typeof(IReadOnlyRepository)); + services.ShouldContainTransient(typeof(IReadOnlyRepository), typeof(MyTestDefaultRepository)); services.ShouldContainTransient(typeof(IBasicRepository), typeof(MyTestDefaultRepository)); services.ShouldContainTransient(typeof(IRepository), typeof(MyTestDefaultRepository)); //MyTestEntityWithInt32Pk - services.ShouldContainTransientImplementationFactory(typeof(IReadOnlyRepository)); + services.ShouldContainTransient(typeof(IReadOnlyRepository), typeof(MyTestDefaultRepository)); services.ShouldContainTransient(typeof(IBasicRepository), typeof(MyTestDefaultRepository)); services.ShouldContainTransient(typeof(IRepository), typeof(MyTestDefaultRepository)); - services.ShouldContainTransientImplementationFactory(typeof(IReadOnlyRepository)); - services.ShouldContainTransientImplementationFactory(typeof(IReadOnlyBasicRepository)); + services.ShouldContainTransient(typeof(IReadOnlyRepository), typeof(MyTestDefaultRepository)); + services.ShouldContainTransient(typeof(IReadOnlyBasicRepository), typeof(MyTestDefaultRepository)); services.ShouldContainTransient(typeof(IBasicRepository), typeof(MyTestDefaultRepository)); services.ShouldContainTransient(typeof(IRepository), typeof(MyTestDefaultRepository)); } @@ -114,20 +114,20 @@ public class RepositoryRegistration_Tests services.ShouldContainTransient(typeof(IRepository), typeof(MyTestDefaultRepository)); //MyTestAggregateRootWithGuidPk - services.ShouldContainTransientImplementationFactory(typeof(IReadOnlyRepository)); + services.ShouldContainTransient(typeof(IReadOnlyRepository), typeof(MyTestAggregateRootWithDefaultPkCustomRepository)); services.ShouldContainTransient(typeof(IBasicRepository), typeof(MyTestAggregateRootWithDefaultPkCustomRepository)); services.ShouldContainTransient(typeof(IRepository), typeof(MyTestAggregateRootWithDefaultPkCustomRepository)); - services.ShouldContainTransientImplementationFactory(typeof(IReadOnlyRepository)); - services.ShouldContainTransientImplementationFactory(typeof(IReadOnlyBasicRepository)); + services.ShouldContainTransient(typeof(IReadOnlyRepository), typeof(MyTestAggregateRootWithDefaultPkCustomRepository)); + services.ShouldContainTransient(typeof(IReadOnlyBasicRepository), typeof(MyTestAggregateRootWithDefaultPkCustomRepository)); services.ShouldContainTransient(typeof(IBasicRepository), typeof(MyTestAggregateRootWithDefaultPkCustomRepository)); services.ShouldContainTransient(typeof(IRepository), typeof(MyTestAggregateRootWithDefaultPkCustomRepository)); //MyTestEntityWithInt32Pk - services.ShouldContainTransientImplementationFactory(typeof(IReadOnlyRepository)); + services.ShouldContainTransient(typeof(IReadOnlyRepository), typeof(MyTestDefaultRepository)); services.ShouldContainTransient(typeof(IBasicRepository), typeof(MyTestDefaultRepository)); services.ShouldContainTransient(typeof(IRepository), typeof(MyTestDefaultRepository)); - services.ShouldContainTransientImplementationFactory(typeof(IReadOnlyRepository)); - services.ShouldContainTransientImplementationFactory(typeof(IReadOnlyBasicRepository)); + services.ShouldContainTransient(typeof(IReadOnlyRepository), typeof(MyTestDefaultRepository)); + services.ShouldContainTransient(typeof(IReadOnlyBasicRepository), typeof(MyTestDefaultRepository)); services.ShouldContainTransient(typeof(IBasicRepository), typeof(MyTestDefaultRepository)); services.ShouldContainTransient(typeof(IRepository), typeof(MyTestDefaultRepository)); } @@ -209,10 +209,10 @@ public class RepositoryRegistration_Tests services.ShouldNotContainService(typeof(IRepository)); //MyTestAggregateRootWithGuidPk - services.ShouldContainTransientImplementationFactory(typeof(IReadOnlyRepository)); + services.ShouldContainTransient(typeof(IReadOnlyRepository), typeof(MyTestDefaultRepository)); services.ShouldContainTransient(typeof(IBasicRepository), typeof(MyTestDefaultRepository)); services.ShouldContainTransient(typeof(IRepository), typeof(MyTestDefaultRepository)); - services.ShouldContainTransientImplementationFactory(typeof(IReadOnlyRepository)); + services.ShouldContainTransient(typeof(IReadOnlyRepository), typeof(MyTestDefaultRepository)); services.ShouldContainTransient(typeof(IBasicRepository), typeof(MyTestDefaultRepository)); services.ShouldContainTransient(typeof(IRepository), typeof(MyTestDefaultRepository)); } @@ -234,11 +234,11 @@ public class RepositoryRegistration_Tests new MyTestRepositoryRegistrar(options).AddRepositories(); //MyTestAggregateRootWithGuidPk - services.ShouldContainTransientImplementationFactory(typeof(IReadOnlyRepository)); + services.ShouldContainTransient(typeof(IReadOnlyRepository), typeof(MyTestAggregateRootWithDefaultPkCustomRepository)); services.ShouldContainTransient(typeof(IBasicRepository), typeof(MyTestAggregateRootWithDefaultPkCustomRepository)); services.ShouldContainTransient(typeof(IRepository), typeof(MyTestAggregateRootWithDefaultPkCustomRepository)); - services.ShouldContainTransientImplementationFactory(typeof(IReadOnlyRepository)); - services.ShouldContainTransientImplementationFactory(typeof(IReadOnlyBasicRepository)); + services.ShouldContainTransient(typeof(IReadOnlyRepository), typeof(MyTestAggregateRootWithDefaultPkCustomRepository)); + services.ShouldContainTransient(typeof(IReadOnlyBasicRepository), typeof(MyTestAggregateRootWithDefaultPkCustomRepository)); services.ShouldContainTransient(typeof(IBasicRepository), typeof(MyTestAggregateRootWithDefaultPkCustomRepository)); services.ShouldContainTransient(typeof(IRepository), typeof(MyTestAggregateRootWithDefaultPkCustomRepository)); }