Thursday, January 31, 2008

Basic Property Coding Conventions in C# 3

This is a question that is arguably aesthetic: the following two classes are functionally equivalent. Which way would you prefer to write your classes?
    public class Foo
{
public string Name
{
get;
private set;
}
public Foo(string name)
{
Name = name;
}
}
or
    public class Foo
{
private string m_Name;
public string Name
{
get { return m_Name; }
}
public Foo(string name)
{
m_Name = name;
}
}
Does one stand out to you as the better way?

Labels:

Friday, January 25, 2008

Model-View-ViewModel Pattern for WPF: Yet another approach

A few days back, I posted an example here of implementing Model-View-Controller in a WPF scenario. On a side note, WPF specialist Josh Smith posted his own rendition here, which is also a very nice example and a more humorous choice of program.

A quick redux:
I was originally bamboozled by the two-way databinding features of WPF. I architected a GUI that was directly bound to my business objects.This was a bad idea for me. Why? Because my business objects do not support any form of invalid state. As the user edits the screen, he/she is directly editing a business object.

The object is in a transitional state at this point. It should be OK to allow invalidity as the user edits, but my business object will not allow invalid values, so validation and user interactivity breaks down in this approach.

I need something that stands between the GUI and the BOs. I know! Lets pull out the classic Model-View-Controller Pattern! Now I can allow the user to edit the UI as they please. When the user clicks some sort of an "Apply" or "OK" button, an event is sent to the controller, where an attempt is made to perform some of the user's changes. At the very least, validation can occur at this point; the business object will throw any exceptions and they can be caught, logged, and displayed, put on the evening news, and troops will be deployed to Norway. If there are no exceptions, the business logic is successfully performed, and the view's bindings are updated to reflect the changes in the model. This is a popular design with the web-based guys, since their views have no state.

I wasn't happy with this. Why? I'm not working web pages right now; I want two-way databinding. I have to write plumbing code to send data from the GUI to the controller? I have to write plumbing code to send changes from the model to the GUI? Wasn't the whole point of WPF databinding to get rid of this plumbing code?

The Model-View-ViewModel Pattern: Binding to Facsimile Objects

I want to dynamically bind to something that doesn't really support dynamic binding. What to do? The MVVM approach is a very attractive compromise between two-way databinding and rigid model integrity!

Before I begin, I owe references to the masters and creators of this design:
  • Dan Crevier has the complete and unabridged version of MVVM. Go read straight from the source to get the full picture. The guys is very sharp, and savvy with WPF implementation details that I may never fully understand.

I'll break it down into the key elements:
  • Forget the controller, you wont be needing it here
  • You can keep the "IView" interface from the previous example, but they are much less useful, since we will not be mocking a view in the unit tests any more. I threw them away to keep things simple.
  • The Model objects don't change.
  • The ViewModel is a "made for UI" object that represents the Business Object.
  • The UI supports direct, two-way intimate binding with the ViewModel
  • The ViewModel supports invalid states. It can easily implement IDataErrorInfo here
  • When the user "commits" the edits, the ViewModel attempts to "update" the model object that it is representing.
Here is my "Pete-brand" abstract concept of a ViewModel:

/// <summary>
/// A view model is a facsimile of a business object that is safe for databinding and invalidation
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class ViewModelBase<T> : IDataErrorInfo where T : BusinessObject
{
/// <summary>
/// The model that the ViewModel is representing
/// </summary>
protected T Model
{
get;
set;
}
/// <summary>
/// ViewModels can represent new Models that havent been created yet.
/// If the model is null, then a "construction" will occur uppon the call to ApplyChanges
/// </summary>
protected bool IsNewModelInstance
{
get { return Model == null; }
}
/// <summary>
/// Setting up for a new model instance
/// </summary>
public ViewModelBase()
{
Model = null;
}
/// <summary>
/// setting up for editing an existing model
/// </summary>
/// <param name="model"></param>
public ViewModelBase(T model)
{
Model = model;
}
/// <summary>
/// Attempt to create or edit the underlying business object
/// </summary>
public void ApplyChanges()
{
try
{
if (IsNewModelInstance)
CreateModel();
else
ApplyToModel();
UpdateViewModelFromBusinessObject();
}
catch(Exception e)
{
UpdateViewModelFromBusinessObject();
throw e;
}
}
/// <summary>
/// edit the underlying model
/// </summary>
protected abstract void ApplyToModel();
/// <summary>
/// create the underlying model
/// </summary>
protected abstract void CreateModel();
/// <summary>
/// Any changes to the model should be reflected in the ViewModel
/// </summary>
protected abstract void UpdateViewModelFromBusinessObject();

#region IDataErrorInfo Members
///<summary>
///Gets the error message for the property with the given name.
///</summary>
///
///<returns>
///The error message for the property. The default is an empty string ("").
///</returns>
///
///<param name="columnName">The name of the property whose error message to get. </param>
public abstract string this[string columnName] { get; }
///<summary>
///Gets an error message indicating what is wrong with this object.
///</summary>
///
///<returns>
///An error message indicating what is wrong with this object. The default is an empty string ("").
///</returns>
///
public abstract string Error { get; }
#endregion
}

A few key pieces here:
  • A ViewModel can be used to construct a new business object, or it can be used to edit existing ones.
  • A ViewModel exposes its own copy of public properties of the inner object, and they can indeed be as invalid and cockeyed as the user pleases.
  • When the user clicks "Apply", the ApplyChanges method will be called, which will either attempt to construct a new BO or edit an existing BO using the properties from the ViewModel. Exceptions may be thrown by the BO, and they will be caught and displayed in the normal fashion.
The program itself is functionally the same thing. The BlogEntry editor window is now tightly bound to a BlogEntryViewModel. It is the responsibility of BlogEntryViewModel to attempt to plug property changes in to the BlogEntry.

Observations along the way:
  • As you allow the user to do more and more before clicking apply, the chances of exceptions and validation problems increases. All the better reason to implement IDataErrorInfo, I suppose.
  • I am still not happy with the IDataErrorInfo interface. The ViewModel has validation, the business objects have validation, I need to find a way to reuse the validation from the BOs and not rewrite this!
  • As you allow the user to do more and more before clicking apply, the Apply() method of the ViewModel object will become more complicated as it tries to synchronize the business objects with its own state. These are a lot of Moving Parts.
  • The ViewModel and the Model contain the bulk of the intelligence, and they are very easy to isolate and test.
  • Josh Smith and others tell me that the CSLA framework handles this tie between the GUI and the BO in a graceful manner. I want to find the best solution, I will be researching here next.
The source code is a derivative of the MVC work. Download the new source code here

Wednesday, January 23, 2008

Model-View-Controller Architecture for WPF: An Approach.

Do you know that disappointing feeling you get, when you learn a new technology, you start using it, just to discover that all the examples of the new technology across the internet don't amount to a practical real-life application?

The problem with conventional WPF examples
I am far from an expert in UI, especially windows forms UI, which makes me the perfect victim for bad examples. This is the feeling that I am getting with WPF right now. Where are the best practices? Its a very impressive technology, and it makes for some very entertaining parlor tricks. We see new sample interfaces that look like video games. We see a plethora of code samples for new tricks, but few of them form to any semblance of a practical windows application.

New Technology Causes new Confusion

The other thing we get with WPF is a very dangerous period of conceptual oblivion as we try to figure out where we should define the bindings, where should we define behavior, where to draw the line between presentation and logic etc.
  • Should I use a ContentTemplate? Maybe a DataTemplate?
  • Should I define my bindings declaratively, or should I do it manually in the codebehind? If I do it in the XAML, should I use the XAML notation, or the odd new binding syntax with the curly braces?
The questions go on and on... OK I have X-teen ways to do the same thing, which way do I go?

One thing I am discovering (the hard way) is that if you just want to write a normal program, a lot of the classic design patterns are still very relevant with WPF.

The Danger of "Direct" DataBinding
DataBinding is where I got burnt.
Think about it: everywhere you look, you see examples of a WPF UI directly bound to a business object. Direct binding between the UI and a business object has some dangerous implications:
  • As I type/click etc... I am causing immediate changes to the business object. How do I undo them?
  • Since the binding is two-way and immediate, does this mean I no longer need an "Apply" button anymore?
  • What if I want to cancel what I was just doing?
I was writing a program where the UI was directly bound to my business objects. I had a textbox bound to the "Name" property of an object. My object throws an exception if its passed a name that is empty, or too big.

What happens when I type beyond this limit? As a default behavior, WPF swallows the exception! It turns out you need to write a custom binding object to display the exception to the user in a messagebox.

What is the recommended solution for databinding to objects? Your objects need to implement IDataErrorInfo. The gist of implementing IDataErrorInfo is this:

public virtual string this[string propertyName] {
get {
//find the property that matches the string "propertyName"
//check the current state of the property, is it valid?
//if its valid, return string.Empty
//if its invalid, return a formatted error
}

This code gives me a migraine headache. Why should my business objects EVER exist in an invalid state?
The recommended solution seems like an antipattern to me. Imagine that, all of your business objects implementing INotifyPropertyChanged, INotiftyCollectionChanged, IDataErrorInfo just so they can work with WPF?

But still, databinding is a very cool feature in WPF. How to leverage this without violating my model? I made a mistake, I was so focused on using new WPF tricks that I forgot how to build a good testable application.

My (current) Solution

MVC is a design pattern intended to solve the problem between separating a UI and the logic. These were the qualities I was looking to achieve:
  • "smart" business objects, they throw exceptions if you try to pass them anything invalid, or something goes wrong in an operation
  • "dumb" WPF UI that is bound in a dynamic, readonly fashion to the objects
  • The UI catches exceptions and shows them to the user in a messagebox
  • A "controller" that receives gestures from the UI and performs logic on the model
  • The controller should be very testable
  • The UI should be very mockable
  • The UI has no direct write access to anything in the business objects
The Sample Program: the cliche Blogger Application ala WPF

The sample program is here. It is a very simple, practical windows program for adding/editing/removing blog posts. The main attraction is the architecture.

The Models
The model is simple. I have a Blog that contains a collection of BlogEntries, and they are both "Persistable" meaning they have an ID and a name.
The Blog class exposes a list of its BlogEntries, but it has specialized Add/Remove methods for modifying this list.









The Views


Since I needed the view to be mockable, I set up for an interface, such as IView.
Any view must have an associated controller, and be able to update its display with the latest data. I create a view for every aspect of the program. In this case, I have a view for BlogEntries, and a view for Blogs. These views generate events that are handled by the controllers. They use BlogEntryEventArgs as arguments for the events.




The Controllers

The controllers exist to handle generated events from the views, and this is where the real business logic begins. As you can see, there is an event handler for every event in the view. One noteworthy thing here is that the controller does not need to talk back to the view for updates because the view is directly bound (in a readonly fashion) to the model. That little feature is thanks to the WPF databinding power!







Finally, The UI

The UI is two windows, one for selecting blog entries and one for editing them. Each window implements a view. Upon window construction, each window registers a controller. Button callbacks from the UI generate events sent to the controller. The windows catch and display any exceptions, and the UI is automatically updated by the direct databinding to the model.

What about tests?

How to test UI interaction without a UI? Make a "mock" of the views, and test the controller. Since our views are dumb, we can effectively test the bulk of the application using a dummy view. For example:


private class MockBlogEntryView:IBlogEntryView{...}

///
/// isolate the controller for testing
///

[Test]
public void TestCreate()
{
Blog _blog = new DummyBlogDAO().GetByID(0);
MockBlogEntryView _mockBlogEntryView = new MockBlogEntryView();
BlogEntryController _blogEntryController = new BlogEntryController(_mockBlogEntryView);
BlogEntryEventArgs _blogEntryEventArgs = new BlogEntryEventArgs(null, "blahblahblah", "New post", _blog);
_mockBlogEntryView.ThrowCreateEvent(_blogEntryEventArgs);
}
At this point, feel free to replace MockBlogEntryView with something from your favorite mocking API such as Rhino Mocks or TypeMock.NET if you are too lazy to define a dummy view.

Other Observations

Admittedly, this is yet but another simple example for a tricky concept. Does this architecture scale nicely as the complexity grows? Your mileage may indeed vary.
In my example, I have a view-per-model design, but
  • There is no real correlation at all between the number of views you have and the number of "model" objects.
  • There is no real correlation between the number of controllers you have and the number of views either. (In retrospect, perhaps I only needed one controller)
There is never a single and straight path of correctness for building any complex app; there are many right ways. I'll post again when I see how this architecture holds up to the test of time and complexity.

Download the Source Code here

Labels: ,

Sunday, January 20, 2008

PostSharp - Part 2: DataBinding support and some adjustments

Now that I have had a chance to play around with PostSharp with a few more rounds I've discovered that my old examples have some room for improvement.
Go here for my initial impressions.
  • I want my "Triggers" to occur only on property setters
  • I want the triggers to disregard any property related to reflection
  • I want to my triggers to be independent of one another (this is important)
  • I want to create a non-intrusive attribute that supports INotifyPropertyChanged
For the first two requirements, this is easy. I add some conditionals to the beginning of each MethodBoundaryAspect event. For example,


[Serializable]
public class NotNullValidator : OnMethodBoundaryAspect
{
public override void OnEntry(MethodExecutionEventArgs eventArgs)
{
//ignore calls that have no arguments, they come from reflection
if (eventArgs.GetArguments() == null)
return;
//ignore anything that isnt a setter
if (!eventArgs.Method.Name.StartsWith("set_"))
return;

if (eventArgs.GetArguments()[0] == null)
throw new ArgumentNullException(eventArgs.Method.Name);
base.OnEntry(eventArgs);
}
}

Now for the INotifyPropertyChanged functionality. I searched for an existing example, but came up empty-handed. Fortunately, there was an open source version of Data Binding support readily available at http://code.google.com/p/postsharp-user-samples/ . These examples are in VB, so I converted the code in to C#. I will be looking at adding my changes to the trunk as soon as I can get my examples to match their standards. In the meantime, this is the C# code I have been using for NotifyPropertyChanged support (based on the provided VB code):

/// <summary>
/// Custom attribute that, when applied on a type (designated <i>target type</i>), implements the interface
/// <see cref="INotifyPropertyChanged"/> and raises the <see cref="INotifyPropertyChanged.PropertyChanged"/>
/// event when any property of the target type is modified.
/// </summary>
/// <remarks>
/// Event raising is implemented by appending logic to the <b>set</b> accessor of properties. The
/// <see cref="INotifyPropertyChanged.PropertyChanged"/> is raised only when accessors successfully complete and the
/// underlying value is really changed.
/// </remarks>
[MulticastAttributeUsage(MulticastTargets.Class | MulticastTargets.Struct), Serializable()]
public sealed class SupportDataBindingAttribute : CompoundAspect
{

#region "Private Variables"
[NonSerialized()]
private int myAspectPriority = 0;
#endregion

/// <summary>
/// Method called at compile time to get individual aspects required by the current compound
/// aspect.
/// </summary>
/// <param name="targetElement">Metadata element (<see cref="Type"/> in our case) to which
/// the current custom attribute instance is applied.</param>
/// <param name="collection">Collection of aspects to which individual aspects should be
/// added.</param>
public override void ProvideAspects(object targetElement, LaosReflectionAspectCollection collection)
{
// Get the target type.
Type targetType = (Type) targetElement;
// On the type, add a Composition aspect to implement the INotifyPropertyChanged interface.
collection.AddAspect(targetType, new AddNotifyPropertyChangedInterfaceSubAspect());
// On the type, add a Composition aspect to implement the IEditableObject interface
//collection.AddAspect(targetType, new AddEditableObjectInterfaceSubAspect());
// Add a OnMethodBoundaryAspect on each writable non-static property. The implementation of
// INotifyPropertyChanged.PropertyChanged needs the name of the property (not of the field), so we have to detect
// changes on the property level, not on the field level. Unfortunately, there is no rule for naming properties and
// their related fields. Even more, one property could access many fields, or gets its value out of one or
// more fields. At this point, using an enhancer to add this functionallity is only recomended, as there exixts a design
// rule, which relates one field to one and only one property and vice versa. Unfortunately, there is no compile time check available.
// Please be careful!!
// Personally, I tend to say, that implementing INotifyPropertyChanged is out of the scope of enhancers due to the
// possibility to pack logic within the properties implementation, which is never under the enhancers control.
foreach (PropertyInfo pi in targetType.UnderlyingSystemType.GetProperties())
{
if (object.ReferenceEquals(pi.DeclaringType, targetType) && pi.CanWrite)
{
MethodInfo mi = pi.GetSetMethod();
if (!mi.IsStatic)
{
collection.AddAspect(mi, new OnPropertySetSubAspect(pi.Name, this));
}
}
}
}
public int AspectPriority {
get { return myAspectPriority; }
set { myAspectPriority = value; }
}

/// <summary>
/// Implementation of <see cref="OnMethodBoundaryAspect"/> that raises the
/// <see cref="INotifyPropertyChanged.PropertyChanged"/> event when a property set
/// accessor completes successfully and the value really changes.
/// </summary>
[Serializable()]
private class OnPropertySetSubAspect : OnMethodBoundaryAspect
{

private readonly string myPropertyName;
private object myOldValue;

/// <summary>
/// Initializes a new <see cref="OnPropertySetSubAspect"/>.
/// </summary>
/// <param name="propertyName">Name of the property to which this set accessor belong.</param>
/// <param name="parent">Parent <see cref="NotifyPropertyChangedAttribute"/>.</param>
public OnPropertySetSubAspect(string propertyName, SupportDataBindingAttribute parent)
{
this.AspectPriority = parent.AspectPriority;
myPropertyName = propertyName;
}

public override void OnEntry(MethodExecutionEventArgs eventArgs)
{
// Construct the name of the properties get-method and backup the value before the set-method is invoked.
myOldValue =
eventArgs.Instance.GetType().InvokeMember(eventArgs.Method.Name.Substring(4),
BindingFlags.GetProperty, null, eventArgs.Instance, null,
null, null, null);
base.OnEntry(eventArgs);
}
/// <summary>
/// Executed when the set accessor successfully completes. Raises the
/// <see cref="INotifyPropertyChanged.PropertyChanged"/> event.
/// </summary>
/// <param name="eventArgs">Event arguments with information about the
/// current execution context.</param>
public override void OnSuccess(MethodExecutionEventArgs eventArgs)
{
object newValue;
newValue =
eventArgs.Instance.GetType().InvokeMember(eventArgs.Method.Name.Substring(4),
BindingFlags.GetProperty, null, eventArgs.Instance, null,
null, null, null);
// Raises the PropertyChanged event, if necessary. We assume in this sample, that only value types were used.
if (myOldValue != newValue)
{
// Get the implementation of INotifyPropertyChanged. We have access to it through the IComposed interface,
// which is implemented at compile time.
NotifyPropertyChangedImplementation implementation =
(NotifyPropertyChangedImplementation)
((IComposed<INotifyPropertyChanged>) eventArgs.Instance).GetImplementation(
eventArgs.InstanceCredentials);
implementation.OnPropertyChanged(myPropertyName);
}
}
}

/// <summary>
/// Implementation of <see cref="CompositionAspect"/> that adds the <see cref="INotifyPropertyChanged"/>
/// interface to the type to which it is applied.
/// </summary>
[Serializable()]
private class AddNotifyPropertyChangedInterfaceSubAspect : CompositionAspect
{
/// <summary>
/// Called at runtime, creates the implementation of the <see cref="INotifyPropertyChanged"/> interface.
/// </summary>
/// <param name="eventArgs">Execution context.</param>
/// <returns>A new instance of <see cref="NotifyPropertyChangedImplementation"/>, which implements
/// <see cref="INotifyPropertyChanged"/>.</returns>
public override object CreateImplementationObject(InstanceBoundLaosEventArgs eventArgs)
{
return new NotifyPropertyChangedImplementation(eventArgs.Instance);
}

/// <summary>
/// Called at compile-time, gets the interface that should be publicly exposed.
/// </summary>
/// <param name="containerType">Type on which the interface will be implemented.</param>
/// <returns></returns>
public override Type GetPublicInterface(Type containerType)
{
return typeof(INotifyPropertyChanged);
}

/// <summary>
/// Gets weaving options.
/// </summary>
/// <returns>Weaving options specifying that the implementation accessor interface (<see cref="IComposed{T}"/>)
/// should be exposed, and that the implementation of interfaces should be silently ignored if they are
/// already implemented in the parent types.</returns>
public override CompositionAspectOptions GetOptions()
{
return CompositionAspectOptions.GenerateImplementationAccessor | CompositionAspectOptions.IgnoreIfAlreadyImplemented;
}
}

/// <summary>
/// Implementation of the <see cref="INotifyPropertyChanged"/> interface.
/// </summary>
private class NotifyPropertyChangedImplementation : INotifyPropertyChanged
{
// Instance that exposes the current implementation.
private readonly object myInstance;

/// <summary>
/// Initializes a new <see cref="NotifyPropertyChangedImplementation"/> instance.
/// </summary>
/// <param name="instance">Instance that exposes the current implementation.</param>
public NotifyPropertyChangedImplementation(object instance)
{
myInstance = instance;
}

/// <summary>
/// Event raised when a property is changed on the instance that
/// exposes the current implementation.
/// </summary>
//public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
//public delegate void PropertyChangedEventHandler(object sender, System.ComponentModel.PropertyChangedEventArgs e);

/// <summary>
/// Raises the <see cref="PropertyChanged"/> event. Called by the
/// property-level aspect (<see cref="AddNotifyPropertyChangedInterfaceSubAspect"/>)
/// at the end of property set accessors.
/// </summary>
/// <param name="propertyName">Name of the changed property.</param>
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null) {
PropertyChanged(myInstance, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}


How to use this? In my business object, I have:

[SupportDataBinding]
public class SomeBusinessObject
{
#region members
private string m_Name;
#endregion

#region properties

[NotNullValidator]
[Logger( LogOnEntry = true, LogOnException = true, LogOnSuccess = true)]
[StringLengthValidator(MinLength = 1,MaxLength = 10)]
public string Name
{
get { return m_Name; }
set { m_Name = value; }
}
#endregion

#region methods
public SomeBusinessObject(string name)
{
m_Name = name;
}
#endregion
}

And a test program:

class Program
{
static void Main(string[] args)
{
SomeBusinessObject someBusinessObject = new SomeBusinessObject("Pete");
INotifyPropertyChanged bindableVersion = someBusinessObject as INotifyPropertyChanged;
bindableVersion.PropertyChanged += new PropertyChangedEventHandler(bindableVersion_PropertyChanged);
try
{
Console.WriteLine("Try passing in something long");
someBusinessObject.Name = "Some name that is too long";
}
catch(Exception e)
{
Console.WriteLine("Oops! that was too long");
}
try
{
Console.WriteLine("Try passing in something too short");
someBusinessObject.Name = string.Empty;
}
catch(Exception e)
{
Console.WriteLine("Oops! That was too short");
}
try
{
Console.WriteLine("Try passing in a null");
someBusinessObject.Name = null;
}
catch
{
Console.WriteLine("Oops, that was too null...");
}

Console.WriteLine("Run a successful property change.");
someBusinessObject.Name = "123";
Console.Read();
}

static void bindableVersion_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
Console.WriteLine("Handling property changed event!");
}
}

And my output:

Try passing in something long
Oops! that was too long
Try passing in something too short
Oops! That was too short
Try passing in a null
Oops, that was too null...
Run a successful property change.
OnEntry: set_Name
OnSuccess: set_Name
Handling property changed event!

Contact me if you are interested in getting my code in a downloadable solution and I will post it! Otherwise, I am going to work at getting my code on to the PostSharp user samples next week.

Friday, January 18, 2008

PostSharp - its like triggers that dont suck

**Update-- I have some amended code in a newer post located here**

Do you remember the first time you learned about triggers? Maybe you were like me, and you were initially fooled in to thinking that hidden behavior hiding behind basic operations was a good idea, only to discover its ugly side later down the road.

PostSharp brings the notion of triggers to C#, except this time I've found a safe and easy way to reuse a lot of common code with just a little bit of AOP.

Ive been pulled in to the debate about how to supplement class properties with common functionalities such as validation, logging, "NotifyPropertyChanged", Observability, and other common stuff that needs to occur with properties.

We want our Business objects to have a uniform behavior, but we often write redundant code in each property for validation, logging, databinding etc...

This afternoon I came accross PostSharp and WOW I am impressed with its potential!

Imagine this scenario:
you have a BusinessObject with a Name property.
  • the name property should not allow nulls
  • it should not allow empty strings
  • it should not allow strings longer than length of 10
  • logging should be performed before and after the property has changed
  • logging should occur if theres any exceptions that crop up inside of the depths of the Name property


public class SomeBusinessObject
{
#region members
private string m_Name;
#endregion

#region properties

[NotNullValidator]
[Logger( LogOnEntry = true, LogOnException = true, LogOnSuccess = true)]
[StringLengthValidator(MinLength = 1,MaxLength = 10)]
public string Name
{
get { return m_Name; }
set { m_Name = value; }
}
#endregion

#region methods
public SomeBusinessObject(string name)
{
m_Name = name;
}
#endregion
}

And I am done. No Kidding! Check out my little console program:

class Program
{
static void Main(string[] args)
{
SomeBusinessObject someBusinessObject = new SomeBusinessObject("Pete");

try
{
Console.WriteLine("Try passing in something long");
someBusinessObject.Name = "Some name that is too long";
}
catch(Exception e)
{
Console.WriteLine("Oops! that was too long");
}
try
{
Console.WriteLine("Try passing in something too short");
someBusinessObject.Name = string.Empty;
}
catch(Exception e)
{
Console.WriteLine("Oops! That was too short");
}
try
{
Console.WriteLine("Try passing in a null");
someBusinessObject.Name = null;
}
catch
{
Console.WriteLine("Oops, that was too null...");
}

Console.WriteLine("Run a successful property change.");
someBusinessObject.Name = "123";
Console.Read();
}
}

The output of this looks like:

Try passing in something long
OnEntry: set_Name
OnException: set_Name
Oops! that was too long
Try passing in something too short
OnEntry: set_Name
OnException: set_Name
Oops! That was too short
Try passing in a null
Oops, that was too null...
Run a successful property change.
OnEntry: set_Name
OnSuccess: set_Name

Note how the order of the attributes decides the precedence of execution. So, whats the trick? I defined those validation and logging attributes. They all inherit from OnMethodBoundaryAspect which gives me the ability to inject logic right before and after the execution of a method.
For example, here is the Null-checking property validator:

[Serializable]
public class NotNullValidator : OnMethodBoundaryAspect
{
public override void OnEntry(MethodExecutionEventArgs eventArgs)
{
if (eventArgs.GetArguments()[0] == null)
throw new Exception(string.Format("Value was null."));
base.OnEntry(eventArgs);
}
}

Here is the string length property validator:

[Serializable]
public class StringLengthValidator : OnMethodBoundaryAspect
{
public int MinLength
{
get;
set;
}
public int MaxLength
{
get;
set;
}
public override void OnEntry(MethodExecutionEventArgs eventArgs)
{
string value = eventArgs.GetArguments()[0] as string;
if(value.Length>MaxLength)
throw new Exception(string.Format("Exceeded maximum string length of {0}",MaxLength));
if(value.Length<MinLength)
throw new Exception(string.Format("Fell short of the minimum string length of {0}",MinLength));
base.OnEntry(eventArgs);
}
}

and finally, the Logging code:

[Serializable]
public class Logger : OnMethodBoundaryAspect
{
#region properties
public bool LogOnEntry
{
get;
set;
}
public bool LogOnExit
{
get;
set;
}
public bool LogOnException
{
get;
set;
}
public bool LogOnSuccess
{
get;
set;
}
#endregion

#region methods
public override void OnEntry(MethodExecutionEventArgs eventArgs)
{
if(LogOnEntry)
Console.WriteLine("OnEntry: {0}", eventArgs.Method.Name);
base.OnEntry(eventArgs);
}
public override void OnException(MethodExecutionEventArgs eventArgs)
{
if(LogOnException)
Console.WriteLine("OnException: {0}", eventArgs.Method.Name);
base.OnException(eventArgs);
}
public override void OnExit(MethodExecutionEventArgs eventArgs)
{
if(LogOnExit)
Console.WriteLine("OnExit: {0}", eventArgs.Method.Name);
base.OnExit(eventArgs);
}
public override void OnSuccess(MethodExecutionEventArgs eventArgs)
{
if(LogOnSuccess)
Console.WriteLine("OnSuccess: {0}", eventArgs.Method.Name);
base.OnSuccess(eventArgs);
}
#endregion
}

and this is just a warmup!

I found an example of INotifyPropertyChanged and IEditable property attributes in vb, I'm in the process of converting it to C# and I should have this in the next few days.