From 178eb273106f3ee484ee110d1003b84cc8b32e61 Mon Sep 17 00:00:00 2001 From: Lemon Date: Mon, 10 Sep 2018 10:31:29 +0700 Subject: [PATCH] Feature/auto configuration (#134) * Add IServiceResolveCallback Api * Update version of Microsoft.Extensions.Hosting * Refactor services.BuildxxxProvider * Fix csproj * performance optimization * Add AspectCore.Extensions.Configuration project * Refactor * Provider configuration bind * Fix scope resolve --- build/version.props | 4 +- .../AspectCore.Extensions.Configuration.sln | 53 +++++++++++++++++++ ...AspectCore.Extensions.Configuration.csproj | 22 ++++++++ .../ConfigurationBindingAttribute.cs | 18 +++++++ .../ConfigurationMetadataAttribute.cs | 27 ++++++++++ .../Attributes/ConfigurationValueAttribute.cs | 19 +++++++ .../ConfigurationBindResolveCallback.cs | 46 ++++++++++++++++ .../ConfigurationBindType.cs | 8 +++ .../IConfigurationMetadataProvider.cs | 11 ++++ .../ServiceContainerExtensions.cs | 19 +++++++ ...Core.Extensions.Configuration.Tests.csproj | 22 ++++++++ .../ConfigurationBindingTest.cs | 47 ++++++++++++++++ .../ConfigurationValueTest.cs | 43 +++++++++++++++ .../SimpleObjectBenchmarks.cs | 10 ++-- .../AspectCore.Abstractions.csproj | 11 ++-- .../Injector/IManyEnumerable.cs | 2 +- .../Injector/IServiceResolveCallback.cs | 15 ++++++ .../AspectCore.Core/AspectCore.Core.csproj | 14 ++--- .../Extensions/ServiceDefinitionExtensions.cs | 8 +++ .../IServiceResolveCallbackProvider.cs | 9 ++++ .../Injector/PropertyInjectorCallback.cs | 17 ++++++ .../Injector/ServiceCallSiteResolver.cs | 39 ++++++++------ .../Injector/ServiceContainer.cs | 3 ++ .../Injector/ServiceResolver.cs | 29 +++++----- .../Startup.cs | 2 +- .../DynamicProxyServiceProviderFactory.cs | 2 +- .../ServiceCollectionBuildExtensions.cs | 44 ++++++++++----- .../ServiceCollectionExtensions.cs | 16 +++--- .../ServiceCollectionToContainerExtensions.cs | 16 +++++- .../AspectCore.Extensions.Hosting.csproj | 2 +- .../AdditionalInterceptorSelectorTests.cs | 2 +- .../UseMicrosoftDISpecificationTests.cs | 2 +- 32 files changed, 505 insertions(+), 77 deletions(-) create mode 100644 configuration/AspectCore.Extensions.Configuration.sln create mode 100644 configuration/src/AspectCore.Extensions.Configuration/AspectCore.Extensions.Configuration.csproj create mode 100644 configuration/src/AspectCore.Extensions.Configuration/Attributes/ConfigurationBindingAttribute.cs create mode 100644 configuration/src/AspectCore.Extensions.Configuration/Attributes/ConfigurationMetadataAttribute.cs create mode 100644 configuration/src/AspectCore.Extensions.Configuration/Attributes/ConfigurationValueAttribute.cs create mode 100644 configuration/src/AspectCore.Extensions.Configuration/ConfigurationBindResolveCallback.cs create mode 100644 configuration/src/AspectCore.Extensions.Configuration/ConfigurationBindType.cs create mode 100644 configuration/src/AspectCore.Extensions.Configuration/IConfigurationMetadataProvider.cs create mode 100644 configuration/src/AspectCore.Extensions.Configuration/ServiceContainerExtensions.cs create mode 100644 configuration/test/AspectCore.Extensions.Configuration.Tests/AspectCore.Extensions.Configuration.Tests.csproj create mode 100644 configuration/test/AspectCore.Extensions.Configuration.Tests/ConfigurationBindingTest.cs create mode 100644 configuration/test/AspectCore.Extensions.Configuration.Tests/ConfigurationValueTest.cs create mode 100644 core/src/AspectCore.Abstractions/Injector/IServiceResolveCallback.cs create mode 100644 core/src/AspectCore.Core/Injector/IServiceResolveCallbackProvider.cs create mode 100644 core/src/AspectCore.Core/Injector/PropertyInjectorCallback.cs diff --git a/build/version.props b/build/version.props index 71824219..dacf3585 100644 --- a/build/version.props +++ b/build/version.props @@ -1,7 +1,7 @@ - 0 - 7 + 1 + 0 0 $(VersionMajor).$(VersionMinor).$(VersionPatch) diff --git a/configuration/AspectCore.Extensions.Configuration.sln b/configuration/AspectCore.Extensions.Configuration.sln new file mode 100644 index 00000000..d57a4ccd --- /dev/null +++ b/configuration/AspectCore.Extensions.Configuration.sln @@ -0,0 +1,53 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspectCore.Extensions.Configuration", "src\AspectCore.Extensions.Configuration\AspectCore.Extensions.Configuration.csproj", "{F031B41A-C7D1-475C-AF51-3C163A91DF8B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "references", "references", "{17B63D57-9385-4790-BC48-8682125AD342}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{BD682B96-A49E-496C-B093-85904FA3474A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspectCore.Extensions.Reflection", "..\reflection\src\AspectCore.Extensions.Reflection\AspectCore.Extensions.Reflection.csproj", "{BDB5D3B0-4CC1-4BC4-951A-AACF2C5BB0E2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspectCore.Abstractions", "..\core\src\AspectCore.Abstractions\AspectCore.Abstractions.csproj", "{D2E82FEC-7753-4BC2-BF7F-777C53D009F1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspectCore.Core", "..\core\src\AspectCore.Core\AspectCore.Core.csproj", "{1E167DFD-C7D6-4EB0-9B87-F65AF97764C5}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{EE749FBD-FA9E-4391-8CCA-FA7E9DE183BD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspectCore.Extensions.Configuration.Tests", "test\AspectCore.Extensions.Configuration.Tests\AspectCore.Extensions.Configuration.Tests.csproj", "{7E643D68-6361-4611-BF65-23FF230591A5}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F031B41A-C7D1-475C-AF51-3C163A91DF8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F031B41A-C7D1-475C-AF51-3C163A91DF8B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F031B41A-C7D1-475C-AF51-3C163A91DF8B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F031B41A-C7D1-475C-AF51-3C163A91DF8B}.Release|Any CPU.Build.0 = Release|Any CPU + {BDB5D3B0-4CC1-4BC4-951A-AACF2C5BB0E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BDB5D3B0-4CC1-4BC4-951A-AACF2C5BB0E2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BDB5D3B0-4CC1-4BC4-951A-AACF2C5BB0E2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BDB5D3B0-4CC1-4BC4-951A-AACF2C5BB0E2}.Release|Any CPU.Build.0 = Release|Any CPU + {D2E82FEC-7753-4BC2-BF7F-777C53D009F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D2E82FEC-7753-4BC2-BF7F-777C53D009F1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D2E82FEC-7753-4BC2-BF7F-777C53D009F1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D2E82FEC-7753-4BC2-BF7F-777C53D009F1}.Release|Any CPU.Build.0 = Release|Any CPU + {1E167DFD-C7D6-4EB0-9B87-F65AF97764C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1E167DFD-C7D6-4EB0-9B87-F65AF97764C5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1E167DFD-C7D6-4EB0-9B87-F65AF97764C5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1E167DFD-C7D6-4EB0-9B87-F65AF97764C5}.Release|Any CPU.Build.0 = Release|Any CPU + {7E643D68-6361-4611-BF65-23FF230591A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7E643D68-6361-4611-BF65-23FF230591A5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7E643D68-6361-4611-BF65-23FF230591A5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7E643D68-6361-4611-BF65-23FF230591A5}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {F031B41A-C7D1-475C-AF51-3C163A91DF8B} = {BD682B96-A49E-496C-B093-85904FA3474A} + {BDB5D3B0-4CC1-4BC4-951A-AACF2C5BB0E2} = {17B63D57-9385-4790-BC48-8682125AD342} + {D2E82FEC-7753-4BC2-BF7F-777C53D009F1} = {17B63D57-9385-4790-BC48-8682125AD342} + {1E167DFD-C7D6-4EB0-9B87-F65AF97764C5} = {17B63D57-9385-4790-BC48-8682125AD342} + {7E643D68-6361-4611-BF65-23FF230591A5} = {EE749FBD-FA9E-4391-8CCA-FA7E9DE183BD} + EndGlobalSection +EndGlobal diff --git a/configuration/src/AspectCore.Extensions.Configuration/AspectCore.Extensions.Configuration.csproj b/configuration/src/AspectCore.Extensions.Configuration/AspectCore.Extensions.Configuration.csproj new file mode 100644 index 00000000..43c8c25b --- /dev/null +++ b/configuration/src/AspectCore.Extensions.Configuration/AspectCore.Extensions.Configuration.csproj @@ -0,0 +1,22 @@ + + + + Configuration extension system for ASP.NET Core via AspectCore-Framework. + AspectCore.Extensions.Configuration + False + AspectCore.Extensions.Configuration + AspectCore.Extensions.Configuration + Reflection;Aop;DynamicProxy;Configuration + Configuration extension system for ASP.NET Core via AspectCore-Framework. + netstandard2.0 + + + + + + + + + + + diff --git a/configuration/src/AspectCore.Extensions.Configuration/Attributes/ConfigurationBindingAttribute.cs b/configuration/src/AspectCore.Extensions.Configuration/Attributes/ConfigurationBindingAttribute.cs new file mode 100644 index 00000000..41fb0c4a --- /dev/null +++ b/configuration/src/AspectCore.Extensions.Configuration/Attributes/ConfigurationBindingAttribute.cs @@ -0,0 +1,18 @@ +using System; + +namespace AspectCore.Extensions.Configuration +{ + public class ConfigurationBindingAttribute : ConfigurationMetadataAttribute + { + public override string[] Sections { get; } + + public override string Key { get; } = null; + + public override ConfigurationBindType Type { get; } = ConfigurationBindType.Class; + + public ConfigurationBindingAttribute(params string[] sections) + { + Sections = sections; + } + } +} \ No newline at end of file diff --git a/configuration/src/AspectCore.Extensions.Configuration/Attributes/ConfigurationMetadataAttribute.cs b/configuration/src/AspectCore.Extensions.Configuration/Attributes/ConfigurationMetadataAttribute.cs new file mode 100644 index 00000000..be4d66bb --- /dev/null +++ b/configuration/src/AspectCore.Extensions.Configuration/Attributes/ConfigurationMetadataAttribute.cs @@ -0,0 +1,27 @@ +using System; +using System.Linq; + +namespace AspectCore.Extensions.Configuration +{ + public abstract class ConfigurationMetadataAttribute : Attribute, IConfigurationMetadataProvider + { + public abstract string[] Sections { get; } + public abstract string Key { get; } + public abstract ConfigurationBindType Type { get; } + + public string GetSection() + { + if (Sections == null || Sections.Length == 0) + { + return null; + } + + if (Sections.Length ==1) + { + return Sections[0]; + } + + return Sections.Aggregate((x, y) => x + ":" + y); + } + } +} \ No newline at end of file diff --git a/configuration/src/AspectCore.Extensions.Configuration/Attributes/ConfigurationValueAttribute.cs b/configuration/src/AspectCore.Extensions.Configuration/Attributes/ConfigurationValueAttribute.cs new file mode 100644 index 00000000..3b76464a --- /dev/null +++ b/configuration/src/AspectCore.Extensions.Configuration/Attributes/ConfigurationValueAttribute.cs @@ -0,0 +1,19 @@ +using System; + +namespace AspectCore.Extensions.Configuration +{ + public class ConfigurationValueAttribute : ConfigurationMetadataAttribute + { + public override string Key { get; } + + public override ConfigurationBindType Type { get; } = ConfigurationBindType.Value; + + public override string[] Sections { get; } + + public ConfigurationValueAttribute(string key, params string[] sections) + { + Key = key; + Sections = sections; + } + } +} \ No newline at end of file diff --git a/configuration/src/AspectCore.Extensions.Configuration/ConfigurationBindResolveCallback.cs b/configuration/src/AspectCore.Extensions.Configuration/ConfigurationBindResolveCallback.cs new file mode 100644 index 00000000..ff7080f4 --- /dev/null +++ b/configuration/src/AspectCore.Extensions.Configuration/ConfigurationBindResolveCallback.cs @@ -0,0 +1,46 @@ +using System.Reflection; +using AspectCore.Injector; +using AspectCore.Extensions.Reflection; +using Microsoft.Extensions.Configuration; + +namespace AspectCore.Extensions.Configuration +{ + public sealed class ConfigurationBindResolveCallback : IServiceResolveCallback + { + private const BindingFlags _flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; + + public object Invoke(IServiceResolver resolver, object instance, ServiceDefinition service) + { + if (instance == null || instance is IConfiguration) + { + return instance; + } + + var instanceType = instance.GetType(); + var configuration = resolver.ResolveRequired(); + foreach (var field in instanceType.GetFields(_flags)) + { + var reflector = field.GetReflector(); + var configurationMetadata = reflector.GetCustomAttribute(); + if (configurationMetadata == null) + { + continue; + } + + var section = configurationMetadata.GetSection(); + if (configurationMetadata.Type == ConfigurationBindType.Value) + { + var key = section == null ? configurationMetadata.Key : section + ":" + configurationMetadata.Key; + reflector.SetValue(instance, configuration.GetValue(field.FieldType, key)); + } + else + { + var configurationSection = section == null ? configuration : configuration.GetSection(section); + reflector.SetValue(instance, configurationSection.Get(field.FieldType)); + } + } + + return instance; + } + } +} \ No newline at end of file diff --git a/configuration/src/AspectCore.Extensions.Configuration/ConfigurationBindType.cs b/configuration/src/AspectCore.Extensions.Configuration/ConfigurationBindType.cs new file mode 100644 index 00000000..49cd5f18 --- /dev/null +++ b/configuration/src/AspectCore.Extensions.Configuration/ConfigurationBindType.cs @@ -0,0 +1,8 @@ +namespace AspectCore.Extensions.Configuration +{ + public enum ConfigurationBindType + { + Value, + Class + } +} \ No newline at end of file diff --git a/configuration/src/AspectCore.Extensions.Configuration/IConfigurationMetadataProvider.cs b/configuration/src/AspectCore.Extensions.Configuration/IConfigurationMetadataProvider.cs new file mode 100644 index 00000000..d82fd669 --- /dev/null +++ b/configuration/src/AspectCore.Extensions.Configuration/IConfigurationMetadataProvider.cs @@ -0,0 +1,11 @@ +namespace AspectCore.Extensions.Configuration +{ + public interface IConfigurationMetadataProvider + { + string[] Sections { get; } + + string Key { get; } + + ConfigurationBindType Type { get; } + } +} \ No newline at end of file diff --git a/configuration/src/AspectCore.Extensions.Configuration/ServiceContainerExtensions.cs b/configuration/src/AspectCore.Extensions.Configuration/ServiceContainerExtensions.cs new file mode 100644 index 00000000..c68f00a3 --- /dev/null +++ b/configuration/src/AspectCore.Extensions.Configuration/ServiceContainerExtensions.cs @@ -0,0 +1,19 @@ +using System; +using AspectCore.Injector; + +namespace AspectCore.Extensions.Configuration +{ + public static class ServiceContainerExtensions + { + public static IServiceContainer AddConfigurationInject(this IServiceContainer container) + { + if (container == null) + { + throw new ArgumentNullException(nameof(container)); + } + + container.AddType(Lifetime.Singleton); + return container; + } + } +} \ No newline at end of file diff --git a/configuration/test/AspectCore.Extensions.Configuration.Tests/AspectCore.Extensions.Configuration.Tests.csproj b/configuration/test/AspectCore.Extensions.Configuration.Tests/AspectCore.Extensions.Configuration.Tests.csproj new file mode 100644 index 00000000..3d3fbe01 --- /dev/null +++ b/configuration/test/AspectCore.Extensions.Configuration.Tests/AspectCore.Extensions.Configuration.Tests.csproj @@ -0,0 +1,22 @@ + + + + netcoreapp2.1 + + false + + + + + + + + + + + + + + + + diff --git a/configuration/test/AspectCore.Extensions.Configuration.Tests/ConfigurationBindingTest.cs b/configuration/test/AspectCore.Extensions.Configuration.Tests/ConfigurationBindingTest.cs new file mode 100644 index 00000000..cd566aba --- /dev/null +++ b/configuration/test/AspectCore.Extensions.Configuration.Tests/ConfigurationBindingTest.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; +using AspectCore.Injector; +using Microsoft.Extensions.Configuration; +using Xunit; + +namespace AspectCore.Extensions.Configuration.Tests +{ + public class ConfigurationBindingTest + { + [Fact] + public void LoadBinding() + { + var dict = new Dictionary + { + {"creator:age", "24"}, + {"creator:name", "lemon"} + }; + var builder = new ConfigurationBuilder().AddEnvironmentVariables(); + builder.AddInMemoryCollection(dict); + var configuration = builder.Build(); + var container = new ServiceContainer(); + container.AddInstance(configuration); + container.AddConfigurationInject(); + container.AddType(); + var service = container.Build().Resolve(); + Assert.Equal(service.ToString(), "lemon-24"); + } + } + + public class Config + { + public string Name { get; set; } + + public int Age { get; set; } + } + + public class BindConfigService + { + [ConfigurationBinding("creator")] + private Config _config; + + public override string ToString() + { + return $"{_config.Name}-{_config.Age}"; + } + } +} \ No newline at end of file diff --git a/configuration/test/AspectCore.Extensions.Configuration.Tests/ConfigurationValueTest.cs b/configuration/test/AspectCore.Extensions.Configuration.Tests/ConfigurationValueTest.cs new file mode 100644 index 00000000..e5bb3e4b --- /dev/null +++ b/configuration/test/AspectCore.Extensions.Configuration.Tests/ConfigurationValueTest.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using Microsoft.Extensions.Configuration; +using Xunit; +using AspectCore.Injector; + +namespace AspectCore.Extensions.Configuration.Tests +{ + public class ConfigurationValueTest + { + [Fact] + public void LoadValue() + { + var dict = new Dictionary + { + {"creator:age", "24"}, + {"creator:name", "lemon"} + }; + var builder = new ConfigurationBuilder().AddEnvironmentVariables(); + builder.AddInMemoryCollection(dict); + var configuration = builder.Build(); + var container = new ServiceContainer(); + container.AddInstance(configuration); + container.AddConfigurationInject(); + container.AddType(); + var service = container.Build().Resolve(); + Assert.Equal(service.ToString(), "lemon-24"); + } + } + + public class ValueConfigService + { + [ConfigurationValue("age", "creator")] + private int age; + + [ConfigurationValue("name", "creator")] + private string name; + + public override string ToString() + { + return $"{name}-{age}"; + } + } +} \ No newline at end of file diff --git a/core/performance/AspectCore.IoC.Benchmarks/SimpleObjectBenchmarks.cs b/core/performance/AspectCore.IoC.Benchmarks/SimpleObjectBenchmarks.cs index 0f00dc16..cdafd951 100644 --- a/core/performance/AspectCore.IoC.Benchmarks/SimpleObjectBenchmarks.cs +++ b/core/performance/AspectCore.IoC.Benchmarks/SimpleObjectBenchmarks.cs @@ -60,32 +60,32 @@ public SimpleObjectBenchmarks() dryIocContainer.Register(typeof(IRepository<>), typeof(Repository<>)); } - //[Benchmark] + [Benchmark] public IUserService AspectCoreIoC() { return serviceResolver.Resolve(); } - //[Benchmark] + [Benchmark] public IUserService MicrosoftDependencyInjection() { return serviceProvider.GetService(); } - //[Benchmark] + [Benchmark] public IUserService Autofac() { return autofacContainer.Resolve(); } - //[Benchmark] + [Benchmark] public IUserService ZkWebIoC() { return zkWebContainer.Resolve(ZKWebStandard.Ioc.IfUnresolved.Throw, null); } - //[Benchmark] + [Benchmark] public IUserService DryIoC() { return dryIocContainer.Resolve(); diff --git a/core/src/AspectCore.Abstractions/AspectCore.Abstractions.csproj b/core/src/AspectCore.Abstractions/AspectCore.Abstractions.csproj index 5205be4a..6acf2524 100644 --- a/core/src/AspectCore.Abstractions/AspectCore.Abstractions.csproj +++ b/core/src/AspectCore.Abstractions/AspectCore.Abstractions.csproj @@ -7,18 +7,17 @@ AspectCore.Abstractions AspectCore.Abstractions AspectCore.Abstractions - DynamicProxy;Aop;Aspect;AspectCore;Intercepter + DynamicProxy;Aop;Aspect;AspectCore;Interceptor The abstract design of the AspectCore framework. netstandard2.0;net45 - + - - + - + - + diff --git a/core/src/AspectCore.Abstractions/Injector/IManyEnumerable.cs b/core/src/AspectCore.Abstractions/Injector/IManyEnumerable.cs index 4ede5aab..f9c64b64 100644 --- a/core/src/AspectCore.Abstractions/Injector/IManyEnumerable.cs +++ b/core/src/AspectCore.Abstractions/Injector/IManyEnumerable.cs @@ -4,7 +4,7 @@ namespace AspectCore.Injector { - [NonAspect] + [NonAspect, NonCallback] public interface IManyEnumerable : IEnumerable, IEnumerable { } diff --git a/core/src/AspectCore.Abstractions/Injector/IServiceResolveCallback.cs b/core/src/AspectCore.Abstractions/Injector/IServiceResolveCallback.cs new file mode 100644 index 00000000..7dfd8994 --- /dev/null +++ b/core/src/AspectCore.Abstractions/Injector/IServiceResolveCallback.cs @@ -0,0 +1,15 @@ +using System; +using AspectCore.DynamicProxy; + +namespace AspectCore.Injector +{ + [NonAspect, NonCallback] + public interface IServiceResolveCallback + { + object Invoke(IServiceResolver resolver, object instance, ServiceDefinition service); + } + + public sealed class NonCallback : Attribute + { + } +} \ No newline at end of file diff --git a/core/src/AspectCore.Core/AspectCore.Core.csproj b/core/src/AspectCore.Core/AspectCore.Core.csproj index 85aa5ea0..644e5f3e 100644 --- a/core/src/AspectCore.Core/AspectCore.Core.csproj +++ b/core/src/AspectCore.Core/AspectCore.Core.csproj @@ -6,17 +6,17 @@ true AspectCore.Core AspectCore.Core - DynamicProxy;Aop;Aspect;AspectCore;Intercepter + DynamicProxy;Aop;Aspect;AspectCore;Interceptor The implementation of the AspectCore framework. - netstandard2.0;net45 + net45;netstandard2.0 - - - - - + + + + + \ No newline at end of file diff --git a/core/src/AspectCore.Core/Injector/Extensions/ServiceDefinitionExtensions.cs b/core/src/AspectCore.Core/Injector/Extensions/ServiceDefinitionExtensions.cs index e0bc02f6..1a011591 100644 --- a/core/src/AspectCore.Core/Injector/Extensions/ServiceDefinitionExtensions.cs +++ b/core/src/AspectCore.Core/Injector/Extensions/ServiceDefinitionExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Diagnostics; using System.Reflection; using System.Linq; @@ -9,6 +10,8 @@ namespace AspectCore.Injector { internal static class ServiceDefinitionExtensions { + private static readonly ConcurrentDictionary _callbackMap = new ConcurrentDictionary(); + internal static Type GetImplementationType(this ServiceDefinition serviceDefinition) { if (serviceDefinition is TypeServiceDefinition typeServiceDefinition) @@ -60,5 +63,10 @@ internal static bool IsManyEnumerable(this ServiceDefinition serviceDefinition) var serviceTypeInfo = serviceDefinition.ServiceType.GetTypeInfo(); return serviceTypeInfo.IsGenericType && serviceTypeInfo.GetGenericTypeDefinition() == typeof(IManyEnumerable<>); } + + internal static bool RequiredResolveCallback(this ServiceDefinition serviceDefinition) + { + return _callbackMap.GetOrAdd(serviceDefinition, service => !service.ServiceType.GetReflector().IsDefined()); + } } } \ No newline at end of file diff --git a/core/src/AspectCore.Core/Injector/IServiceResolveCallbackProvider.cs b/core/src/AspectCore.Core/Injector/IServiceResolveCallbackProvider.cs new file mode 100644 index 00000000..725f5a19 --- /dev/null +++ b/core/src/AspectCore.Core/Injector/IServiceResolveCallbackProvider.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace AspectCore.Injector +{ + internal interface IServiceResolveCallbackProvider + { + IServiceResolveCallback[] ServiceResolveCallbacks { get; } + } +} \ No newline at end of file diff --git a/core/src/AspectCore.Core/Injector/PropertyInjectorCallback.cs b/core/src/AspectCore.Core/Injector/PropertyInjectorCallback.cs new file mode 100644 index 00000000..4d4be565 --- /dev/null +++ b/core/src/AspectCore.Core/Injector/PropertyInjectorCallback.cs @@ -0,0 +1,17 @@ +namespace AspectCore.Injector +{ + public class PropertyInjectorCallback : IServiceResolveCallback + { + public object Invoke(IServiceResolver resolver, object instance, ServiceDefinition service) + { + if (instance == null || !service.RequiredPropertyInjection()) + { + return instance; + } + var injectorFactory = resolver.Resolve(); + var injector = injectorFactory.Create(instance.GetType()); + injector.Invoke(instance); + return instance; + } + } +} \ No newline at end of file diff --git a/core/src/AspectCore.Core/Injector/ServiceCallSiteResolver.cs b/core/src/AspectCore.Core/Injector/ServiceCallSiteResolver.cs index 8f44a8ae..38ca2750 100644 --- a/core/src/AspectCore.Core/Injector/ServiceCallSiteResolver.cs +++ b/core/src/AspectCore.Core/Injector/ServiceCallSiteResolver.cs @@ -12,6 +12,7 @@ internal class ServiceCallSiteResolver { private readonly ConstructorCallSiteResolver _constructorCallSiteResolver; private readonly ConcurrentDictionary> _resolvedCallSites; + public ServiceCallSiteResolver(ServiceTable serviceTable) { _constructorCallSiteResolver = new ConstructorCallSiteResolver(serviceTable); @@ -20,31 +21,30 @@ public ServiceCallSiteResolver(ServiceTable serviceTable) internal Func Resolve(ServiceDefinition service) { - return _resolvedCallSites.GetOrAdd(service, ResolvePropertyInject); + return _resolvedCallSites.GetOrAdd(service, ResolveCallback); } - internal Func ResolvePropertyInject(ServiceDefinition service) + private Func ResolveCallback(ServiceDefinition service) { var callSite = ResolveInternal(service); - if (!service.RequiredPropertyInjection()) + if (!service.RequiredResolveCallback()) { return callSite; } + return resolver => { var instance = callSite(resolver); - if (instance == null) + var callbacks = resolver.ServiceResolveCallbacks; + for (var i = 0; i < callbacks.Length; i++) { - return null; + instance = callbacks[i].Invoke(resolver, instance, service); } - var injectorFactory = resolver.Resolve(); - var injector = injectorFactory.Create(instance.GetType()); - injector.Invoke(instance); return instance; }; } - internal Func ResolveInternal(ServiceDefinition service) + private Func ResolveInternal(ServiceDefinition service) { switch (service) { @@ -56,13 +56,15 @@ internal Func ResolveInternal(ServiceDefinition service return delegateServiceDefinition.ImplementationDelegate; case TypeServiceDefinition typeServiceDefinition: return ResolveTypeService(typeServiceDefinition); - case ManyEnumerableServiceDefintion manyEnumerableServiceDefintion: - return ResolveManyEnumerableService(manyEnumerableServiceDefintion); - case EnumerableServiceDefintion enumerableServiceDefintion: - return ResolveEnumerableService(enumerableServiceDefintion); + case ManyEnumerableServiceDefintion manyEnumerableServiceDefinition: + return ResolveManyEnumerableService(manyEnumerableServiceDefinition); + case EnumerableServiceDefintion enumerableServiceDefinition: + return ResolveEnumerableService(enumerableServiceDefinition); default: return resolver => null; - }; + } + + ; } private Func ResolveManyEnumerableService(ManyEnumerableServiceDefintion manyEnumerableServiceDefintion) @@ -77,6 +79,7 @@ private Func ResolveManyEnumerableService(ManyEnumerabl { instance.SetValue(resolver.ResolveDefinition(elementDefinitions[i]), i); } + return ActivatorUtils.CreateManyEnumerable(elementType, instance); }; } @@ -94,6 +97,7 @@ private Func ResolveEnumerableService(EnumerableService var element = resolver.ResolveDefinition(elementDefinitions[i]); instance.SetValue(element, i); } + return instance; }; } @@ -104,7 +108,8 @@ private Func ResolveProxyService(ProxyServiceDefinition { return ResolveTypeService(proxyServiceDefinition.ClassProxyServiceDefinition); } - var proxyConstructor = proxyServiceDefinition.ProxyType.GetTypeInfo().GetConstructor(new Type[] { typeof(IAspectActivatorFactory), proxyServiceDefinition.ServiceType }); + + var proxyConstructor = proxyServiceDefinition.ProxyType.GetTypeInfo().GetConstructor(new Type[] {typeof(IAspectActivatorFactory), proxyServiceDefinition.ServiceType}); var reflector = proxyConstructor.GetReflector(); var serviceResolver = Resolve(proxyServiceDefinition.ServiceDefinition); return resolver => reflector.Invoke(resolver.ResolveRequired(), serviceResolver(resolver)); @@ -115,8 +120,10 @@ private Func ResolveTypeService(TypeServiceDefinition t var callSite = _constructorCallSiteResolver.Resolve(typeServiceDefinition.ImplementationType); if (callSite == null) { - throw new InvalidOperationException($"Failed to create instance of type '{typeServiceDefinition.ServiceType}'. Possible reason is cannot match the best constructor of type '{typeServiceDefinition.ImplementationType}'."); + throw new InvalidOperationException( + $"Failed to create instance of type '{typeServiceDefinition.ServiceType}'. Possible reason is cannot match the best constructor of type '{typeServiceDefinition.ImplementationType}'."); } + return callSite; } } diff --git a/core/src/AspectCore.Core/Injector/ServiceContainer.cs b/core/src/AspectCore.Core/Injector/ServiceContainer.cs index 9b1e8b98..2f9d68e6 100644 --- a/core/src/AspectCore.Core/Injector/ServiceContainer.cs +++ b/core/src/AspectCore.Core/Injector/ServiceContainer.cs @@ -73,6 +73,9 @@ private void AddInternalServices() Singletons.AddInstance(_configuration); if (!Contains(typeof(ITransientServiceAccessor<>))) Singletons.AddType(typeof(ITransientServiceAccessor<>), typeof(TransientServiceAccessor<>)); + + //add service resolve callbacks + Scopeds.AddType(); //add DynamicProxy services Singletons.AddType(); diff --git a/core/src/AspectCore.Core/Injector/ServiceResolver.cs b/core/src/AspectCore.Core/Injector/ServiceResolver.cs index 7d6b5a44..485ec98e 100644 --- a/core/src/AspectCore.Core/Injector/ServiceResolver.cs +++ b/core/src/AspectCore.Core/Injector/ServiceResolver.cs @@ -1,15 +1,16 @@ using System; using System.Collections.Concurrent; +using System.Collections.Generic; using System.Linq; using AspectCore.DynamicProxy; namespace AspectCore.Injector { [NonAspect] - internal class ServiceResolver : IServiceResolver + internal sealed class ServiceResolver : IServiceResolver,IServiceResolveCallbackProvider { - private readonly ConcurrentDictionary _resolvedScopedServcies; - private readonly ConcurrentDictionary _resolvedSingletonServcies; + private readonly ConcurrentDictionary _resolvedScopedServices; + private readonly ConcurrentDictionary _resolvedSingletonServices; private readonly ServiceTable _serviceTable; private readonly ServiceCallSiteResolver _serviceCallSiteResolver; internal readonly ServiceResolver _root; @@ -18,18 +19,22 @@ public ServiceResolver(IServiceContainer serviceContainer) { _serviceTable = new ServiceTable(serviceContainer.Configuration); _serviceTable.Populate(serviceContainer); - _resolvedScopedServcies = new ConcurrentDictionary(); - _resolvedSingletonServcies = new ConcurrentDictionary(); + _resolvedScopedServices = new ConcurrentDictionary(); + _resolvedSingletonServices = new ConcurrentDictionary(); _serviceCallSiteResolver = new ServiceCallSiteResolver(_serviceTable); + ServiceResolveCallbacks = this.ResolveMany().ToArray(); } + public IServiceResolveCallback[] ServiceResolveCallbacks { get; } + public ServiceResolver(ServiceResolver root) { _root = root; _serviceTable = root._serviceTable; - _resolvedSingletonServcies = root._resolvedSingletonServcies; + _resolvedSingletonServices = root._resolvedSingletonServices; _serviceCallSiteResolver = root._serviceCallSiteResolver; - _resolvedScopedServcies = new ConcurrentDictionary(); + _resolvedScopedServices = new ConcurrentDictionary(); + ServiceResolveCallbacks = this.ResolveMany().ToArray(); } public object GetService(Type serviceType) @@ -59,9 +64,9 @@ internal object ResolveDefinition(ServiceDefinition definition) switch (definition.Lifetime) { case Lifetime.Singleton: - return _resolvedSingletonServcies.GetOrAdd(definition, d => _serviceCallSiteResolver.Resolve(d)(_root ?? this)); + return _resolvedSingletonServices.GetOrAdd(definition, d => _serviceCallSiteResolver.Resolve(d)(_root ?? this)); case Lifetime.Scoped: - return _resolvedScopedServcies.GetOrAdd(definition, d => _serviceCallSiteResolver.Resolve(d)(this)); + return _resolvedScopedServices.GetOrAdd(definition, d => _serviceCallSiteResolver.Resolve(d)(this)); default: return _serviceCallSiteResolver.Resolve(definition)(this); } @@ -70,7 +75,7 @@ internal object ResolveDefinition(ServiceDefinition definition) #region IDisposable Support private bool disposedValue = false; - protected virtual void Dispose(bool disposing) + private void Dispose(bool disposing) { if (!disposedValue) { @@ -78,13 +83,13 @@ protected virtual void Dispose(bool disposing) { if (_root == null || _root == this) { - foreach (var singleton in _resolvedSingletonServcies.Where(x => x.Value != this)) + foreach (var singleton in _resolvedSingletonServices.Where(x => x.Value != this)) { var disposable = singleton.Value as IDisposable; disposable?.Dispose(); } } - foreach (var scoped in _resolvedScopedServcies.Where(x => x.Value != this)) + foreach (var scoped in _resolvedScopedServices.Where(x => x.Value != this)) { var disposable = scoped.Value as IDisposable; disposable?.Dispose(); diff --git a/extras/sample/AspectCore.Extensions.DependencyInjection.Sample/Startup.cs b/extras/sample/AspectCore.Extensions.DependencyInjection.Sample/Startup.cs index ff02fd08..344c3e2b 100644 --- a/extras/sample/AspectCore.Extensions.DependencyInjection.Sample/Startup.cs +++ b/extras/sample/AspectCore.Extensions.DependencyInjection.Sample/Startup.cs @@ -34,7 +34,7 @@ public IServiceProvider ConfigureServices(IServiceCollection services) config.Interceptors.AddTyped(); }); //方式二步骤2.调用services.BuildAspectCoreServiceProvider构建动态代理服务解析器 - return services.BuildAspectCoreServiceProvider(); + return services.BuildAspectInjectorProvider(); } public void Configure(IApplicationBuilder app, IHostingEnvironment env) diff --git a/extras/src/AspectCore.Extensions.DependencyInjection/DynamicProxyServiceProviderFactory.cs b/extras/src/AspectCore.Extensions.DependencyInjection/DynamicProxyServiceProviderFactory.cs index 83bd79aa..34463d5e 100644 --- a/extras/src/AspectCore.Extensions.DependencyInjection/DynamicProxyServiceProviderFactory.cs +++ b/extras/src/AspectCore.Extensions.DependencyInjection/DynamicProxyServiceProviderFactory.cs @@ -12,7 +12,7 @@ public IServiceCollection CreateBuilder(IServiceCollection services) public IServiceProvider CreateServiceProvider(IServiceCollection containerBuilder) { - return containerBuilder.BuildAspectCoreServiceProvider(); + return containerBuilder.BuildDynamicProxyServiceProvider(); } } } diff --git a/extras/src/AspectCore.Extensions.DependencyInjection/ServiceCollectionBuildExtensions.cs b/extras/src/AspectCore.Extensions.DependencyInjection/ServiceCollectionBuildExtensions.cs index 68015018..1887d998 100644 --- a/extras/src/AspectCore.Extensions.DependencyInjection/ServiceCollectionBuildExtensions.cs +++ b/extras/src/AspectCore.Extensions.DependencyInjection/ServiceCollectionBuildExtensions.cs @@ -14,11 +14,28 @@ namespace AspectCore.Extensions.DependencyInjection { public static class ServiceCollectionBuildExtensions { + [Obsolete("Use BuildAspectInjectorProvider to return AspectCore Injector, or Use BuildDynamicProxyServiceProvider to return MSDI ServiceProvider.", true)] public static IServiceProvider BuildAspectCoreServiceProvider(this IServiceCollection services) { - return services.AddDynamicProxyCore().BuildServiceProvider(false); + return services.BuildDynamicProxyServiceProvider(); } - public static IServiceCollection AddDynamicProxyCore(this IServiceCollection services) + + public static ServiceProvider BuildDynamicProxyServiceProvider(this IServiceCollection services) + { + return services.WeaveDynamicProxyService().BuildServiceProvider(); + } + + public static ServiceProvider BuildDynamicProxyServiceProvider(this IServiceCollection services, bool validateScopes) + { + return services.WeaveDynamicProxyService().BuildServiceProvider(validateScopes); + } + + public static ServiceProvider BuildDynamicProxyServiceProvider(this IServiceCollection services, ServiceProviderOptions options) + { + return services.WeaveDynamicProxyService().BuildServiceProvider(options); + } + + public static IServiceCollection WeaveDynamicProxyService(this IServiceCollection services) { if (services == null) { @@ -33,7 +50,7 @@ public static IServiceCollection AddDynamicProxyCore(this IServiceCollection ser IServiceCollection dynamicProxyServices = new ServiceCollection(); foreach (var service in services) { - if (serviceValidator.TryValidate(service, out Type implementationType)) + if (serviceValidator.TryValidate(service, out var implementationType)) dynamicProxyServices.Add(MakeProxyService(service, implementationType, proxyTypeGenerator)); else dynamicProxyServices.Add(service); @@ -44,37 +61,36 @@ public static IServiceCollection AddDynamicProxyCore(this IServiceCollection ser return dynamicProxyServices; } - private static ServiceDescriptor MakeProxyService(ServiceDescriptor descriptor, Type implementationType, IProxyTypeGenerator proxyTypeGenerator) { var serviceTypeInfo = descriptor.ServiceType.GetTypeInfo(); if (serviceTypeInfo.IsClass) { return ServiceDescriptor.Describe( - descriptor.ServiceType, - proxyTypeGenerator.CreateClassProxyType(descriptor.ServiceType, implementationType), - descriptor.Lifetime); + descriptor.ServiceType, + proxyTypeGenerator.CreateClassProxyType(descriptor.ServiceType, implementationType), + descriptor.Lifetime); } else if (serviceTypeInfo.IsGenericTypeDefinition) { return ServiceDescriptor.Describe( - descriptor.ServiceType, - proxyTypeGenerator.CreateClassProxyType(implementationType, implementationType), - descriptor.Lifetime); + descriptor.ServiceType, + proxyTypeGenerator.CreateClassProxyType(implementationType, implementationType), + descriptor.Lifetime); } else { var proxyType = proxyTypeGenerator.CreateInterfaceProxyType(descriptor.ServiceType, implementationType); return ServiceDescriptor.Describe( - descriptor.ServiceType, - CreateFactory(descriptor, proxyType), - descriptor.Lifetime); + descriptor.ServiceType, + CreateFactory(descriptor, proxyType), + descriptor.Lifetime); } } private static Func CreateFactory(ServiceDescriptor descriptor, Type proxyType) { - var proxyConstructor = proxyType.GetTypeInfo().GetConstructor(new Type[] { typeof(IAspectActivatorFactory), descriptor.ServiceType }); + var proxyConstructor = proxyType.GetTypeInfo().GetConstructor(new Type[] {typeof(IAspectActivatorFactory), descriptor.ServiceType}); var reflector = proxyConstructor.GetReflector(); if (descriptor.ImplementationInstance != null) { diff --git a/extras/src/AspectCore.Extensions.DependencyInjection/ServiceCollectionExtensions.cs b/extras/src/AspectCore.Extensions.DependencyInjection/ServiceCollectionExtensions.cs index dfe3e054..77628660 100644 --- a/extras/src/AspectCore.Extensions.DependencyInjection/ServiceCollectionExtensions.cs +++ b/extras/src/AspectCore.Extensions.DependencyInjection/ServiceCollectionExtensions.cs @@ -11,7 +11,13 @@ namespace AspectCore.Extensions.DependencyInjection { public static class ServiceCollectionExtensions { + [Obsolete("Use ConfigureDynamicProxy")] public static IServiceCollection AddDynamicProxy(this IServiceCollection services, Action configure = null) + { + return ConfigureDynamicProxy(services, configure); + } + + public static IServiceCollection ConfigureDynamicProxy(this IServiceCollection services, Action configure = null) { if (services == null) { @@ -24,7 +30,7 @@ public static IServiceCollection AddDynamicProxy(this IServiceCollection service if (configurationService == null) { - services.AddSingleton(configuration); + services.AddSingleton(configuration); } return services; @@ -37,13 +43,7 @@ internal static IServiceCollection TryAddDynamicProxyServices(this IServiceColle throw new ArgumentNullException(nameof(services)); } - var configurationService = services.LastOrDefault(x => x.ServiceType == typeof(IAspectConfiguration) && x.ImplementationInstance != null); - var configuration = (IAspectConfiguration)configurationService?.ImplementationInstance ?? new AspectConfiguration(); - - if (configurationService == null) - { - services.AddSingleton(configuration); - } + //services.ConfigureDynamicProxy(); services.TryAddTransient(typeof(IManyEnumerable<>), typeof(ManyEnumerable<>)); services.TryAddScoped(); diff --git a/extras/src/AspectCore.Extensions.DependencyInjection/ServiceCollectionToContainerExtensions.cs b/extras/src/AspectCore.Extensions.DependencyInjection/ServiceCollectionToContainerExtensions.cs index 08766cdf..347a03de 100644 --- a/extras/src/AspectCore.Extensions.DependencyInjection/ServiceCollectionToContainerExtensions.cs +++ b/extras/src/AspectCore.Extensions.DependencyInjection/ServiceCollectionToContainerExtensions.cs @@ -8,12 +8,25 @@ namespace AspectCore.Extensions.DependencyInjection { public static class ServiceCollectionToContainerExtensions { + public static IServiceProvider BuildAspectInjectorProvider(this IServiceCollection services) + { + return BuildAspectInjectorProvider(services, null); + } + + public static IServiceProvider BuildAspectInjectorProvider(this IServiceCollection services, Action additional) + { + var container = services.ToServiceContainer(); + additional?.Invoke(container); + return container.Build(); + } + public static IServiceContainer ToServiceContainer(this IServiceCollection services) { if (services == null) { throw new ArgumentNullException(nameof(services)); } + services.AddScoped(); services.AddScoped(); return new ServiceContainer(services.AddAspectCoreContainer().Select(Replace)); @@ -24,7 +37,8 @@ public static IServiceCollection AddAspectCoreContainer(this IServiceCollection if (services == null) { throw new ArgumentNullException(nameof(services)); - } + } + services.TryAddSingleton, AspectCoreServiceProviderFactory>(); return services; } diff --git a/extras/src/AspectCore.Extensions.Hosting/AspectCore.Extensions.Hosting.csproj b/extras/src/AspectCore.Extensions.Hosting/AspectCore.Extensions.Hosting.csproj index f15659cf..e305a3ec 100644 --- a/extras/src/AspectCore.Extensions.Hosting/AspectCore.Extensions.Hosting.csproj +++ b/extras/src/AspectCore.Extensions.Hosting/AspectCore.Extensions.Hosting.csproj @@ -4,7 +4,7 @@ netstandard2.0 - + diff --git a/extras/test/AspectCore.Extensions.DependencyInjection.Test/AdditionalInterceptorSelectorTests.cs b/extras/test/AspectCore.Extensions.DependencyInjection.Test/AdditionalInterceptorSelectorTests.cs index 596d24cb..c4a5f390 100644 --- a/extras/test/AspectCore.Extensions.DependencyInjection.Test/AdditionalInterceptorSelectorTests.cs +++ b/extras/test/AspectCore.Extensions.DependencyInjection.Test/AdditionalInterceptorSelectorTests.cs @@ -16,7 +16,7 @@ public void ImplementationMethod_Test() { var services = new ServiceCollection(); services.AddTransient(); - var provider = services.BuildAspectCoreServiceProvider(); + var provider = services.BuildAspectInjectorProvider(); var service = provider.GetService(); var val = service.GetValue("le"); Assert.Equal("lemon", val); diff --git a/extras/test/AspectCore.Extensions.DependencyInjection.Test/UseMicrosoftDISpecificationTests.cs b/extras/test/AspectCore.Extensions.DependencyInjection.Test/UseMicrosoftDISpecificationTests.cs index 4d2fa6f1..0ff9d51e 100644 --- a/extras/test/AspectCore.Extensions.DependencyInjection.Test/UseMicrosoftDISpecificationTests.cs +++ b/extras/test/AspectCore.Extensions.DependencyInjection.Test/UseMicrosoftDISpecificationTests.cs @@ -14,7 +14,7 @@ protected override IServiceProvider CreateServiceProvider(IServiceCollection ser config.Interceptors.AddDelegate((ctx, next) => next(ctx)); }); - return serviceCollection.BuildAspectCoreServiceProvider(); + return serviceCollection.BuildDynamicProxyServiceProvider(); } } } \ No newline at end of file