昨天我们创建了切面并将它应用到了一个方法之上。PostSharp在如何选择切入点上非常灵活,这对我们开发人员来说是一大好事。想象一下我们的异常切面可以自动的加入到项目任何类中的任何方法上。对于记录异常日志该怎么样做呢?不久我们就会给新方法增加异常的日志记录功能。当然,实际的应用远比我们今天的例子复杂。但是,你同样还是能从这个例子中感受到postsharp的威力。
OnExceptionAspect是被设计用在方法上的,昨天我们就将它直接应用到了一个方法之上。但是现在需求边了,我们如何满足下面的需求呢:
1.我们需要在方法的多个阶段进行拦截
2.在每个类的每个方法上设置切面
如果我们手动的一个一个方法上面去添加attribute的话,肯定很麻烦。
OnMethodBoundaryAspect
我们可以使用PostSharp提供的另外一个方法OnMethodBoundaryAspect来实现上面提到的第一个需求。这个类提供了四个不同的方法,这四个方法允许我们在执行方法的不同阶段进行切面的拦截:
- OnEntry - 在方法执行之前拦截
- OnExit - 即便代码中有错误产生了,此方法也会在方法结束的时候被调用
- OnSuccess - 仅仅在方法执行完毕而且在这过程当中没有异常发生的情况下才会被调用
- OnException - 当方法发生异常时被调用
为了更好的理解这个方法,我们建立一个专门用来记录方法开始执行和方法执行完毕的切面。在项目中增加名为MethodTraceAspect.cs的文件,并加入以下代码:
1 [Serializable]
2 publicclassMethodTraceAspect:OnMethodBoundaryAspect
3 {
4 publicoverridevoidOnEntry(MethodExecutionArgs args)
5 {
6 Debug.WriteLine(args.Method.Name+" started");
7 }
8
9 publicoverridevoidOnExit(MethodExecutionArgs args)
10 {
11 Debug.WriteLine(args.Method.Name+" finished");
12 }
13 }
这个切面可以说是AOP中的“Hello world”,但是足够达到我们今天的目的了。可以想象当我们将这个切面应用到方法上面时程序就会打印出"started"和"finished"到输出窗口中。接着上一课的代码,我们将这个切面加到GetByName方法上面去。
1 [DatabaseExceptionWrapper]
2 [MethodTraceAspect]
3 publicIQueryableGetByName(string value)
4 {
5 var res = _contactStore.Where(c => c.FirstName.Contains(value)
6 || c.LastName.Contains(value));
7
8 if(res.Count()<1)
9 {
10 ThrowNoResultsException();
11 }
12
13 Thread.Sleep(3000);
14 return res.AsQueryable();
15 }
运行程序,你会发现输出窗口中多了以下一段文字(和我们预料的一样):
GetByName started
GetByName finished
类级别的声明
对于第二个需求,为了避免手动的将切面添加到每个方法之上,我们可以仅仅在类上只声明一次,然后PostSharp会自动的将切面添加到这个类中的所有方法上面去。让我们试试看。将GetByName上面的MethodTraceAspect移到InMemoryStore类上
1 [MethodTraceAspect]
2 internalclassInMemoryDataStore:PostSharpDemo1.IContactRepository
3 {
4 …
5 }
6
现在我们再次运行程序,我们可以在输出窗口中看到以下内容:
.cctor Started
InitializeData Started
set__contactStore Started
set__contactStore Finished
get__contactStore Started
get__contactStore Finished
get__contactStore Started
get__contactStore Finished
get__contactStore Started
get__contactStore Finished
get__contactStore Started
get__contactStore Finished
get__contactStore Started
get__contactStore Finished
get__contactStore Started
get__contactStore Finished
InitializeData Finished
.cctor Finished
.ctor Started
.ctor Finished
GetAll Started
get__contactStore Started
get__contactStore Finished
GetAll Finished
你可以注意到有许多调用方法我们在类中并没有声明。其实这是属性自动生成的getter和setter方法。当你的代码进行编译时,编译器会自动的将属性生成getter和setter方法。.cctor表明这是静态的构造函数调用,.ctor表明这是实例构造函数调用。
有趣的是,你本意可能不是将切面应用于所有的,甚至用不到的方法之上。对于这个问题我们有几个方法来避免,不过我们会在明天进行讨论。
总结
今天我们学习了另外一个切面,同时我们也学到了如何仅用一句话就将切面应用于多个方法之上。明天我们将学习更多的切面技术。
原文:http://www.sharpcrafters.com/blog/post/Day-2-Applying-Aspects-with-Multicasting-Part-1.aspx
译者注:有些地方可能并没有完全按照原文翻译,因为那样实在太拗口了。