dotnetcore / aspectcore-framework Goto Github PK
View Code? Open in Web Editor NEWAspectCore is an AOP-based cross platform framework for .NET Standard.
License: MIT License
AspectCore is an AOP-based cross platform framework for .NET Standard.
License: MIT License
如题,集成 Autofac 后,AspectCore 无法激活控制器操作方法一级的拦截器。
其它层级的拦截器不受影响;
如果不采用 Autofac,全部使用 asp.net core 原生 DI,则无此问题。
public ValuesController(ISampleRepository repo) { }
[FromContainer]
public ISampleRepository repos { get; set; }
其中构造函数可以获取到注入的ISampleRepository 对象,但是带有[FromContainer]的属性获取不到,
是使用的姿势不对吗,sample当中没有找到FromContainer的例子,只有在文档看到.
在dotnetcore2.0使用会报错
Need better document.
以下方法:
[WrapperInterceptor]
public virtual dynamic DoSomeThing(){
return DoSomeInterestingThingAsync();
}
其中 DoSomeInterestingThingAsync
返回 Task<Lemon>
类型的结果。
由于方法返回的是 dynamic,所以 AspectCore 会将其识别为同步方法,而不去 await 它。
因此例中的 WrapperInterceptor
内,context.ReturnValue
返回的是 Task<Lemon>
。如果我要将这个 Task<T>
内的 Lemon 取出然后包装进一个包装类中,填回 ReturnValue 的话,需要判断这个 Task 是否完成执行。
这个等待的工作使用者来完成,还是由 AspectCore 内部来完成?
Should not use hashcode as the key of dictionary.
考虑如下的一个类:
public class WeiboClient
{
public Account Account { get; internal set; }
protected internal IHttpService HttpService { get; set; }
protected internal WeiboSettings Settings { get; }
// 调用这个构造函数需要再执行Init进行二段构造
public WeiboClient(WeiboSettings settings)
{
Settings = settings ?? throw new ArgumentNullException(nameof(settings));
}
public WeiboClient(string username, string password, WeiboSettings settings, IHttpService httpService = null) : this(settings)
{
Init(username, password, httpService);
}
public void Init(string username, string password, IHttpService httpService = null)
{
Account = new Account(username, password, AccountType.Weibo);
HttpService = httpService ?? new HttpService();
}
// ReLoginAndRetry是一个继承了InterceptorAttribute的类
[ReLoginAndRetry]
public virtual async Task<ActionEvent> PostPicWeibo(string text, ICollection<string> picUrls = null)
{
// do something
}
}
然后将该类注册到依赖注入容器,WeiboSettings也注入。
于是该类将会被注册到AspectCore的代理类容器中(就是会调用CreateClassProxyType方法产生代理类型)
如果此时使用DI容器获取WeiboClient对象,那么将会调用第二个构造函数。
——————————————
而如果该类没有被加入到AspectCore的代理类容器中(例如将PostPicWeibo方法的virtual去掉),如果此时使用DI容器获取WeiboClient对象,那么将会调用第一个构造函数。
这个行为不一致的现象导致了一些问题,不知应如何解决,还望repo主指点。
我目前的临时解决方法是注释掉第二个构造函数
after download and compile the latest version "AspectCore.Extensions.DependencyInjection" solution,there is the error such as “CS0246 未能找到类型或命名空间名“AspectCoreOptions”(是否缺少 using 指令或程序集引用?)”.
I think the version from nuget server is not the latest, please confrim it, thank you.
调用MethodInfo
应该使用Callvirt
还是使用Call
,应该由MethodInfo
类型决定,而不应该通过参数指定。
而且在缓存时,也会减少枚举在比较时装箱与拆箱。
具体来说就是AsyncAspectTests这个测试类的Async_Test测试用例,原本的代码有笔误,改正之后这个用例就不通过了
[Fact]
public void Async_Test()
{
var proxy = ProxyGenerator.CreateClassProxy<FakeAsyncClass>();
var result = proxy.Async(100); // 此处原本误写作DynAsync
Assert.IsAssignableFrom<Task<int>>(result);
}
以下是被代理方法
[AsyncTestInterceptor]
public virtual Task Async(int value)
{
return Task.Run<int>(async () =>
{
await Task.Delay(value);
return value;
});
}
以下是测试输出:
Assert.IsAssignableFrom() Failure
Expected: typeof(System.Threading.Tasks.Task)
Actual: typeof(System.Threading.Tasks.Task)
最新的预览发行版没有修改AspectCore.Extensions.DependencyInjection,好多以前的代码跑不通了.对了,在这里我怎么没有找到AspectCore.Extensions.DependencyInjection开源的项目代码呢,谢谢
当使用autofac作DI容器 给类做拦截器的时候 该类不能继承泛型接口
When I register any else services in MS DI container while adding Interceptors configuration,it would resolve the other types to the interceptor.
Details:
System.FormatException: Encountered an invalid type for a default value.
at System.Reflection.MdConstant.GetValue(MetadataImport scope, Int32 token, RuntimeTypeHandle fieldTypeHandle, Boolean raw)
at System.Reflection.RuntimeParameterInfo.GetDefaultValueInternal(Boolean raw)
at System.Reflection.RuntimeParameterInfo.get_HasDefaultValue()
at AspectCore.Utils.ProxyGeneratorUtils.ParameterBuilderUtils.DefineParameters(MethodInfo targetMethod, MethodBuilder methodBuilder)
at AspectCore.Utils.ProxyGeneratorUtils.MethodBuilderUtils.DefineMethod(MethodInfo method, String name, MethodAttributes attributes, Type implType, TypeDesc typeDesc)
at AspectCore.Utils.ProxyGeneratorUtils.MethodBuilderUtils.DefineInterfaceMethod(MethodInfo method, Type implType, TypeDesc typeDesc)
at AspectCore.Utils.ProxyGeneratorUtils.MethodBuilderUtils.DefineInterfaceProxyMethods(Type interfaceType, Type targetType, Type[] additionalInterfaces, TypeDesc typeDesc)
at AspectCore.Utils.ProxyGeneratorUtils.CreateInterfaceProxyInternal(String name, Type interfaceType, Type implType, Type[] additionalInterfaces, IAspectValidator aspectValidator)
at AspectCore.Utils.ProxyGeneratorUtils.CreateInterfaceProxy(Type interfaceType, Type implType, Type[] additionalInterfaces, IAspectValidator aspectValidator)
at AspectCore.DynamicProxy.ProxyTypeGenerator.CreateInterfaceProxyType(Type serviceType, Type implementationType)
at AspectCore.Extensions.DependencyInjection.ServiceCollectionBuildExtensions.MakeProxyService(ServiceDescriptor descriptor, Type implementationType, IProxyTypeGenerator proxyTypeGenerator)
at AspectCore.Extensions.DependencyInjection.ServiceCollectionBuildExtensions.BuildAspectCoreServiceProvider(IServiceCollection services)
My Code:
// ServiceAccessLogAttribute
public class ServiceAccessLogAttribute : AbstractInterceptorAttribute
{
public override async Task Invoke(AspectContext context, AspectDelegate next)
{
await next(context);
}
}
// Startup.ConfigureServices
services.AddTransient(provider => new ServiceAccessLogAttribute());
services.AddDynamicProxy(config =>
{
config.Interceptors.AddServiced<ServiceAccessLogAttribute>(method =>
{
var isService = method.DeclaringType.Name.EndsWith("Service");
return isService;
});
});
return services.BuildAspectCoreServiceProvider();
Project Info:
OS: Windows 10
dotnet --version: 2.1.104
Target: netcoreapp2.0
AspectCore.Extensions.DependencyInjection: 0.3.1
public static class AspectContextExtensions
{
public static HttpContext GetHttpContext(this AspectContext context)
{
object value;
if (context.Items.TryGetValue("HttpContext", out value))
{
return (HttpContext)value;
}
var controller = context.Target.ImplementationInstance as Controller;
if (controller != null)
{
context.Items["HttpContext"] = controller.HttpContext;
return controller.HttpContext;
}
return null;
}
}
在属性的get或set标记方法拦截器即为该属性拦截器?或者重写定义属性拦截器接口?
如果一个接口注册了多个实现,在注入的时候aspectcore是怎么选择一个实现的?
I defind both Interface and it's Implementation class in a TestClass, then it resolve a origin instance but not a dynamic proxy.
有如下Interceptor属性,将其添加在一个返回int的方法上。
在调用await context.Invoke(next)之后,context.ReturnValue依然是null。
环境:dotnet --version 2.1.103,aspectcore版本为0.3.1,di容器为Windsor
不知是不是我的使用方式不对。
public class InterceptorAttribute : AbstractInterceptorAttribute
{
public override async Task Invoke(AspectContext context, AspectDelegate next)
{
await context.Invoke(next);
var returnValue = context.ReturnValue;
Assert.NotNull(returnValue);
}
}
public interface IService
{
[Interceptor]
int Get();
}
你好,现在代码重构了很多了,使用样例还没有哦,都等待了一个星期了呢
Nuget下载的版本为v0.2.4
源码如下:
using AspectCore.Configuration;
using AspectCore.DynamicProxy;
using AspectCore.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AspectCoreDemo1
{
class Program
{
static void Main(string[] args)
{
RegisterAop();
TestAop test = new TestAop();
test.CallValue("今天是周三");
Console.ReadLine();
}
private static void RegisterAop()
{
IServiceCollection services = new ServiceCollection();
services.AddDynamicProxy(config =>
{
config.Interceptors.AddServiced<ExceptionLogAttribute>(Predicates.ForNameSpace("AspectCoreDemo1.*")); //拦截所有AspectCoreDemo及其子命名空间下面的接口或类
});
}
}
/// <summary>
/// 记录异常日志
/// </summary>
public class ExceptionLogAttribute : AbstractInterceptorAttribute
{
public override Task Invoke(AspectContext context, AspectDelegate next)
{
try
{
Console.WriteLine("Before service call");
return next(context);
}
catch (Exception ea)
{
Console.WriteLine($"出现异常!异常信息:{ea.Message}");
throw ea;
}
finally
{
Console.WriteLine("After service call");
}
}
}
[ExceptionLog]
public class TestAop
{
public virtual void CallValue(string value)
{
Console.WriteLine($"我就是CallValue({nameof(value)})执行的,value:{value}");
}
}
}
it will be greater if you add docs as well as some examples
非常感谢这次的更新。这个版本的代码,层次清晰,紧凑,很棒。但是样例太少了。比如字段注入,属性注入,方法注入,参数注入,返回值注入的样例太少,以至于不知道怎么使用了,只知道FromServices是对参数的注入,其他的不知道怎么使用。能抽空做个样例项目不,谢谢
一、假如接口IB继承IA,类C实现并注册IB,如果IB中的方法没有加入拦截器,则IA中的拦截器会无效
二、似乎不支持方法的参数或者返回值是Tuple或者ValueTuple,注册拦截器时会发生异常
针对 #49 的情况,AspectCore 能否提供 context.IsAsync
、context.AsyncReturnValue
这样的 API?
如果 AspectCore 可以提供这样的 API,那么需要考虑以下几种情况:
so, is there support for static method ?
有如下拦截器:
[AttributeUsage(AttributeTargets.Method)]
public class AwaitBeforeInvokeAttribute : AbstractInterceptorAttribute
{
public override async Task Invoke(AspectContext context, AspectDelegate next)
{
await Task.Delay(100);
var output = context.ServiceProvider.GetRequiredService<ITestOutputHelper>();
if (context.Proxy is AwaitBeforeInvokeTester tester
&& tester.Cts.IsCancellationRequested)
{
context.ReturnValue = Task.FromResult(-1);
return;
}
output.WriteLine("Before Invoke Next");
await context.Invoke(next);
output.WriteLine("After Invoke Next");
}
}
// 被代理类
public class AwaitBeforeInvokeTester
{
[DoNotWire]
public CancellationTokenSource Cts { get; set; } // 增加这个为了让无限调用退出
[AwaitBeforeInvoke]
public virtual async Task<int> ExecuteAsync()
{
await Task.Delay(100);
return 1;
}
}
使用windsor时,会输出多次"Before Invoke Next",直到CancellationTokenSource取消。
我用相同的方式测试了autofac和msdi,输出符合预期,所以应该是windsor的相关适配存在问题
具体测试用例可以参考:https://github.com/huoshan12345/AspectCore-Framework/blob/infinite_invoke_in_windsor/extras/test/AspectCore.Extensions.Windsor.Test/AwaitBeforeInvokeNextTests.cs
我做一个AspectCore与Castle.Core的测试,分别针对同步方法与异步方法,可是执行的速度相差很大,不知道为什么,是不是使用方式有问题,求解。
我测试的结果如下:
Method | Mean | Error | StdDev |
---|---|---|---|
Create_By_Castle_Core_Async | 78.28 ns | 1.5851 ns | 1.6278 ns |
Create_By_AspectCore_Async | 386.44 ns | 7.7314 ns | 7.2319 ns |
Create_By_Castle_Core | 48.40 ns | 0.8871 ns | 0.8298 ns |
Create_By_AspectCore | 352.03 ns | 5.2261 ns | 4.8885 ns |
考虑支持 ref return方法的代理 dotnet/roslyn#118
static ref int Max(ref int first, ref int second, ref int third)
{
ref int max = first > second ? ref first : ref second;
return max > third ? ref max : ref third;
}
AspectCoreLite
提供IProxyActivator
接口和它的默认实现ProxyActivator
来创建目标类型的代理类。在一个基本的切面中,我们需要定义或者生成Interceptor
Target
Proxy
PointCut
和JoinPoint
。
在Interceptor
系统中,框架定义了IInterceptor
接口,它声明了一个返回值为Task
的异步执行方法:
namespace AspectCore.Lite.Abstractions
{
public interface IInterceptor
{
Task ExecuteAsync(IAspectContext aspectContext, InterceptorDelegate next);
}
}
然而在一般情况下可以使用另一个抽象的InterceptorAttribute
自定义特性类,它实现IInterceptor
接口。AspectCoreLite
默认实现了基于Attribute
的PointCut
。我们的自定义拦截器看起来像下面这样:
public class CustomInterceptorAttribute: InterceptorAttribute
{
public override async Task ExecuteAsync(IAspectContext aspectContext , InterceptorDelegate next)
{
Console.WriteLine("Before target call");
try
{
await next(aspectContext);
}
catch (Exception)
{
Console.WriteLine("Target threw an exception!");
throw;
}
finally
{
Console.WriteLine("After target call");
}
}
}
执行next(aspectContext)
意味着你可以继续调用下一个拦截器或原始的目标方法。
接下来我们定义Target
类,声明一个标记了CustomInterceptorAttribute
自定义特性的虚方法:
public class Target
{
[CustomInterceptor]
public virtual void Call()
{
Console.WriteLine("target calling...");
}
}
最后,我们使用默认的构造器创建ProxyActivator
的实例,并且调用CreateClassProxy
方法创建Target
类的运行时代理:
IProxyActivator proxyActivator = new ProxyActivator();
Target proxy = proxyActivator.CreateClassProxy<Target>(new Target());
proxy.Call();
输出:
Before target call
target calling...
After target call
Test code:
using System;
using System.Threading.Tasks;
using AspectCore.DynamicProxy;
namespace Program
{
public class Program
{
public class Intercept : AbstractInterceptorAttribute
{
public override Task Invoke(AspectContext context, AspectDelegate next)
{
context.Parameters[0] = "lemon";
return context.Invoke(next);
}
}
public interface IService
{
Task<string> GetValue(string val);
}
public class Service : IService
{
[Intercept]
public async Task<string> GetValue(string val)
{
await Task.Delay(3000);
return val;
}
}
static void Main(string[] args)
{
var builder = new ProxyGeneratorBuilder();
builder.Configure(_ => { });
var proxyGenerator = builder.Build();
var proxy = proxyGenerator.CreateInterfaceProxy<IService, Service>();
// IService proxy = new Service();
var val = proxy.GetValue("le");
Console.WriteLine("should return immediately");
Console.WriteLine(val.Result);
}
}
}
Should print "should return immediately" immediately after GetValue, then sleep 3s and print the final result.
Problem lines:
Reference:
https://stackoverflow.com/questions/17284517/is-task-result-the-same-as-getawaiter-getresult
如题。
当我需要对返回结果进行操作的时候,如果返回结果是个Task,例如Task之类的,就需要一个封装方法了。
我自己在实现一个缓存接口的时候实现了一个,凑合能用,不知道你们这边有没有更好的实现可以用。
示例代码如下:
var cacheValue = await DistributedCache.GetAsync(cacheKey);
if (cacheValue != null)
{
using (var ms = new MemoryStream(cacheValue))
{
var formatter = new BinaryFormatter();
var result = formatter.Deserialize(ms);
if (context.IsAsync())
{
dynamic member = context.ServiceMethod.ReturnType.GetMember("Result")[0];
dynamic temp = System.Convert.ChangeType(result, member.PropertyType);
context.ReturnValue = System.Convert.ChangeType(Task.FromResult(temp), context.ServiceMethod.ReturnType);
}
else
{
context.ReturnValue = System.Convert.ChangeType(result, context.ServiceMethod.ReturnType);
}
}
return;
}
Transient
(no cache) , Singleton
(global cache) or Scoped
?As this issue's title said, when mapping to TDestination, it will be able to automatically inject the dependencies in constructor.
thx. 💌
添加性能追踪api,方便从外部调试和监控DynamicProxy创建代理类和执行方法拦截的性能
IAspectContext 引用哪个包,麻烦说明
The StackTrace is here:
at AspectCore.Extensions.Reflection.Emit.ILGeneratorExtensions.EmitConstant(ILGenerator ilGenerator, Object value, Type valueType)
at AspectCore.Extensions.Reflection.CustomAttributeReflector.CreateInvoker()
at AspectCore.Extensions.Reflection.CustomAttributeReflector..ctor(CustomAttributeData customAttributeData)
at AspectCore.Extensions.Reflection.CustomAttributeReflector.<>c.<Create>b__11_0(CustomAttributeData data)
at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
at System.Linq.Enumerable.SelectIListIterator`2.ToArray()
at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
at AspectCore.Extensions.Reflection.MemberReflector`1..ctor(TMemberInfo reflectionInfo)
at AspectCore.Extensions.Reflection.MethodReflector..ctor(MethodInfo reflectionInfo)
at AspectCore.Extensions.Reflection.MethodReflector.<>c__DisplayClass0_0.<Create>g__CreateInternal0(Pair`2 item)
at AspectCore.Extensions.Reflection.ReflectorCacheUtils`2.<>c__DisplayClass1_0.<GetOrAdd>b__0(TMemberInfo k)
at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
at AspectCore.Extensions.Reflection.MethodReflector.Create(MethodInfo reflectionInfo, CallOptions callOption)
at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
at AspectCore.DynamicProxy.RuntimeAspectContext.Complete()
at Shriek.ServiceProxy.Tcp.Client.TcpServiceClient.<Invoke>d__35.MoveNext() in E:\项目\myproject\ShriekFx\src\Shriek.ServiceProxy.Tcp\Client\TcpServiceClient.cs:line 128
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at AspectCore.DynamicProxy.AspectActivator.InvokeTask[TResult](AspectActivatorContext activatorContext)
at AspectCore.DynamicGenerated.TcpTestService.Test(String sth)
at Shriek.Samples.WebApiProxy.Program.Main(String[] args) in E:\项目\myproject\ShriekFx\samples\Shriek.Samples.WebApiProxy\Program.cs:line 101
我在Startup.cs中修改了ConfigureServices方法,如下:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddMvc().AddControllersAsServices();
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterDynamicProxy(config =>
{
//自定义拦截器 config.Interceptors.AddTyped<CustomInterceptorAttribute(Predicates.ForNameSpace("aoptest.Services"));
//排除Quer开头的方法
config.NonAspectPredicates.AddMethod("Quer*");
});
builder.Populate(services);
var dataAccess = Assembly.GetExecutingAssembly();
builder.RegisterAssemblyTypes(dataAccess)
.Where(t => t.Name.EndsWith("Service"))
.AsImplementedInterfaces();
var container = builder.Build();
return new AutofacServiceProvider(container);
}
拦截器CustomInterceptorAttribute的定义代码:
public class CustomInterceptorAttribute : AbstractInterceptor
{
public override async Task Invoke(AspectContext context, AspectDelegate next)
{
await next(context);
//方法名
Console.WriteLine("执行方法: "+ context.ImplementationMethod.Name);
}
}
要拦截的业务层代码:
public class UserService : IUserService
{
public string AddUser()
{
return "AddUser()";
}
public string QueryName()
{
return "QueryName()";
}
public string SayName()
{
return "SayName()";
}
}
在Controller中调用,后台打印如下:
可以看出
config.NonAspectPredicates.AddMethod("Quer*");并没有起作用。
services.AddScoped<IAsyncResult>(serverProvider =>
{
// 这里这个serviceProvider 是否是Aspect 的provider, 是否拥有属性拦截
var t = serverProvider.GetRequiredService<CookieOptions>();
return Task.FromResult(t);
});
目前我还处于研究阶段,还没怎么用过这个框架, 主要是担心这个问题。
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.