We're very pleased to announce the release of the PostSharp Domain Toolkit – the first tool to treat the implementation of INotifyPropertyChanged seriously and finally end change notifications pain for WPF developers!

Let's start from the beginning...

The Problem

When you work with WPF using patterns such as Model-View-ViewModel (MVVM) or, in fact, any kind of data binding to your domain classes, in most cases you need to implement property values change notifications for your mutable objects. The .NET framework provides you with INotifyPropertyChanged interface but sadly leaves you the burden of implementing it. If you try to manually implement the interface, you'll end up with classes which look like this:

public class Customer : INotifyPropertyChanged
{
    private string firstName;
    public string FirstName
    {
        get { return firstName; }
        set
        {
            firstName = value;
            OnPropertyChanged("FirstName");
            OnPropertyChanged("FullName");
        }
    }
 
    private string lastName;
    public string LastName
    {
        get { return lastName; }
        set
        {
            lastName = value;
            OnPropertyChanged("LastName");
            OnPropertyChanged("FullName");
        }
    }

    public string FullName
    {
        get { return string.Concat(firstName, " ", lastName); }
    }

 
    public event PropertyChangedEventHandler PropertyChanged;
 
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

There are at least two problems here. First, notice the amount of boilerplate code in the solution. Instead of using simple automatic properties (one line of code), you need to create regular field-backed properties with OnPropertyChanged calls in each of them. Each property must also be aware of all other properties depending on its value (and we're still ignoring any inter-object dependencies).

What's worse, the code is now riddled with string literals. Just a minor typo is enough for the notifications to silently fail (with no build-time error and no runtime exception). Refactoring also becomes much harder – whenever you change the name of the property, you need to change the literal as well. Some refactoring tools might help here, but once you have several classes with the same property name (and you are going to have Name and Description properties on multiple classes) you can't fully trust them anymore.

If writing all this boilerplate code seems tiresome to you, think about tedium involved in debugging all the subtle problems that are so easily introduced by making a single typo or forgetting about a single dependency. Oh, the humanity!

Existing (Non-)Solutions

The problem is obviously not new and there are several libraries attempting to tackle it. Some of them would require you to create proxies or wrapper classes around your objects, introducing another layer of indirection and code complexity. Others simply make your boilerplate code less error-prone. In C# 5 (or using ActiveSharp in pre .NET 4.5 environment; see here and here for details) writing basic properties can be simplified to repeating a pattern similar to this one:

private int foo;
public int Foo
{
    get { return foo; }
    set { SetValue(ref foo, value); }
}

The more advanced solutions use IL weaving to automatically introduce property change notifications without actually requiring you to write any dedicated code. They can also handle basic properties dependencies supporting usage scenarios such as the one in this code snippet:

public class Person : INotifyPropertyChanged
{
    public string GivenNames { get; set; }
    public string FamilyName { get; set; }

    public string FullName
    {
        get
        {
            return string.Format("{0} {1}", GivenNames, FamilyName);
        }
    }   //...
}

Real Life

As usual, limitations and problems become obvious when you start using those solutions in real life. While they may handle the easy scenarios easily, it turns out that what you actually want to write in your code is not always that simple.

First, you may have various reasons to set field values directly and not via property setters. You may not even have properties for some of the fields at all:

private string firstName;
private string lastName;

public string Name
{
    get { return string.Concat(firstName, " ", lastName); }
}

The problem here is that none of the previously available solutions are able to automatically react to field values changes.

Second, when creating MVVM applications you will often want to have properties depending on values of child objects’ properties:

private Customer customer;

public string CustomerName
{
    get { return string.Concat(this.customer.FirstName,
                               " ", this.customer.LastName); }
}

How do you raise change notifications for the CustomerName property in this case?

You could try listening to NotifyPropertyChanged events of the Customer object, but you would have to manually write code to properly add and remove the necessary event handlers. Imagine how complicated and error-prone it would get with multi-level dependencies:

public string CustomerCity
{
   get { return this.customer.Address.City; }
}

 

Enter PostSharp Domain Toolkit.

PostSharp Domain Toolkit

Having personally experienced all the above pain points, and having at our disposal all the power of PostSharp, we decided to finally tackle the problem of property change notifications for (nearly) all standard business cases.

While simple PostSharp-based INotifyPropertyChanged implementations have long been available (see, for example, here), we decided to take the problem more seriously and made sure we concentrated on useful cases rather than just the easy ones.

The Domain Toolkit properly supports all of the above cases while also providing API and customization points for even more sophisticated scenarios. It automatically handles the following:

  • automatic and field-backed properties;
  • complex properties directly or indirectly depending on fields and other properties of the same class;
  • complex properties depending on methods within the same class (as long as they do not depend on methods in other classes);
  • complex properties depending on properties of child objects providing change notifications (automatically in case of simple property chains, basing on explicit declaration of dependencies in case of arbitrarily complex code).

To better understand the power of PostSharp Domain Toolkit simply have a look at the code below:

[NotifyPropertyChanged]
class CustomerViewModel
{
    private Customer customer;
 
    public CustomerViewModel(Customer customer)
    {
        this.customer = customer;
    }
 
    public string FirstName
    {
        get { return this.customer.FirstName; }
        set { this.customer.FirstName = value; }
    }
 
    public string LastName
    {
        get { return this.customer.LastName; }
        set { this.customer.LastName = value; }
    }
 
    public Address Address { get { return this.customer.Address; } }
 
    public string FullName { get { return this.customer.FullName; } }
 
    public string BusinessCard
    {
        get
        {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.Append(this.FirstName);
            stringBuilder.Append(" ");
            stringBuilder.Append(this.LastName);
            stringBuilder.AppendLine();
            stringBuilder.AppendLine(this.customer.Address.Line1);
            string line2 = this.customer.Address.Line2;
            if (!string.IsNullOrWhiteSpace(line2))
            {
               stringBuilder.AppendLine(line2);
            }
            stringBuilder.Append(this.customer.Address.PostalCode);
            stringBuilder.Append(' ');
            stringBuilder.Append(this.customer.Address.Town);
 
            return stringBuilder.ToString();
        }
    }
}

[NotifyPropertyChanged]
class Customer
{
    private string lastName;
    private Address address;
 
    public string FirstName { get; set; }
 
    public string LastName
    {
        get { return lastName; }
        set { lastName = value; }
    }
 
    public Address Address
    {
        get { return address; }
        set { address = value; }
    }
 
    public string FullName
    {
        get { return string.Join( " ", this.FirstName, this.LastName ); }
    }
 
    public void Reset()
    {
        this.FirstName = null;
        this.lastName = null;
        this.address = null;
    }
 
}

Yes, all the properties in the above classes are going to automatically raise change notifications, thanks to just the single NotifyPropertyChangedAttribute per class!

And that’s not all, our toolkit is the first framework that realizes you may not always want to raise change notification immediately after property value change. Consider this snippet:

public class Account
{
    public decimal Balance { get; private set; }
    public DateTime LastOperationTime { get; private set; }
    public int RemainingDailyOperationsLimit { get; private set; }
 
    //...

    public void Withdraw(decimal amount)
    {
        //... all kinds of checks ...

        this.LastOperationTime = DateTime.UtcNow;
        this.RemainingDailyOperationsLimit--;
        this.Balance -= amount;
    }
 
    //...
}

In all the solutions considered so far, this snippet would raise notification after each single property change, which means that at the time the first 2 of 3 events have been raised, the Account object would be in an inconsistent state, some property values would have already been modified and some not.

In this case your change notification listener would see an object in which the last operation time is already updated, but the account balance is lagging behind. This may not be a serious issue if all you need the notifications for is a simple case of displaying the values on-screen. It does get serious, however, as soon as any kind of business (or even more advanced presentation) code starts to react to the events.

The only consistent solution here is to raise all the events at the end of the method only. And that is exactly what our framework does. It intelligently identifies when execution flows back from an object to its caller, and only then, when the object state should again be consistent, raises all pending change notifications. All this, so there is nothing for you to worry about.

Summary

The automatic property change notifications mechanism in the new PostSharp Domain Toolkit automatically handles all standard business cases and generates exactly the right notifications, at exactly the right time.

Just like the other PostSharp Toolkits, the new Domain Toolkit frees you from spending precious time on infrastructural issues and keeps you focused on business logic.

As always, you can trust our solution not to misbehave or fail silently. If we encounter any code construct we can't properly handle automatically, we'll notify you about it by emitting an error at compile time. You'll then have a chance to use an additional attribute to help the Toolkit identify the property or method dependencies. Or, if you prefer, you can take even more control by using our public API to deal with your corner cases. But that's a topic for another blog post and honestly we don't expect you to need that possibility often (if ever).

More posts on Postsharp Domain Toolkit coming soon. In the meantime, you can have a look at the source of the PostSharp Domain Toolkit on github or, even better, install the nuget package and immediately start using the new framework (sample code above should be more than enough to get you started).

Happy PostSharping!

Karol Walędzik is an enterprise software applications architect and developer at Internetium Sp. z o.o. (Warsaw, Poland), as well as a .NET Programming and Artificial Intelligence lecturer and AI researcher at Warsaw University of Technology.

Last week, we announced the pre-release of PostSharp Threading Toolkit, a library of aspects implementing best-practice patterns of multithreaded applications. The toolkit is available on NuGet and its source code is hosted on GitHub.

Note: the source code of the ping-pong example is available on GitHub too.

The Right Level of Abstraction

The motto of this toolkit is that direct use of the System.Threading namespace brings so much complexity that it cannot scale to large object models. In other words, if you’re using System.Threading or just locks in your business or UI layer (except in a few very specific classes), you are probably addressing the threading concern at the wrong level of abstraction, and you and your co-workers will perhaps have difficulties to handle that complexity in large applications.

A far better approach to multithreading is to grab a few threading models and to make sure everyone understands them. Then, select the proper threading model for every part of your application (UI and domain will probably require a different threading model). Finally, implement the threading model in all your classes. That final step is obviously the most complex and time-consuming (there can be thousands of classes) and cancels the benefits of abstraction – unless you are getting the tools for the job: Erlang, F#, or… PostSharp Threading Toolkit.

The Bad: Mutable Shared State

Imagine you have a room full of accounting assistants all working on the yearly balance of the same company. To make it easier to share information among workers, the manager thought it was smart to decide that all accounts will be maintained on a blackboard, so that many workers can update it simultaneously. (I chose a blackboard on purpose, because if whiteboards existed, they would probably have chosen a computer.)

It’s clear that workers of this team need to cooperate (i.e., communicate) in order to get their jobs done, otherwise they are going to overwrite each other’s accounting lines and mess with the balance.

The moral in the fable: concurrent access by several workers to shared mutable state causes mess.

A state (typically: a field or an element array) is mutable if it can be modified. It is shared if it can be read or updated by several workers concurrently. Immutable shared state does not cause conflict (all workers can concurrently look at the President’s picture on the wall). Private mutable state is safe too (there would be no issue if every worker was assigned a single account).

Actors Against Mutable Shared State

Actor-Based Programming (or simply actors) address the problem of multithreaded software by eradicating mutable shared state. Formally, the Actor model wants to ensure that, for any state:

     NOT (state is mutable AND state is shared).

In other words, the Actor model ensures that:

     state is mutable XOR state is shared.

The Actor model implements these restrictions using – guess what: actors! Think of as Actor as an object-oriented class (with some children objects such as collections, perhaps).

The Actor model is based on the following principles:

  • Actors communicate between each other and with the rest of the world using messages.
  • Messages are immutable.
  • An actor process only one message at a time.
  • Processing messages may cause a change of the state of the actor that processes it. Other state changes must be ordered through message passing.

The principles ensure that the only mutable state is private to an actor, and since the message-passing mechanism guarantees actors are single-threaded, the mutable state is not shared among different processors.

These simple principles are at the origin of the success of Erlang and F#, but have traditionally been difficult to implement in C#. With the new C# 5.0 and PostSharp Threading Toolkit, this is now much easier!

A Simple Ping-Pong Game

Let’s see these principles in action on the simplest multi-actor game invented: ping-pong. To make the game a little more interesting (and show that execution is actually asynchronous), I’m throwing two balls in the game.

class Player : Actor
{
    string name;
    int counter;

    public Player(string name)
    {
        this.name = name;
    }

    public void Ping(Player peer,  int countdown)
    {
        Console.WriteLine("{0}.Ping from thread {1}", this.name, 
Thread.CurrentThread.ManagedThreadId); if (countdown >= 1) { peer.Ping(this, countdown - 1); } this.counter++; } } class Program { static void Main(string[] args) { Player ping = new Player("Sarkozy"); Player pong = new Player("Hollande"); ping.Ping(pong, 4); ping.Ping(pong, 4); Console.ReadLine(); } }

This program gives the following output:

Sarkozy.Ping from thread 3
Sarkozy.Ping from thread 3
Hollande.Ping from thread 4
Hollande.Ping from thread 4
Sarkozy.Ping from thread 3
Sarkozy.Ping from thread 3
Hollande.Ping from thread 4
Hollande.Ping from thread 4
Sarkozy.Ping from thread 6
Sarkozy.Ping from thread 6

As you can see, actors run on a different thread than the Main method. Calling the Ping method results in a message to be appended to the actor’s message queue. When a message queue was empty and got its first message, the dispatcher creates a Task to process the message queue. Therefore, actors run on the thread pool and there’s no need to have one thread per actor.

Note that the field Player.counter is incremented by the Ping method. Incrementing consists in reading and writing a field atomically. In a concurrent environment, we should have used Interlocked.Increment. But since the field is private to the actor, we don’t have to think about data races.

Build-Time Validation

I’ve said that the Player.counter field is safe because it’s private. I meant that logically it belongs to the private state of the actor, as opposed to the state that is shared among actors and threads. That said, private is also a C# concept, so what if the field was public?

We thought about this issue, and we designed the toolkit to minimize the level of thinking necessary to apply a threading model, so the toolkit will warn you of any potential error with the following build-time error message:

1>------ Build started: Project: TestAsync, Configuration: Debug Any CPU ------
1>POSTSHARP : postsharp error THR005: Field Player.counter must be private or 
protected because the Player class is an Actor. 1>POSTSHARP : postsharp error PS0060: The processing of module "TestAsync.exe" was
not successful. ========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

The toolkit does not claim it’s smarter than you, but it prevents you (and more likely your co-workers) from obvious mistakes. If you decide that a public field is really what you want, you have to annotate the field with the [ThreadUnsafe] custom attribute, which opts out from the safety net enforced by the toolkit.

In a similar spirit, the toolkit will prevent an actor to expose public method that return a value or have ref or out parameters. Why? Because these methods are modified to run asynchronously, so output values could not be known at the time the asynchronous call completes (asynchronous means that the execution continues after the call completes).

If you think this is an awful limitation, you’re right. But we have some good news for you.

Where C# 5.0 Saves the Day

The C# feature of the year is the ability to work with asynchronous methods thanks to two new keywords: async and await. The async keyword basically allows the use of the await keyword, which suspends the execution of the method until some task (or anything “awaitable”) completes. This is exactly what we need to get actor methods return some output to the caller.

Suppose we want to add a GetCounter method to the Player class. We could simply do that:

public async Task<int> GetCounter()
{
    return this.counter;
}

Intellisense gives some warning that there is no async keyword and the method will therefore run synchronously, but PostSharp adds some magic to make the method run asynchronously from the message queue of the Actor. You don’t have to take care of that and can safely ignore the compiler warning.

Now, suppose we call GetCounter just after we call Ping. The message is going to be queued just after the first Ping messages, we we’re probably going to get a counter value of 1. This is not what we want: we want to get the counter after the ping-pong game is finished. To achieve that, we need to make the Ping method awaitable by using the async and await keywords.

Here is the updated source code of our new ‘awaitable’ actor game:

class Player : Actor
{
    string name;
    private int counter;

    public Player(string name)
    {
        this.name = name;
    }

    public async Task<int> GetCounter()
    {
        return this.counter;
    }

    public async Task Ping(Player peer, int countdown)
    {
        Console.WriteLine("{0}.Ping({1}) from thread {2}", this.name, countdown, 
Thread.CurrentThread.ManagedThreadId); if (countdown > 1) { await peer.Ping(this, countdown - 1); } this.counter++; } [ThreadSafe] public override string ToString() { return this.name; } } class Program { static void Main(string[] args) { AsyncMain().Wait(); } private static async Task AsyncMain() { Player ping = new Player("Sarkozy"); Player pong = new Player("Hollande"); Task pingTask = ping.Ping(pong, 4); Task pongTask = pong.Ping(ping, 4); await pingTask; await pongTask; Console.WriteLine("{0} Counter={1}", ping, await ping.GetCounter()); Console.WriteLine("{0} Counter={1}", pong, await pong.GetCounter()); } }

 

Is PostSharp Necessary Anyway?

You could wonder where is PostSharp in this code. After all, isn’t C# 5.0 all you need? Actually, PostSharp is hidden behind the Actor class. It will modify the code of all public instance methods of actors to make them compatible with the Actor model. If you remove PostSharp, you will perhaps still have asynchronous methods, but they will not respect the Actor model and C# 5.0 alone isn’t going to clean up the multithreaded mess.

Actually, PostSharp adds the following code on the top of the main method of the asynchronous state machine. That seems complex, but it’s actually approximately equivalent to doing an “await Task.Yield()” if the code executes from the wrong context, to force it to be rescheduled in the right context.

if (!this.<>4__this.Dispatcher.CheckAccess())
{
  this.<>t__dispatchAwaiter = Task.Yield().GetAwaiter();
  SynchronizationContext oldContext = SynchronizationContext.Current;
  SynchronizationContext.SetSynchronizationContext
(this.<>4__this.Dispatcher.SynchronizationContext); this.<>t__builder.AwaitUnsafeOnCompleted<YieldAwaitable.YieldAwaiter,
Player.<GetCounter>d__0>(ref this.<>t__dispatchAwaiter, ref this); } else {
// Your original code.
}

Additionally to adding behavior to public methods, PostSharp (the Threading Toolkit, more specifically) ensures that your source code respects some safety rules for the Actor model, and emits build-time errors or run-time exceptions if necessary.

To-Do List

Although the main functionalities are implemented and work fine, there is still a lot of work on the to-do list. For instance, the toolkit does not enforce dispatching of private interface implementation yet, neither callback methods (delegate). Not all build-time and run-time checking are already implemented. Specifically, we don’t check that all messages (method parameters and return values) are immutable or otherwise thread-safe.

Summary

I’ve shown how PostSharp Threading Toolkit, together with C# 5.0, offer a compelling implementation of the Actor threading model. As you can see, proper compiler support (PostSharp is a compiler extension) makes programming much easier because it allows to address a problem at the proper level of abstraction.

In later posts, we’ll talk about other threading models supported by PostSharp Threading Toolkit: reader-writer synchronized, and thread unsafe (or singlethreaded).

Happy PostSharping!


-gael

One of the ideas behind the PostSharp Toolkits was a zero code change requirement, that would allow you to simply install the relevant toolkit from NuGet, rebuild your project and that’s it. To achieve that, we have revived the PostSharp XML configuration. The XML configuration is the unification of the Plug-in and Project models in the project loader. Let’s have a look on how PostSharp Diagnostics Toolkits use this XML configuration.

PSPROJ XML Configuration

After installing the PostSharp Diagnostics Toolkits via NuGet, a file with the .psproj extension will be created, named after the current project. The .psproj files are an XML representation of the PostSharp Project structure – containing the configuration of the application, with resolved properties and references.

Let’s take a look at the default configuration that is produced by the PostSharp Diagnostic Toolkit package:

<?xml version="1.0" encoding="utf-8" ?>
<!-- Default project used when PostSharp is detected according to project references. -->
<Project xmlns="http://schemas.postsharp.org/1.0/configuration" 
ReferenceDirectory="{$ReferenceDirectory}">  
 
<Property Name="LoggingBackend" Value="trace" />
 
<Using File="default"/>
<Using File="..\..\Build\bin\{$Configuration}\PostSharp.Toolkit.Diagnostics.Weaver.dll"/>
<Tasks>
   <XmlMulticast />
</Tasks>

<Data Name="XmlMulticast">
    <LogAttribute
xmlns="clr-namespace:PostSharp.Toolkit.Diagnostics;assembly:PostSharp.Toolkit.Diagnostics" />
    <LogAttribute
xmlns="clr-namespace:PostSharp.Toolkit.Diagnostics;assembly:PostSharp.Toolkit.Diagnostics"
AttributeExclude="true"
AttributeTargetMembers="regex:get_.*|set_.*" /> 
</Data>
</Project>

Let’s look at the properties more closely:

Project – the XML root node – defines the PostSharp Project configuration.

The property LoggingBackend specifies the logger that should be used by the Diagnostics Toolkit. This value is set during the installation of the toolkit via NuGet. The supported values in the PostSharp Diagnostics Toolkit package are trace (default) and console. Additional packages, PostSharp Diagnostics Toolkit for Log4Net and NLog add log4net and nlog as the supported backends.

The Using directives are a part of the plug-in model, allowing PostSharp to use external services. In this case, there are two entries (additional entries are added by other toolkits), the default, which is a required entry, and a (relative) path to the actual weaver implementation of our toolkit. At build time, PostSharp looks for project configurations in the referenced DLLs.

The Tasks section specifies a key feature of the XML configuration – the XML Multicasting. Like regular aspect multicasting, that is, the ability to apply a single aspect to multiple elements, XML Multicasting allows you to define aspect multicasting declaratively via XML. The XmlMulticast task will look for the data island with the name XmlMulticast, instantiate and apply the aspects that are specified within.

Which brings us to the actual LogAttribute aspect, that is shipped with the toolkits. This is a custom MethodLevelAspect, that is defined in the assembly PostSharp.Toolkit.Diagnostics.dll. The two entries in the XML file above define that the aspect will be applied by default to a) all methods in all types of the current assembly and b) will ignore property getters and setters. These lines are equivalent to applying the aspect on assembly level in code:

[assembly: Log] 
    
[assembly: Log(AttributeExclude="true" AttributeTargetMembers="regex:get_.*|set_.*")]

You can limit the multicasting by using the regular PostSharp filters, such as AttributeTargetTypes.

Logging Options

To control the logging level, severity and granularity of the logging, the Diagnostics toolkits include several options for fine-grained control over the logging output:

OnEntryLevel/OnSuccessLevel/OnExceptionLevel – specifies the logging level (severity) of the Entry/Exit/Exception message (e.g. “Entering: MyType.MyMethod()/Leaving: MyType.MyMethod()”)

Possible values:

  • None – the message will not be logged
  • Debug – the message will be logged at Debug/Trace level (when applicable)
  • Info – the message will be logged at Info level
  • Warning – the message will be logged at Warn level
  • Error – the message will be logged at Error level
  • Fatal – the message will be logged at Fatal level

OnEntryOptions/OnSuccessOptions – sets options for logging parameters and return values.

The options include:

  • None – no parameter information will be included
  • IncludeParameterType – includes the type name of the parameter
  • IncludeParameterName – includes the name of the parameter
  • IncludeParameterValue - Includes parameter value, by calling ToString on the object instance
  • IncludeReturnValue – includes the return value (applicable on OnSuccessOptions only)
  • IncludeThisArgument – includes the value of this argument in an instance method

The default values for the different options are:

Option Name Default Value
OnEntryLevel Debug
OnSuccessLevel Debug
OnExceptionLevel Warning
OnEntryOptions IncludeParameterType, IncludeParameterName, IncludeParameterValue
OnSuccessOptions IncludeParameterType, IncludeReturnValue
OnExceptionOptions None (exception is printed using the OnExceptionLevel severity)

Examples:

Suppose we have a method Reverse in class StringUtils, that takes in a string and returns a reversed string. With the default settings, using NLog as the backend, our call to this method with the word “orange” will be logged like this:

 

...

TRACE Entering: MyApplication.StringUtils.Reverse(System.String input = "orange")
TRACE Leaving: MyApplication.StringUtils.Reverse(System.String) : "egnaro"
...

 

In the Entering line, the method signature contains the type name (System.String), the parameter name (“input”) and its value. The Leaving line contains only the parameter type and the return value.

Let’s look at another example. Suppose you have a class Customer, implementing an Active Record pattern, located in the namespace MyApplication.Data. Suppose we want to log all calls made to methods in this namespace with the Info level, and having the value of the instance (this argument) printed in the log output together with the value of the parameters. Simply add the following line to the .psproj file:

 

<LogAttribute
    xmlns="clr-namespace:PostSharp.Toolkit.Diagnostics;assembly:PostSharp.Toolkit.Diagnostics"
    AttributeTargetTypes="MyApplication.Data.*"
    OnEntryLevel="Info"
    OnSuccessLevel="Info"
    OnEntryOptions="IncludeThisArgument | IncludeParameterValue" />

You can find additional configuration examples in the test projects of the PostSharp Diagnostics Toolkit source code.

Additional notes

In the toolkits we’re done away with manually specifying the ordering using AttributePriority – the value is now generating automatically during compilation, so be aware that ordering of the XML elements matters.

As always, we’d like to get your feedback on our Support Forums for PostSharp Toolkits! If you have suggestions, issues, or any other feedback, please let us know about it!

We hope that you’ll enjoy using the PostSharp Toolkits as much as we enjoy building them!

Happy PostSharping!

-Igal