PostSharp 这个静态植入的aop框架我就不多说了,在以前的aop文件,我们也尝试用MSBuild+Mono.Cicel理解静态植入AOP的原理。最近公司准备购买Postsharp做一些AOP,减少开发是代码量,至于选择AOP相信也不用多说。我也在今天重新了解了些Postsharp最新版更新,这阵子的博客更新也少了,所以准备在MSBuild+Mono.Cicel的基础上再一些Postsharp系列。今天既然是初见,那么我们就从最简单的OnExceptionAspect开始。
一:OnExceptionAspect
起定义如下:

先写Aspect Attribute:
[Serializable]
public
class ExceptionAspectDemoAttribute : OnExceptionAspect
{
public
override
void OnException(MethodExecutionArgs args)
{
var msg =
string.Format(
"
时间[{0:yyyy年MM月dd日 HH时mm分}]方法{1}发生异常: {2}\n{3}
", DateTime.Now, args.Method.Name, args.Exception.Message, args.Exception.StackTrace);
Console.WriteLine(msg);
args.FlowBehavior = FlowBehavior.Continue;
}
public
override Type GetExceptionType(System.Reflection.MethodBase targetMethod)
{
return
typeof(NullReferenceException);
}
}
注意Postsharp的Aspect都需要标记为可序列化的,因为在编译时会为我们二进制序列化为资源,减少在运行是的开销,这个将在后面专门讲。
上面的code继承至OnExceptionAspect,并且override OnException和GetExceptionType,GetExceptionType为我们需要处理的特定异常。OnException为异常处理决策方法。我们的异常处理决策是当NullReferenceException时候我们会记录日志,并且方法指定继续(args.FlowBehavior = FlowBehavior.Continue)。
看看我们的测试代码:
View Code
很显然我们的两个方法抛出了null异常和自定义异常,预期是NullReferenceException会被扑捉,而自定义异常会中断,运行效果如下:
我们在来看看postsharp为我们做了什么,当然是反编译看看:

二:Postsharp的Multicasting
1:Multicasting class:
在这随便也说一下postsharp的Multicasting,多播这样翻译感觉有点死板呵呵,理解就行。利用这一点我们可以吧我们的aspect放在class,assembly等目标上匹配我们的多个目标。比如现在我们不想在我们的每个方法上加attribute,那我们可以选择在class上,如:

反编译,同样注入了我们每个方法:

2:Multicasting assembly:
我们同样可以利用
[assembly: PostSharpDemo.ExceptionAspectDemoAttribute()]
标记在我们的程序集上。
3:AttributeExclude:
但是注意这样也标记了我们的aspect,某些时候可能会导致堆栈溢出 ,我们可以用AttributeExclude=true来排除。
同时我们也可以设置应用目标:AttributeTargetMemberAttributes是一个枚举类型,定义如下:

比如我们需要过滤编译时候生成的目标(自动属性,action等等),
[assembly: PostSharpDemo1.MethodTraceAspect(AttributeExclude =
true, AttributePriority =
0, AttributeTargetMemberAttributes = MulticastAttributes.CompilerGenerated)]
4:AttributePriority:
还有AttributePriority,我们可以设置编译时优先级。如果我们对目标标记了多个aspect,这样postsharp就不确定注入先后顺序,这样不能确保正确性,在vs编译时候我们会看见警告:Their order of execution is undeterministic.

这是时候AttributePriority就派上用途了来决定我们植入的先后优先级。
5:其他匹配
同上AttributeTargetMemberAttributes 我们还可以利用AttributeTargetMembers,AttributeTargetTypes进行目标名称的匹配,支持模糊匹配。
我们了解了简单的OnExceptionAspectAOP面向方向切入,在第一节中我们将继续我们的PostSharp AOP系列的OnMethodBoundaryAspect方法行为的切入,这也是我们常用的AOP切入。
OnMethodBoundaryAspect顾名思义其为对方法边界的切入,定义如下:

在这里提供了四个方法边界点为我们切入。我们可以很轻松的对方法权限,执行时间,参数合法性等aspect。
aspect传入参数MethodExecutionArgs给我如下信息,同时还包括父类AdviceArgs的Instance属性,实例方法才有值,静态方法则为null,

这里还需要说一下属性FlowBehavior:表示方法执行行为,是一个枚举变量:

二:执行时间统计demo
下面我们实践一个方法执行时间统计demo:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PostSharp.Aspects;
namespace PostSharpDemo
{
[Serializable]
public
class OnMethodBoundaryAspectDemoAttribute : OnMethodBoundaryAspect
{
public
bool Enabled
{
get;
set;
}
public
override
void OnEntry(MethodExecutionArgs args)
{
if (
this.Enabled)
{
args.MethodExecutionTag = System.Diagnostics.Stopwatch.StartNew();
}
}
public
override
void OnExit(MethodExecutionArgs args)
{
if (
this.Enabled)
{
var sw = args.MethodExecutionTag
as System.Diagnostics.Stopwatch;
if (sw !=
null)
{
sw.Stop();
Console.WriteLine(String.Format(
"
方法{0}执行时间为:{1}s
", args.Method.Name, sw.ElapsedMilliseconds /
1000));
sw =
null;
}
}
}
}
}
测试方法:
[OnMethodBoundaryAspectDemoAttribute(Enabled=
true)]
public
static
void OnMethodBoundaryAspectDemoAttributeTest()
{
System.Threading.Thread.Sleep(
2000);
}
结果如下:

注:这里我们也可以用到我们上节说的 多播(Multicasting)加到我们的class,assembly上统计我们所有的方法。
在最后在废话一句,我们可以很轻松的指定我们的方法(比如使我们的wcf服务操作契约)的访问权限,比如基于操作权限的功能点function的处理,如[PowerAttribute(“Add,Edit”)]这样简单处理,我们只需要在OnEnter中aspect,决定方法FlowBehavior行为,剩下的事情教给大家自己实践。
欢迎大家积极指正和多多交流。
介绍了OnExceptionAspect和OnMethodBoundaryAspect ,在这节我们将继续了解MethodInterceptionAspect,他为我们提供了关于方法处理的AOP切入,不同于OnMethodBoundaryAspect,他不是边界,是方法体。有了我们可以在我们的方法切入aspect很多有用的信息,比如将同步方法变为异步,防止多次点击重复提交,winform,wpf的多线程调用UI(参见PostSharp - Thread Dispatching(GUI多线程)),长时间操作在超过用户接受时间弹出进度条等等有用的关于用户体验和业务逻辑功能,简化我们的编程开发。
同样我们先来看看其MethodInterceptionAspect定义:

Invoke MethodInterceptionArgs参数:

我们一般使用Proceed是的方法进行处理。在这时我们可以加入线程池调用,使的其变为异步操作。
同时MethodInterceptionAspect 还继承了MethodLevelAspect 的CompileTimeValidate编译是验证,CompileTimeInitialize编译时初始化,RuntimeInitialize运行时初始化,后边的初始化我们将在后面一节PostSharp范围(static和instance中讲到)。
其定义很简单,在于我们的发挥:
二:防止多次提交处理demo:
我们这里只采用简单思路在方法进入禁止按钮可用,方法执行完成后恢复可用状态。我们将使监听winform事件处理方法,按钮来自EventHandle的第一个参数Sender。
[Serializable]
public
class UnMutipleTriggerAttribute : MethodInterceptionAspect
{
public
override
bool CompileTimeValidate(System.Reflection.MethodBase method)
{
var ps = method.GetParameters();
if (ps !=
null && ps.Count() >
0 && ps[
0].Name ==
"
sender
")
return
true;
return
false;
}
public
override
void OnInvoke(MethodInterceptionArgs args)
{
if (args.Arguments.Count >
0)
{
var controls = args.Arguments[
0]
as System.Windows.Forms.Control;
if (controls !=
null && controls.Enabled)
{
controls.Enabled =
false;
args.Proceed(); ;
controls.Enabled =
true;
}
}
}
}
在这里我们是监听方法的处理事件函数根据vs自动生成规则,第一个参数是sender,事件源,这里利用了CompileTimeValidate在编译时决定是否注入aspect。
注意这里只是一个简单的demo,只针对于同步操作,如要变为异步操作,这需要改为在异步操作后启用。
测试在button点击方法加上attribute:
[UnMutipleTriggerAttribute]
private
void Save(
object sender, EventArgs e)
{
System.Threading.Thread.Sleep(
2000);
}
效果:

这个例子很简单的就完成了。
如果我们了解wpf或者silverlight开发中的MVVM模式,就知道框架要求我们的ViewModel必须实现INotifyPropertyChanged,来得到属性改变的事件通知,更新UI。
实现INotifyPropertyChanged接口很简单,而且一沉不变,属于重复劳动。在这里我们将看看如何运用PostSharp来解决我们的重复劳动。当然这里只是一个demo演示,具体在项目开发中你直接实现INotifyPropertyChanged,或者AOP植入,这取决我个人和团队文化。
在下面我们将会用到Postsharp的:
- IntroduceMember:向目标对象植入成员。
- IntroduceInterface:使得目标实现接口,参数接口类型。
- OnLocationSetValueAdvice:PostSharp的一种Advice Aspect。
这节我们要讨论的是PostSharp的LocationInterceptionAspect,PostSharp官方把Property和Field成为Location。所以LocationInterceptionAspect就是为了实现Property和Field的拦截。在我们前面讨论了关于方法OnMethodBoundaryAspect的aspect,我们很容易想到,在c#中Property就是一个编译时分为Get和Set两个方法,对于property的aspect就类似于了我们的Method的aspect。而对于Field的aspect同样可以转换为对Property的aspect。
下面我们用反编译工具来证实一下我的说法.
代码:
[LazyLoad(
"
test
",
"
test
")]
private
string TestField;
编译后:

我们在来看看LocationInterceptionAspect定义:

其OnGetvalue和OnSetValue是我们主要拦截的方法,起参数LocationInterceptionArgs定义:

同样给也拥有来自父类AdviceArgs的Instance对象,对于对象级Location为所在对象,静态则为null;
LocationInterceptionAspect的使用方法和我们的OnMethodBoundaryAspect和类似,使用方式也一样,对于使用对不重要,鄙人觉得更重要的是我们的设计思想。
我暂时能想到的很好的LocationInterceptionAspect使用场景则是LazyLoad,对于3.5表达式的出现,我们到处都可以简单这个词,在c#类库中也加入了这个类。
这里我们只是做一个简单的演示demo,根据attribute上制定的类型的方法延时加载对象,废话不说了上code:
[Serializable]
public
class LazyLoadAttribute : LocationInterceptionAspect
{
public
string MethodName
{
get;
private
set;
}
public
string PrivoderFullName
{
get;
private
set;
}
public LazyLoadAttribute(
string MethodName,
string PrivoderFullName)
{
Green.Utility.Guard.ArgumentNotNullOrEmpty(MethodName,
"
MethodName
");
Green.Utility.Guard.ArgumentNotNullOrEmpty(PrivoderFullName,
"
PrivoderFullName
");
this.MethodName = MethodName;
this.PrivoderFullName = PrivoderFullName;
}
public
override
void OnGetValue(LocationInterceptionArgs args)
{
if (args.GetCurrentValue() ==
null)
{
Console.WriteLine(
"
Loading....
");
var value =
this.LoadProperty(args.Instance);
if (value !=
null)
{
args.Value = value;
args.ProceedSetValue();
}
}
args.ProceedGetValue();
}
private
object LoadProperty(
object p)
{
var type = Type.GetType(
this.PrivoderFullName);
//
具体加载程序集需要自定义需求,这里仅为了测试简化。
if (type !=
null)
{
var method = type.GetMethod(
this.MethodName);
if (method !=
null)
{
object[] ps =
null;
if (p !=
null)
{
ps =
new
object[] { p };
}
object entity =
null;
if (!method.IsStatic)
{
entity = System.Activator.CreateInstance(type);
}
return method.Invoke(entity, ps);
}
}
return
null;
}
}
测试code:
class Program
{
static
void Main(
string[] args)
{
/*
* demo4
*/
Student stu =
new Student();
stu.ID =
10;
Console.WriteLine(stu.Name);
Console.WriteLine(stu.Name);
Console.WriteLine(Student.TestStaticProperty);
Console.WriteLine(Student.TestStaticProperty);
Console.Read();
}
public
static
string TextLazyLoadStaticMenthod(Student stu)
{
return
"
Student
" + stu.ID;
}
public
string TextLazyLoadInstacnceMenthod(Student stu)
{
return
"
Student
" + stu.ID;
}
public
string TextLazyLoadStaticPropertyMenthod()
{
return
"
测试
";
}
}
public
class Student
{
//
[LazyLoad("TextLazyLoadStaticMenthod", "PostSharpDemo.Program,PostSharpDemo")]
[LazyLoad(
"
TextLazyLoadInstacnceMenthod
",
"
PostSharpDemo.Program,PostSharpDemo
")]
public
string Name
{
get;
set; }
public
string Sex
{
get;
set; }
[LazyLoad(
"
TextLazyLoadStaticPropertyMenthod
",
"
PostSharpDemo.Program,PostSharpDemo
")]
public
static
string TestStaticProperty
{
get;
set; }
public
int ID
{
get;
set; }
}
效果图片如下:

http://www.cnblogs.com/whitewolf/archive/2011/12/11/PostSharp5.html