From 5138fe4b892f184c8c70158d0f9a656a1aa9748f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=9C=88=E5=85=89=E5=8F=8C=E5=88=80?= <89009143@qq.com>
Date: Sat, 2 Feb 2019 17:53:39 +0800
Subject: [PATCH] add interceptor and dynamicProxy support for LightInject
(#151)
---
extras/AspectCore.Extras.sln | 18 +-
.../AspectCore.Extensions.LightInject.csproj | 19 +++
.../ContainerBuilderExtensions.cs | 158 ++++++++++++++++++
.../LightInjectServiceResolver.cs | 36 ++++
...ectCore.Extensions.LightInject.Test.csproj | 22 +++
.../AsyncIncreamentAttribute.cs | 31 ++++
.../AsyncInterceptorTests.cs | 105 ++++++++++++
.../AwaitBeforeInvokeNextTests.cs | 65 +++++++
.../ExcuteTimesTests.cs | 89 ++++++++++
.../RegistryTest.cs | 83 +++++++++
10 files changed, 624 insertions(+), 2 deletions(-)
create mode 100644 extras/src/AspectCore.Extensions.LightInject/AspectCore.Extensions.LightInject.csproj
create mode 100644 extras/src/AspectCore.Extensions.LightInject/ContainerBuilderExtensions.cs
create mode 100644 extras/src/AspectCore.Extensions.LightInject/LightInjectServiceResolver.cs
create mode 100644 extras/test/AspectCore.Extensions.LightInject.Test/AspectCore.Extensions.LightInject.Test.csproj
create mode 100644 extras/test/AspectCore.Extensions.LightInject.Test/AsyncIncreamentAttribute.cs
create mode 100644 extras/test/AspectCore.Extensions.LightInject.Test/AsyncInterceptorTests.cs
create mode 100644 extras/test/AspectCore.Extensions.LightInject.Test/AwaitBeforeInvokeNextTests.cs
create mode 100644 extras/test/AspectCore.Extensions.LightInject.Test/ExcuteTimesTests.cs
create mode 100644 extras/test/AspectCore.Extensions.LightInject.Test/RegistryTest.cs
diff --git a/extras/AspectCore.Extras.sln b/extras/AspectCore.Extras.sln
index de9efd9a..835c4b3f 100644
--- a/extras/AspectCore.Extras.sln
+++ b/extras/AspectCore.Extras.sln
@@ -43,9 +43,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspectCore.Extensions.DataA
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspectCore.Extensions.DependencyInjection.ConsoleSample", "sample\AspectCore.Extensions.DependencyInjection.ConsoleSample\AspectCore.Extensions.DependencyInjection.ConsoleSample.csproj", "{EBCFF74A-4787-4A3E-A83D-0DC0329CFD2E}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspectCore.Extensions.Hosting", "src\AspectCore.Extensions.Hosting\AspectCore.Extensions.Hosting.csproj", "{96DC1BED-E5E0-4E68-AF04-05115BE39B7A}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspectCore.Extensions.Hosting", "src\AspectCore.Extensions.Hosting\AspectCore.Extensions.Hosting.csproj", "{96DC1BED-E5E0-4E68-AF04-05115BE39B7A}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspectCore.Extensions.Hosting.Tests", "test\AspectCore.Extensions.Hosting.Tests\AspectCore.Extensions.Hosting.Tests.csproj", "{9547A839-70F0-4FAA-B574-9A61082FE567}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspectCore.Extensions.Hosting.Tests", "test\AspectCore.Extensions.Hosting.Tests\AspectCore.Extensions.Hosting.Tests.csproj", "{9547A839-70F0-4FAA-B574-9A61082FE567}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspectCore.Extensions.LightInject", "src\AspectCore.Extensions.LightInject\AspectCore.Extensions.LightInject.csproj", "{E3973799-7370-4D17-882F-D48CFB8C10F3}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspectCore.Extensions.LightInject.Test", "test\AspectCore.Extensions.LightInject.Test\AspectCore.Extensions.LightInject.Test.csproj", "{8E8A9BBB-0168-4A90-9477-FF2C325ECDCB}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -125,6 +129,14 @@ Global
{9547A839-70F0-4FAA-B574-9A61082FE567}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9547A839-70F0-4FAA-B574-9A61082FE567}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9547A839-70F0-4FAA-B574-9A61082FE567}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E3973799-7370-4D17-882F-D48CFB8C10F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E3973799-7370-4D17-882F-D48CFB8C10F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E3973799-7370-4D17-882F-D48CFB8C10F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E3973799-7370-4D17-882F-D48CFB8C10F3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8E8A9BBB-0168-4A90-9477-FF2C325ECDCB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8E8A9BBB-0168-4A90-9477-FF2C325ECDCB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8E8A9BBB-0168-4A90-9477-FF2C325ECDCB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8E8A9BBB-0168-4A90-9477-FF2C325ECDCB}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -148,6 +160,8 @@ Global
{EBCFF74A-4787-4A3E-A83D-0DC0329CFD2E} = {E933A059-A6AC-46AC-928D-33656D7FE93A}
{96DC1BED-E5E0-4E68-AF04-05115BE39B7A} = {EF35FB72-D934-45A8-9856-E60EA2623135}
{9547A839-70F0-4FAA-B574-9A61082FE567} = {BA3DCC4B-D8B9-4761-AAD9-4B1363140049}
+ {E3973799-7370-4D17-882F-D48CFB8C10F3} = {EF35FB72-D934-45A8-9856-E60EA2623135}
+ {8E8A9BBB-0168-4A90-9477-FF2C325ECDCB} = {BA3DCC4B-D8B9-4761-AAD9-4B1363140049}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B7A2B7A3-2AF0-4769-8E22-4FAA41E80591}
diff --git a/extras/src/AspectCore.Extensions.LightInject/AspectCore.Extensions.LightInject.csproj b/extras/src/AspectCore.Extensions.LightInject/AspectCore.Extensions.LightInject.csproj
new file mode 100644
index 00000000..a84079eb
--- /dev/null
+++ b/extras/src/AspectCore.Extensions.LightInject/AspectCore.Extensions.LightInject.csproj
@@ -0,0 +1,19 @@
+
+
+
+
+
+ Interceptor and dynamicProxy support for LightInject via AspectCore Framework.
+ DynamicProxy;Aop;LightInject;AspectCore
+ Interceptor and dynamicProxy support for LightInject via AspectCore Framework.
+ false
+ netstandard2.0
+
+
+
+
+
+
+
+
+
diff --git a/extras/src/AspectCore.Extensions.LightInject/ContainerBuilderExtensions.cs b/extras/src/AspectCore.Extensions.LightInject/ContainerBuilderExtensions.cs
new file mode 100644
index 00000000..d2f60bc7
--- /dev/null
+++ b/extras/src/AspectCore.Extensions.LightInject/ContainerBuilderExtensions.cs
@@ -0,0 +1,158 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using AspectCore.Configuration;
+using AspectCore.DynamicProxy;
+using AspectCore.DynamicProxy.Parameters;
+using AspectCore.Injector;
+using LightInject;
+using IServiceContainer = LightInject.IServiceContainer;
+
+namespace AspectCore.Extensions.LightInject
+{
+ public enum RegistryType
+ {
+ ByType,
+ ByInstance,
+ ByFactory
+ }
+
+ public static class ContainerBuilderExtensions
+ {
+ private static readonly string[] _nonAspect =
+ {
+ "LightInject.*",
+ "LightInject"
+ };
+
+ private static readonly string[] _excepts = new[]
+ {
+ "Microsoft.Extensions.Logging",
+ "Microsoft.Extensions.Options",
+ "System",
+ "System.*",
+ "IHttpContextAccessor",
+ "ITelemetryInitializer",
+ "IHostingEnvironment",
+ }.Concat(_nonAspect).ToArray();
+
+ public static IServiceContainer RegisterDynamicProxy(this IServiceContainer container,
+ IAspectConfiguration aspectConfig = null,
+ Action configure = null)
+ {
+ if (container == null)
+ {
+ throw new ArgumentNullException(nameof(container));
+ }
+ aspectConfig = aspectConfig ?? new AspectConfiguration();
+
+ foreach (var m in _nonAspect)
+ {
+ aspectConfig.NonAspectPredicates.AddNamespace(m);
+ }
+
+ configure?.Invoke(aspectConfig);
+
+ container.RegisterInstance(aspectConfig)
+ .Register(typeof(IManyEnumerable<>), typeof(ManyEnumerable<>))
+ .RegisterInstance(container)
+ .Register()
+ .Register()
+ .Register()
+ .Register()
+ .Register()
+ .Register()
+ .Register()
+ .Register()
+ .Register()
+ .Register()
+ .Register()
+ .Register()
+ .Register()
+ .Register()
+ .Register()
+ .Register();
+
+ container.Decorate(aspectConfig.CreateDecorator());
+
+ return container;
+ }
+
+ private static RegistryType GetRegistryType(this ServiceRegistration registration)
+ {
+ if (registration.FactoryExpression != null) return RegistryType.ByFactory;
+ else if (registration.Value != null) return RegistryType.ByInstance;
+ else return RegistryType.ByType;
+ }
+
+ private static Type GetImplType(this ServiceRegistration registration)
+ {
+ switch (registration.GetRegistryType())
+ {
+ case RegistryType.ByType: return registration.ImplementingType;
+ case RegistryType.ByInstance: return registration.Value.GetType();
+ case RegistryType.ByFactory: return registration.FactoryExpression.Method.ReturnType;
+ default: throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ private static DecoratorRegistration CreateDecorator(this IAspectConfiguration aspectConfiguration)
+ {
+ var reg = new DecoratorRegistration()
+ {
+ CanDecorate = s => CanDecorate(s, aspectConfiguration),
+ ImplementingTypeFactory = CreateProxyType
+ };
+ return reg;
+ }
+
+ private static Type CreateProxyType(IServiceFactory factory, ServiceRegistration registration)
+ {
+ var serviceType = registration.ServiceType.GetTypeInfo();
+ var implType = registration.GetImplType();
+ var proxyTypeGenerator = factory.GetInstance();
+
+ if (serviceType.IsClass)
+ {
+ return proxyTypeGenerator.CreateClassProxyType(serviceType, implType);
+ }
+ else if (serviceType.IsGenericTypeDefinition)
+ {
+ return proxyTypeGenerator.CreateClassProxyType(implType, implType);
+ }
+ else
+ {
+ return proxyTypeGenerator.CreateInterfaceProxyType(serviceType, implType);
+ }
+ }
+
+ private static bool CanDecorate(ServiceRegistration registration, IAspectConfiguration aspectConfiguration)
+ {
+ var serviceType = registration.ServiceType.GetTypeInfo();
+ var implType = registration.GetImplType().GetTypeInfo();
+
+ if (implType.IsProxy() || !implType.CanInherited())
+ {
+ return false;
+ }
+ if (_excepts.Any(x => implType.Name.Matches(x)) || _excepts.Any(x => implType.Namespace.Matches(x)))
+ {
+ return false;
+ }
+ if (!serviceType.CanInherited() || serviceType.IsNonAspect())
+ {
+ return false;
+ }
+
+ var aspectValidator = new AspectValidatorBuilder(aspectConfiguration).Build();
+ if (!aspectValidator.Validate(serviceType, true) && !aspectValidator.Validate(implType, false))
+ {
+ return false;
+ }
+ return true;
+ }
+
+ }
+}
diff --git a/extras/src/AspectCore.Extensions.LightInject/LightInjectServiceResolver.cs b/extras/src/AspectCore.Extensions.LightInject/LightInjectServiceResolver.cs
new file mode 100644
index 00000000..a24bc4a4
--- /dev/null
+++ b/extras/src/AspectCore.Extensions.LightInject/LightInjectServiceResolver.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using AspectCore.DynamicProxy;
+using AspectCore.Injector;
+using LightInject;
+using IServiceContainer = LightInject.IServiceContainer;
+
+namespace AspectCore.Extensions.LightInject
+{
+ [NonAspect]
+ internal class LightInjectServiceResolver : IServiceResolver
+ {
+ private readonly IServiceContainer _container;
+
+ public LightInjectServiceResolver(IServiceContainer container)
+ {
+ _container = container;
+ }
+
+ public object GetService(Type serviceType)
+ {
+ return _container.TryGetInstance(serviceType);
+ }
+
+ public void Dispose()
+ {
+ _container.Dispose();
+ }
+
+ public object Resolve(Type serviceType)
+ {
+ return _container.TryGetInstance(serviceType);
+ }
+ }
+}
diff --git a/extras/test/AspectCore.Extensions.LightInject.Test/AspectCore.Extensions.LightInject.Test.csproj b/extras/test/AspectCore.Extensions.LightInject.Test/AspectCore.Extensions.LightInject.Test.csproj
new file mode 100644
index 00000000..78c1bfec
--- /dev/null
+++ b/extras/test/AspectCore.Extensions.LightInject.Test/AspectCore.Extensions.LightInject.Test.csproj
@@ -0,0 +1,22 @@
+
+
+
+ netcoreapp2.0
+ portable
+ true
+ false
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/extras/test/AspectCore.Extensions.LightInject.Test/AsyncIncreamentAttribute.cs b/extras/test/AspectCore.Extensions.LightInject.Test/AsyncIncreamentAttribute.cs
new file mode 100644
index 00000000..b335a04b
--- /dev/null
+++ b/extras/test/AspectCore.Extensions.LightInject.Test/AsyncIncreamentAttribute.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Threading.Tasks;
+using AspectCore.DynamicProxy;
+
+namespace AspectCoreTest.LightInject
+{
+ [AttributeUsage(AttributeTargets.Method)]
+ public class AsyncIncreamentAttribute : AbstractInterceptorAttribute
+ {
+ public override async Task Invoke(AspectContext context, AspectDelegate next)
+ {
+ await context.Invoke(next);
+ await Task.Delay(100); // 此处模拟一个真.异步方法,用于测试线程上下文切换
+
+ if (context.ReturnValue is Task task)
+ {
+ var result = await task;
+ context.ReturnValue = Task.FromResult(result + 1);
+ }
+ else if (context.ReturnValue is ValueTask valueTask)
+ {
+ var result = await valueTask;
+ context.ReturnValue = new ValueTask(result + 1);
+ }
+ else if (context.ReturnValue is int result)
+ {
+ context.ReturnValue = result + 1;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/extras/test/AspectCore.Extensions.LightInject.Test/AsyncInterceptorTests.cs b/extras/test/AspectCore.Extensions.LightInject.Test/AsyncInterceptorTests.cs
new file mode 100644
index 00000000..d9e8456f
--- /dev/null
+++ b/extras/test/AspectCore.Extensions.LightInject.Test/AsyncInterceptorTests.cs
@@ -0,0 +1,105 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using AspectCore.Extensions.LightInject;
+using LightInject;
+using Xunit;
+
+namespace AspectCoreTest.LightInject
+{
+ public class AsyncService
+ {
+ [AsyncIncreament]
+ public virtual void DonotGet(int num)
+ {
+ }
+
+ [AsyncIncreament]
+ public virtual Task DonotGetAsync(int num)
+ {
+ return Task.CompletedTask;
+ }
+
+ [AsyncIncreament]
+ public virtual int Get(int num)
+ {
+ return num;
+ }
+
+ [AsyncIncreament]
+ public virtual async Task GetAsyncWithTask(int num)
+ {
+ await Task.Delay(100);
+ return num;
+ }
+
+ [AsyncIncreament]
+ public virtual async ValueTask GetAsyncWithValueTask(int num)
+ {
+ await Task.Delay(100);
+ return num;
+ }
+ }
+
+ public class AsyncMethodTests
+ {
+ public static IEnumerable