📜 ⬆️ ⬇️

Some useful aspects for PostSharp

There are some serious AOP frameworks in the .net, but none of them “tax” as PostSharp . Being a big fan (as well as a user) of this framework, I want to present a few “recipes” to the community. I created some of them myself, found others on the Internet and adapted them to fit my needs. Here I will show some of the most "juicy" recipes. And if you are not familiar with the framework or ideology of AOP, I can recommend this webcast . So, let's begin?


Streams


The most “stereotypical” aspect in PostSharp is the aspect for invoking the tagged method in the pool. The aspect is very simple, but sometimes it is very useful:


')
public class WorkerThreadAttribute : OnMethodInvocationAspect<br/>
{<br/>
public override void OnInvocation(MethodInvocationEventArgs eventArgs)<br/>
{<br/>
ThreadPool.QueueUserWorkItem(state => eventArgs.Proceed());<br/>
}<br/>
}<br/>
It is interesting that you can call such code and get the return value without any type conversions, as when using QueueUserWorkItem directly.

Since we have the ability to “direct” the method to a specific thread, we can easily declaratively make the method run in a UI stream, as in WinForms ...

public class FormsThreadAttribute : OnMethodInvocationAspect<br/>
{<br/>
public override void OnInvocation(MethodInvocationEventArgs eventArgs)<br/>
{<br/>
Form f = (Form)eventArgs.Instance;<br/>
if (f.InvokeRequired)<br/>
f.Invoke(eventArgs.Delegate, eventArgs.GetArgumentArray());<br/>
else <br/>
eventArgs.Proceed();<br/>
}<br/>
}<br/>
... so in WPF:

public class WpfThreadAttribute : OnMethodInvocationAspect<br/>
{<br/>
private DispatcherPriority priority;<br/>
public WpfThreadAttribute() : this (DispatcherPriority.Normal) { }<br/>
public WpfThreadAttribute(DispatcherPriority priority)<br/>
{<br/>
this .priority = priority;<br/>
}<br/>
public override void OnInvocation(MethodInvocationEventArgs eventArgs)<br/>
{<br/>
DispatcherObject dispatcherObject = (DispatcherObject)eventArgs.Instance;<br/>
if (dispatcherObject.CheckAccess())<br/>
eventArgs.Proceed();<br/>
else <br/>
dispatcherObject.Dispatcher.Invoke(priority, new Action(eventArgs.Proceed));<br/>
}<br/>
}<br/>
In the latter case, we even have the opportunity to set the priority of execution.

Rewriting properties


In most cases, the logic inside the set / get methods is domain-specific, but there is one aspect that is sometimes useful to “pull out” - these are the notification notification settings - something called property change notification. In WinForms, we have the INotifyPropertyChanged interface INotifyPropertyChanged which sometimes is not so convenient to implement manually. Also, this interface works in WPF, but there is another alternative - the creation of a so-called. dependency property.

Both solutions can be automatically applied to ordinary CLR autoproperties. In both cases, the solutions are very difficult, so instead of the code I post links to the implementation of INotifyPropertyChanged and Dependency Properties . By the way, I’ve said that I used these snippets for the first time for quite a long time, and I’d hardly ever be able to use the “manual” implementation - unless they force the neofobes.

Lazy initialization


It may seem like a solution for the lazy, but lazy initialization can be done declaratively. Moreover, using PostSharp, you can save a lazy initialization of a property even when its value is taken from the IoC container. Here is a simple example - in it we will rewrite the field in our own way:

[Serializable]<br/>
class LazyLoad : OnFieldAccessAspect<br/>
{<br/>
private readonly Type type;<br/>
private readonly object [] args;<br/>
public LazyLoad(Type type, params object [] arguments)<br/>
{<br/>
this .type = type;<br/>
args = arguments;<br/>
}<br/>
public override OnFieldAccessAspectOptions GetOptions()<br/>
{<br/>
return OnFieldAccessAspectOptions.RemoveFieldStorage;<br/>
}<br/>
public override void OnGetValue(FieldAccessEventArgs eventArgs)<br/>
{<br/>
if (eventArgs.StoredFieldValue == null )<br/>
eventArgs.StoredFieldValue = Activator.CreateInstance(type, args);<br/>
eventArgs.ExposedFieldValue = eventArgs.StoredFieldValue;<br/>
}<br/>
}<br/>
Unfortunately, this code contains a not very neat implementation (the type of the object is transferred to the constructor), but on the other hand, this implementation provides additional flexibility. And of course, no one bothers to cause, say, My.IoCContainer.Resolve , bypassing the traditional DI model in which “everything at once” is issued instead of Activator.CreateInstance .

Attempts


Another classic pattern (or anti-pattern, for those who hate every kind of syntactic sugar) is an aspect that allows you to call a method and, in the event of an exception, try to call the method several times with a certain time interval.

[Serializable]<br/>
public class RetryAttribute : OnMethodInvocationAspect<br/>
{<br/>
public int Count { get; set; }<br/>
public int DelayMsec { get; set; }<br/>
public RetryAttribute( int count) : this (count, 0) { }<br/>
public RetryAttribute( int count, int delayMsec)<br/>
{<br/>
Count = count;<br/>
DelayMsec = delayMsec;<br/>
}<br/>
public override void OnInvocation(MethodInvocationEventArgs eventArgs)<br/>
{<br/>
for ( int a = 0; ; ++a)<br/>
{<br/>
try <br/>
{<br/>
if (a != 0 && DelayMsec > 0)<br/>
Thread.Sleep(DelayMsec);<br/>
eventArgs.Proceed();<br/>
return ;<br/>
} <br/>
catch <br/>
{<br/>
if (a == Count) throw ;<br/>
}<br/>
}<br/>
}<br/>
}<br/>
Using this attribute allows you to moderate the "whims" of the system. Also, its modified form can passively call the same function over and over again, aggregating the results of all calls. In my practice, this approach was useful, for example, when enumerating working SQL servers in a local network, because the first call to the search function certainly finds some servers, but alas, not all.

Exceptions


Exception handling is another aspect of development that can be made declarative. Here, for example, is the aspect that catches uncaught exceptions and issues a message in the box:

[Serializable]<br/>
public class ExceptionDialogAttribute : OnExceptionAspect<br/>
{<br/>
public override void OnException(MethodExecutionEventArgs eventArgs)<br/>
{<br/>
ExceptionMessageBox emb = new ExceptionMessageBox(<br/>
eventArgs.Exception,<br/>
ExceptionMessageBoxButtons.OK,<br/>
ExceptionMessageBoxSymbol.Error);<br/>
emb.Show(eventArgs.Instance as Form);<br/>
eventArgs.FlowBehavior = FlowBehavior.Continue;<br/>
}<br/>
}<br/>
Unlike the usual MessageBox I used the more beautiful and useful ExceptionMessageBox . The idea, in principle, is quite simple. And of course this aspect can be customized to ignore exceptions of a particular type altogether. For example, when initializing add-ins in Visual Studio, an exception of type ArgumentException is a common thing, since the studio sometimes tries to add menu items for commands that already exist. To “swallow” an exception, you can use the following aspect:

[Serializable]<br/>
public class IgnoreExceptionAttribute : OnExceptionAspect<br/>
{<br/>
private Type type;<br/>
public IgnoreExceptionAttribute(Type type)<br/>
{<br/>
this .type = type;<br/>
}<br/>
public override Type GetExceptionType(System.Reflection.MethodBase method)<br/>
{<br/>
return type;<br/>
}<br/>
public override void OnException(MethodExecutionEventArgs eventArgs)<br/>
{<br/>
eventArgs.FlowBehavior = FlowBehavior.Continue;<br/>
} <br/>
}<br/>
When creating websites on ASP.NET, I have a slightly different approach - I issue a “safe” page when an exception occurs, but I want the process of throwing out an exception (which, in fact, ASP.NET MVC system processes), it was intercepted and logged. For this, I use the CodePlex.Diagnostics framework and this simple aspect:

[Serializable]<br/>
public sealed class InterceptExceptionAttribute : OnExceptionAspect<br/>
{<br/>
public override void OnException(MethodExecutionEventArgs eventArgs)<br/>
{<br/>
IIdentity identity = WindowsIdentity.GetCurrent();<br/>
Guid guid = ExceptionProvider.Publish(eventArgs.Exception, identity);<br/>
throw new PublishedException(guid, eventArgs.Exception);<br/>
}<br/>
}<br/>

Tracing and logging


Since we are talking about logging, it’s a sin not to show any aspect for these purposes. Despite the fact that most developers have their own, individual approaches to this problem, I want to show a simple aspect that records the ins and outs of methods, as well as exceptions. For all these cases, the relevant information is displayed in the Output Studio window using a simple call to Debug.WriteLine :

[Serializable]<br/>
public sealed class LogAttribute : OnMethodBoundaryAspect<br/>
{<br/>
private static int indent;<br/>
private static int indentSize = 4;<br/>
private static string IndentString<br/>
{<br/>
get<br/>
{<br/>
return new string (Enumerable.Range(0, indentSize*indent).Select(n => n%4 == 0 ? '|' : ' ' ).ToArray());<br/>
}<br/>
}<br/>
private static void Indent() { ++indent; }<br/>
private static void Unindent() { if (indent > 0) --indent; }<br/>
private static void WriteLine( string s) { Debug.WriteLine(IndentString + s); }<br/>
public override void OnEntry(MethodExecutionEventArgs eventArgs)<br/>
{<br/>
StringBuilder sb = new StringBuilder();<br/>
sb.AppendFormat( "Entering {0} with arguments" , eventArgs.Method);<br/>
object [] args = eventArgs.GetReadOnlyArgumentArray() ?? new object [] {};<br/>
foreach (var arg in args)<br/>
{<br/>
sb.AppendFormat( " <{0}>" , arg);<br/>
}<br/>
WriteLine(sb.ToString());<br/>
Indent();<br/>
}<br/>
public override void OnExit(MethodExecutionEventArgs eventArgs)<br/>
{<br/>
Unindent();<br/>
WriteLine( "Exiting " + eventArgs.Method);<br/>
}<br/>
public override void OnException(MethodExecutionEventArgs eventArgs)<br/>
{<br/>
StringBuilder sb = new StringBuilder();<br/>
sb.AppendFormat( "Exception {0} in {1}" , eventArgs.Exception, eventArgs.Method);<br/>
sb.AppendLine();<br/>
WriteLine(sb.ToString());<br/>
}<br/>
}<br/>
It should be mentioned here that for logging there is the addition of Log4PostSharp , which records information on the input and output of methods using the subsystem log4net .

Performance measurement


The author himself PostSharp'a in one of his article on CodeProject showed an interesting use of aspects to calculate the speed of execution of methods, as well as recording the number of their calls. These features are based on the fact that in aspect, just like in normal code, you can have static members, and call them whenever you like. What makes the following aspect possible:

[Serializable]<br/>
public class PerformanceCounterAttribute : OnMethodInvocationAspect<br/>
{<br/>
private static readonly Dictionary< string , PerformanceCounterAttribute><br/>
attributes = new Dictionary< string , PerformanceCounterAttribute>();<br/>
private long elapsedTicks;<br/>
private long hits;<br/>
[NonSerialized]<br/>
private MethodBase method;<br/>
public MethodBase Method { get { return method; } }<br/>
public double ElapsedMilliseconds<br/>
{<br/>
get { return elapsedTicks / (Stopwatch.Frequency / 1000d); }<br/>
}<br/>
public long Hits { get { return hits; } }<br/>
public static IDictionary< string , PerformanceCounterAttribute> Attributes<br/>
{<br/>
get<br/>
{<br/>
return attributes;<br/>
}<br/>
}<br/>
public override void OnInvocation(MethodInvocationEventArgs eventArgs)<br/>
{<br/>
Stopwatch stopwatch = Stopwatch.StartNew();<br/>
try <br/>
{<br/>
eventArgs.Proceed();<br/>
}<br/>
finally <br/>
{<br/>
stopwatch.Stop();<br/>
Interlocked.Add( ref elapsedTicks, stopwatch.ElapsedTicks);<br/>
Interlocked.Increment( ref hits);<br/>
}<br/>
}<br/>
public override void RuntimeInitialize(MethodBase method)<br/>
{<br/>
base .RuntimeInitialize(method);<br/>
this .method = method;<br/>
attributes.Add(method.Name, this );<br/>
}<br/>
}<br/>
Such an interesting PostSharp. And what aspects do you know?

Source: https://habr.com/ru/post/62232/


All Articles