Showing posts with label Design Patterns. Show all posts
Showing posts with label Design Patterns. Show all posts

Friday, January 30, 2009

Paging in ASP.NET using NHiberate

NHibernate, the object-relational mapping (ORM) framework for .NET, supports custom pagination for collections. It provides a potential alternative to the built-in paging mechanism and native support found in ASP.NET GridView web controls. NHibernate exposes in the API for IQuery and ICriteria two methods, SetFirstResult and SetMaxResult, that can be used to enable paging:

Collections are pageable by using the IQuery interface with a filter:

IQuery q = s.CreateFilter( collection, "" ); // the trivial filter
q.setMaxResults(PageSize);
q.setFirstResult(PageSize * pageNumber);
IList page = q.List();

Adding Paging to the Base DAO

The design and architecture of my project, where NHibernate style pagination shall be introduced, was deeply influenced by Billy McCafferty's NHibernate Best Practices with ASP.NET resulting in the proliferation of Data Access Objects (DAO) throughout the guts of the application. Each DAO maps one-to-one to a single matching table in the database.

For example, a 'Task' table would have a corresponding 'TaskDao' in the data access layer (DAL) of the application. All of these DAOs inherit from the same base class 'GenericNHibernateDao' responsible for containing commonly shared code including managing NHibernate sessions and providing generic methods for summoning a specific persisted instance by ID and for saving/deleting an existing instance.

The initial step to implement paging was adding the following members to the aforementioned base class 'GenericNHibernateDao':

public abstract class GenericNHibernateDao : IGenericDao
{
public int PageSize
{
get { return _pageSize; } set { _pageSize = value; }
}

public int PageNumber
{
get { return _pageNumber; } set { _pageNumber = value ; }
}

protected int GetFirstResultPosition()
{
return _pageSize * (_pageNumber - 1);
}

protected void SetPagingFor(IQuery query)
{
query.SetFirstResult(GetFirstResultPosition());
query.SetMaxResults(_pageSize);
}

protected void SetPagingFor(ICriteria criteria)
{
criteria.SetFirstResult(GetFirstResultPosition());
criteria.SetMaxResults(_pageSize);
}

/*
other non-paging related members...
*/
}

The property 'PageSize' gets/sets the number of instances expected to be displayed on a web page for a specific strongly typed collection. Essentially, it handles how many rows to return for a embedded control (such as a GridView) on the page. This property is intended to be internally consumed by NHibernate's 'SetMaxResults' method belonging to IQuery or ICriteria. For example:

IQuery query = Session.CreateQuery();
query.SetMaxResults(_pageSize);

'PageNumber' specifies the set of multiple instances to be returned and displayed on a web page as identified and grouped by a page sequence numeric value (This is the equivalent of 'PageIndex' property for GridViews). For example, if you had a total of five pages worth of data rows then displaying the second page would require setting the page number value to '2' (e.g. _dao.PageNumber = 2). Just as with 'PageSize', this property is intended to be used by the method 'GetFirstResultPosition' as will be explained next.

The protected method 'GetFirstResultPosition' calculates the actual row at which to start paging based on the values provided by 'PageSize' and 'PageNumber'. This method's return value is expected to be passed to NHibernate's 'SetFirstResult' method, once again, part of IQuery or ICriteria. For example:

IQuery query = Session.CreateQuery();
query.SetFirstResult(GetFirstResultPosition());

The overloaded method named 'SetPagingFor' performs the actual paging functionality via the 'SetFirstResult' and 'SetMaxResult' methods of IQuery and ICriteria. (As an aside, the use of IQuery to build and execute HQL is much more common on this project in comparison to the almost non-existent use of ICriteria).

The base class 'GenericNHibernateDao' was also modified to have one of its existing methods 'GetAll' call 'SetPagingFor'. (The 'GetAll' method simply returns a list of all strongly type objects in the database without any specified criteria or filtering):

// GenericNHibernateDao class
public IList<T> GetAll()
{
ICriteria criteria = Session.CreateCriteria(persitentType);
SetPagingFor(criteria); // this is newly added!
return criteria.List();
}

As it shall be made clear later, this new line of code will optionally provide paging functionality when 'GetAll' is requested, if needed, but not required per se.

Applying Paging Functionality in the DAOs

Now that the base class 'GenericNHibernateDao' has been updated to manage paging, any of its derived DAO classes are instantly equipped to perform paging themselves. To actually invoke the paging functionality for any of the DAOs, simply set the appropriate values for page size and page number as shown in this example for the 'TaskDao' class:

// In context (such as a Presenter or Controller class
// part of an MVP/MVC applied framework)
TaskDao _taskDao = new TaskDao();
_taskDao.PageSize = 20;
_taskDao.PageNumber = 5;
IList<TaskDao> list = _taskDao.GetAll();

Without paging, 'GetAll' would have returned something in the neighborhood of 100 or so rows. With paging, the returned list will instead be only 20 rows starting at row (i.e. position) # 80. While a hundred rows might not sound too substantial, larger sets of data can have a more noticeable effect on your application's day-to-day operations if your table contains tens or hundreds of thousands of rows. Your performance will be progressively impacted as your application scales with more data.

On the other hand, NHibernate's paging will significantly decrease the size of your result set. This is in stark contrast to the default behavior of the existing paging available in any GridView control. If this native functionality of the control is used, it will return all rows from the database and then page them in memory. As mentioned earlier, this can lead to slower performance as your data grows. More on this later.

If, for any reason, paging is not essential for a particular web page in possession of a control bound to a strongly typed list (for example,a very small static list of data) then setting the page size and number properties is not required at all. Simply call 'GetAll' by itself disregarding the 'PageSize' and 'PageNumber' properties and the DAO should return all rows found in the associated database table. What makes this possible is that the default values for those two paging properties are formally declared in the DAO base class to behave as expected for "non-pageable" collections:

public abstract class GenericNHibernateDao : IGenericDao
{
protected int _pageSize = -1; // default for unlimited page size
protected int _pageNumber = 1; // default for first item in collection

// more members...
}

Typically, a DAO might have other custom methods that return more narrowly focused (i.e. filtered) lists of typed objects than what 'GetAll' offers. For these other DAO methods, the same pattern can be followed by adding the one line of code calling 'SetPagingFor'. For example, the 'TaskDao' might have a method that returns tasks that were completed in 2006 excluding any other tasks not done within that same year:

// TaskDao class
public IList<TaskDao> GetTasksCompletedIn2006()
{
IQuery query = Session.CreateQuery(
"some HQL statement that filters tasks by 2006...");
SetPagingFor(query); // newly added!
return query.List();
}

It is generally good practice not to pass the values for page size and number directly via the parameter list for any of these custom filtered data access methods. A few reasons to avoid this: (a) it can quickly clutter the intent of the method, and (b) it would prevent the paging functionality from being optional and, as a result, become less flexible, less reusable, and more cumbersome to work with.

Consequently, while it would be tempting to write the method's signature as such:

IList<TaskDao> list = _taskDao.GetAllTasksWithSubtasks(
param1, param2, param3, ..., paramN, pageSize, pageNumber);

It is preferable to do the following instead:

_taskDao.PageSize = 20;
_taskDao.PageNumber = 5;
IList<TaskDao> list = _taskDao.GetAllTasksWithSubtasks(
param1, param2, param3, ..., paramN);

The Downside of NHibernate Paging with ASP.NET Controls

As indicated earlier, one weakness of NHibernate's paging when combined with ASP.NET's GridView control involves some loss of "out-of-the-box" functionality. Under more conventional circumstances, when a collection of objects are bound to a GridView control, one of the built-in paging features of that control is to automatically render on the web page the navigation hyperlinks for the pages. For example, you might see following below your control:

" 1 2 3 ... 10 "

The GridView's default behavior assumes that all data bound directly to its DataSource can be paged as long as the number of items of that data is greater than its PageSize property value. Hence, if that condition is met, the control will slice up and present the data as appropriate.

Conversely, this is not true when using NHibernate style pagination. When binding to an NHibernate paged list method, the GridView's PageSize value will usually be set to the same size as the paged data list, a mere subset of the total data found in the database (or data source). Behind the scenes, the PageIndex property of the GridView will reset to zero because the number of items bound to its DataSource is less than or equal to the PageSize. Therefore, the GridView is under the impression that the data it receives is not pageable and, in turn, disables any paging features, unaware that more items do indeed exist but were just not provided at that moment. The paging features lost include not just immobilizing navigation links but also removing the availability of the event handlers linked with changing the page index.

Without the convenience of auto-generating navigation links, two options emerge that might help to produce the same desired behavior:

  1. Inherit from the GridView control and attempt to confirm whether or not if any paging methods can be overridden some how or in some way
  2. Create a custom, reusable user control to implement the navigation features

Initially, the easier path was taken by developing a very rudimentary and simple implementation of option # 2. This entailed providing in extremely basic custom controls functionality for navigating between pages using homemade "Previous" and "Next" buttons. Currently, it is not implemented as a shareable user control nor is it able to display the pages counts. I intend on exploring option # 1 a bit more in the event that option # 2 evolves into something more elaborate and unwieldy. Until then, it is a work in progress.

A third option does exist involving the possible use of the ObjectDataSource control. However, that strategy can lead down a less than desirable path for the following reasons:

  1. loss of control of how the data access is managed
  2. ease of maintainability diminishes if any widespread changes were to emerge in the future within areas of the application relying on paging
  3. disrupts and conflicts with how the MCP/MVC methodology is currently applied on the project
  4. increased difficulties in writing and running reliable automated unit tests

All things considered, despite some trade offs and a little bit of work, leveraging NHibernate's ability to do paging can be an area that could contribute significantly in optimizing and improving the performance of a data-intensive web application.

Monday, August 25, 2008

Applying good software development practices to VBA and MS Access (and tools, too!)

After working with TSQL almost exclusively for the last six months the time came last month where I had to make code changes to a Microsoft Access front-end application at work.

Based on what I have learned in the past several years or so regarding software development in .NET, I decided to arm myself for tackling legacy code in a platform and language notoriously known for not being ideal for software development.

IDE Tools

I accidentally came across MZ-Tool a plugin for the VB Editor IDE and wow! This made all the difference in the world. It was nice to find a free tool to inject a little ReSharper-ish support into the rigid VBA IDE. The product apparantly started in the VB\VBA world and when .NET came along it was ported to Visual Studio.NET. (It can actually be considered as a direct competitor to Re#. Note that the .NET version is not free.)

Here are the features I was using actively during coding:
  • Replace In All Projects- This was huge in that it allowed me to actually perform the refactoring technique, 'Rename', with various levels of control. What really made it possible is the actual visual tree view of your code where the text you want to change shows up as well as showing what function/sub where it can be find under. I was not afraid to actually change names of things that were either goofy, confusing or antiquated in coding style into much more meaningful names. In the end, I had significantly less fear in breaking code. (This feature is equivalent to Re# 'Find Usages Advanced')
  • Procedure Callers- Similar to Re# 'Show all usage" for a method or variable (and it's also navigable via a visual tree view)
  • Code Templates- This allows me to create and permanently store boilerplate code that I needed for testing code using VBLiteUnit (see next section). This feature opened my eyes to more actively use and create with 'Live Templates' in Re#.
  • Add Procedure- Slick and fast way to create more boiler-plate code for procs/func.
  • Add Error Handler- This was a gift from the heavens. Being able with a simple keystroke drop in code for error handling in any sub/function. Error handling is horrible in VB/VBA so this significantly helped in reducing the pain.
  • Add Module Header and Add Procedure Header- It's just like having GhostDoc. Adding comments is very important in legacy code.
  • Sort Procedures -Helps organize your code. It is equivalent to Re#'s 'File Structure Popup' which I use all the time for the same reasons: drag-n-drop your code to how you see fit.
  • Private Clipboard- This can be big in legacy code and in a language where you do end up with a lot of cutting and pasting. Actually, better than Re#'s version because you can actually control what is stored in it and can retain it for as long as you like within the duration of your session.
  • Review Source Code- A extremely limited version of code analysis in Re#. It only tells you if a variable, constant, procedure is not being used. But good nonetheless to clean up your code and get rid of cruft.
The following features were interesting but less used day to day:
  • Generate XML Documentation- produces a very nice readable xml doc about your code comparable to XML CHM output in VS
  • Statistics- You can actually see how your lines of code is distributed in your code base.
The one thing I wish MZTools had which IMHO I consider to be one of the two most important refactoring techniques is 'Extract Method' ('Rename Method' being the other). If it had that it would be quite the rock solid refactoring tool for VBA. Nonetheless, MZTool just like Re# in Visual Studio has given me control over my code instead of the code controlling me.

Unit Testing

Being a dedicated practitioner of Test Driven Development (TDD), this was important for me to do on a lot levels. After looking at two options I settled on VBLiteUnit because it is extremely lightweight and the ramp up learning time is short especially if you are familiar with any of the other xUnit frameworks (which is exactly what the author intended in creating it hence the word "Lite" in its name. My belief is that the author might have thought that the existing one out there, VBAUnit, was too bulky and cumbersome to use and maintain. Definitely a much more "pragmatic" and "agile" approach in his solution.)

The author decided to use the VB construct 'Select Case' (think 'Switch') with each leg of your 'Case' defining an individual test. Impressively it works really well (and added bonus is that your test descriptions can be more descriptive and natural in tone because it's just string text. Interesting to note as compared with a blog post on Unit Test Naming Conventions written by a developer I used to worked with)

To implement my new changes, as expected, I did have to do some refactoring of the code that required to be touched. This generally resulted in new testable classes but I had TDD with VBLiteUnit (and MZTools) to lead the way.

All in all it was very satisfying to be able to actually do TDD in Access/VBA. It gave me that same feeling that I get when doing TDD in C#. The platform for a moment felt much more real as software development than it ever had before.

Results

Some lessons were learned of course ("evolve or die"). If you have no choices, resources, etc. in a situation no matter how "trivial" then you still attack your problem 100%. Why? Because you not only get to apply and transfer knowledge and techniques to another area of software development but also you might actually learn some new things that you can in turn use later on. For example,
  • I started using the Immediate Window much more now in Visual Studio.
  • I started using features in Re# that I had not used or dismissed before (like the live templates, copy clipboard, etc.)
  • Design patterns can still be used even with what some might consider a "rudimentary" language. For example, I was actually able to implement a variation of the Repository pattern (ala Eric Evan's DDD) that hydrated a domain object and make it work. Hence if you understand design patterns and how to use them then it does not matter what OO language you are using.
However, I can see why VB developers don't know OOP. One good reason is that VB is lacking in some key features of an OOP language. The biggest one in my opinion is that it does not support implementation inheritance. That was frustrating because I was trying to use a technique that Michael Feathers described in his book, Working Effectively with Legacy Code, to test legacy code. That entails creating a seam in your code by inheriting and then overriding an external dependency. Unfortunately, VB simply just enforces the interface contract but does not carry over the implementation code from the base class to its derived ones.

Nonetheless, using VBA with VBUnitLite and MZTools had actually turn out to be a much more gratifying and, yes, may I even say aloud, "fun" experience than I expected. The main reason beyond the more obvious such as being to do TDD was that I was doing so much TSQL just prior that is was like being transported out of the stone ages. The TSQL language and Microsoft's IDE for it (SQL Server Mgt Studio) was so frustrating to use that working back in a programming language designed for applications even as "minor/toy" and with so much stigma as Access VBA was such a breath of fresh air and extremely invigorating (It was like giving a tablespoon of water to a very thirsty person).

TSQL was and still is such a struggle even with TSQLUnit. It has so much limitations when it comes to creating flow logic, reusable code, and testability. (Granted it is a DSL specialized and intended for manipulation of data and its storage structure but I feel like that the SQL language has not really evolved much in its odd 30 years of existence. In areas that it has changed it ends up resembling other non-SQL languages so what's the point? Perhaps it's time for a better replacement language(s)?) In the future, any application developer that I encounter again who says "sprocs are the way to go" will get an earful and maybe more.

TSQL is so painful in its existing form that, believe it or not, I rather be working in VBA than in TSQL if those were my only options!! I know that sounds crazy and shocking but that's how much I prefer not to have to do database development and deal with TSQL. I'll leave it to the folks who actually enjoy it. But sadly it is the bulk of my work these days. (The one thing I do like about it is it's language support for "dynamic SQL" but that is not enough of a motivation for me.)

Who knows? I went from C# --> SQL --> VBA --> SQL in the last six months. If I had gone from C# --> VBA I probably would have a different opinion and experience. Hard to tell in retrospect.

Don't worry I still do C# and have been doing it for the last six months in parallel on side projects and supporting processes at work. Unfortunately, I am back to doing TSQL for the next several months. Aargh!!!

Final Thoughts

What was the point of all of this other than to rant on about a language no one cares about if you consider yourself a "serious" programmer? Well, just like VB6, Microsoft plans on retiring VBA and possibly allowing Office apps to be supported by any.NET language including C#!! Therefore, the tools mentioned above are "dead" in the sense no new development is going be done on them especially that their respective authors have appeared to have moved on from doing any new releases.

IMHO, I think what Microsoft really should do is to allow dynamic languages to support their Office apps. That is where dynamic languages (ex. Ruby, Python, BOO, etc.) seem like such a natural fit and obvious for the nature of that work. Just imagine you can write code very quickly with ever-changing requirements and it does not have to perform fast (as compared with statically typed languages.) Seems like such a "no-brainer". (maybe in the far, not-too-distant future, power users could even be writing MS Excel or Word macros in F#? Just imagine that!)

Monday, July 21, 2008

Data Validation, Business Rules, and the Notification Pattern

On a previous project, I had encountered some unnecessarily long 'Save' methods in various ASP.NET web pages that contained numerous validations of each input value from the UI page. Within the body of those methods it would run through all of those vaildations before it would finally reach the decision as to whether to commit changes to the database or not. (for example, something like check the length of the first name of the user is less than 20, etc.) In general, the methods ended being a bit hard to follow especially if you needed to make a change to them.

Another developer who I used to work with had mentioned to me data validation shouldn't even be in the Presenter Class of a traditional MVP/MVC implementation. He also mentioned his approach at the time (which if I recall correctly was something like exception guards?) as well as Jimmy Nilsson's approach towards data validation as described in his book, Applying Domain-Driven Design and Patterns: With Examples in C# and .NET. In the meantime, I had been recently researching how to do MVP with the ASP.NET custom validators since we are using these on our project at work and was trying to find a "better" way to handle rudimentary validation.

After some "blood, sweat, and tears" I think I was able to successfully apply Fowler's Notification Pattern to solve this "issue". The notification pattern tries to manage the capturing of error messages as it relates to data validation that are specific to domain objects and are generally outputted to the end-user. (An example is if an email address is required on a submit form. If the user skips over that then on 'submit' a message is displayed such as "An email address is required...blah blah) It all started while re-reading one of Jeremy Miller's post on validation as part of his CAB series . His post lead to both Fowler's Notification Pattern and two posts from Jean-Paul S. Boodhoo's blog (Part I and Part II). Those served as my blueprints for my implementation.

Essentially, I went through each of their slightly differing approaches to see what I could use. The core of what I ended up with borrows heavily from Fowler with most of my changes just renaming things to suit my liking. Fowler always writes with such clarity and without the cruft and his code examples are so easy to follow that his version was the main driver for what I wanted to do. Miller's and JP took the pattern to another level but it was too much for what I wanted. My goal was to keep it simple of course and let it evolve on its own (BUFD bad!) I initially developed it on a separate test project. Once that worked I then implemented it in our project at work somewhat seamlessly.

I first created the initial base classes that are the foundation of this pattern and that can be re-used on any project. Below are their interfaces:



/// Specific business rule error that provides a specific message about the broken business rules.
public interface IBusinessRuleError
{
/// Gets or sets the name of the property that causes the error.
string PropertyName { get; set; }

/// Gets or sets the specific error message.
string Message { get; set; }
}

/// Set of Business Rules used by Domain Objects that captures and stores errors.
public interface IBusinessRules
{
/// Gets or sets the business rule errors.
IList Errors { get; set; }

/// Gets a value indicating whether this instance has any business rule errors.
bool HasErrors { get;}

/// Determines whether the specified set of business rules contains error.
bool ContainsError(IBusinessRuleError ruleError);
}


Basically 'BusinessRules' manages a collection of individual 'BusinessRule'. The business rule contains the error message and it also contains the name of the specific property for the error that will be used later to when mapping it back to a specific UI control.

Now I added BusinessRules to the abstract DomainObject class and expose it as a property. Initially it was hard-coded into my domain object but at work I decided to pull it into its own class that could then be instantiated internally and, if need be, injected in as a dependency into the domain object base class (as you know ideal for mock testing it!) Here is what I call the "validator"




public interface IDomainObjectValidator
{
/// Runs the validation of each business rule.
/// Each derived class can override this method to define its own
/// set of validation rules.
void RunValidation();

/// Gets the business rules.
IBusinessRules Rules { get;}

/// Gets a value indicating whether this instance is valid based on whether any business rules failed.
bool IsValid

/// Determines whether [is null or blank] [the specified item to test].
bool IsNullOrBlank(string itemToTest);

/// Fails if condition to test is true.
void FailIf(bool conditionToTest, IBusinessRuleError error);

/// Fails if is null or blank the condition to test is true.
void FailIfNullOrBlank(string itemToTest, IBusinessRuleError error);



This interface has the RunValidation method whose purpose is to cycle through it each business rule that the derived class is responsible to implement for itself. In addition, the interface also has some basic, re-usable validation tests of these methods such as IsNullOrBlank, FailIf, etc. (courtesy of Fowler) (NOTE: What struck me very quickly was the similarities between these generic methods and with the Asserts of NUnit. It dawned on me when I started to implement a new one that checked the difference for dates such IsBetween(string startDate, string endDate). Mmm...looks a lot like NUnit's Is Constraint model. In fact, 'FailIf' looks like a special case of Assert.That. I'm wondering whether some framework exists out for me to use instead of trying to create and maintain my own.)

In turn, the validator's members are delegated and exposed as members of the domain class itself:



// domain object abstract class
private readonly IDomainObjectValidator _validator;

public DomainObject()
{
_validator = new DomainObjectValidator();
}

public DomainObject(IDomainObjectValidator validator)
{
_validator = validator;
}

public bool IsValid
{
get { return _validator.IsValid; }
}

public IBusinessRules Rules
{
get { return _validator.Rules; }
}

public virtual void RunValidation()
{
_validator.RunValidation();
}

public bool IsNullOrBlank(string itemToTest)
{
return _validator.IsNullOrBlank(itemToTest);
}

public void FailIf(bool conditionToTest, IBusinessRuleError error)
{
_validator.FailIf(conditionToTest, error);
}

public void FailIfNullOrBlank(string itemToTest, IBusinessRuleError error)
{
_validator.FailIfNullOrBlank(itemToTest, error);
}



Once that was done then it was time to actually use it for a specific domain object. So I have a domain object named 'Question' that makes up a 'Quiz':



public interface IQuestion
{
/// The text of the question itself.
/// For example, "How old are you?"
string Description { get; set; }

/// Point value of the question if quiz taker gets it correct.
int MaxPointValue { get; set; }

/// Sequence # of the question within a quiz.
int SequenceNumber { get; set; }

// Bunch of other members...

}


So in the actual Question class, I override and implement the 'RunValidation' method with "rules/errors" specific to 'Question':


// Question class
public override void RunValidation()
{
// validation # 1
FailIfNullOrBlank(_description, new BusinessRuleError("Description", "Question description must contain a value."));

// validation # 2
if (_description != null)
{
FailIf(_description.Length > 10,
new BusinessRuleError("Description", "Question description can not be longer than 10 characters."));
}

// validation # 3
FailIf(_maxPointValue > 100, new BusinessRuleError("MaxPointValue", "Maximum Point Value can not exceed 100."));

// ....
// validation # 100...
}



So this is where it all happens. Basically, this is where all the business rules that require validation for 'Question' is kept and maintained. Not in the UI, not in the presenter, not in the database or not anywhere else. Right where it should be. What's great is how nice is it to itemize and view all of your business rules in one place. The best part is unit testing this (which you really can't do well at all if it's in the presenter). Here are one of the tests:



// Question test fixture

[Test][Category("Data Validation")]
public void DoesContainBrokenRuleWhenDescriptionIsNull()
{

Question question = new Question();
question.Description = null;
question.RunValidation();

IBusinessRuleError descriptionError = new BusinessRuleError("Description", "The description for this 'Question'

must contain a value.");
Assert.That(question.Rules.ContainsError(descriptionError), "Does not contain Description error.");
Assert.That(question.IsValid, Is.False, "Question is valid.");
}




How cool is that? I especially like the clarity of this code line:

question.Rules.ContainsError(descriptionError)

Here are a few more tests:




[Test][Category("Data Validation")]
public void DoesContainBrokenRuleWhenDescriptionLengthGreaterThan10()
{
Question question = new Question();

question.Description = "1234567891011";
question.RunValidation();

BusinessRuleError descriptionError = new BusinessRuleError("Description", "The description for this 'Question' can not be longer than 10 characters.");
Assert.That(question.Rules.ContainsError(descriptionError), "Does not contain Description error.");
Assert.That(question.IsValid, Is.False, "Question is valid.");
}

[Test][Category("Data Validation")]
public void DoesContainBrokenRuleWhenMaxPointValueExceeds100()
{
Question question = new Question();

question.MaxPointValue = 101;
question.RunValidation();

BusinessRuleError maxPointValueError = new BusinessRuleError("MaxPointValue", "Maximum Point Value for this 'Question' can not exceed 100.");
Assert.That(question.Rules.ContainsError(maxPointValueError), "Does not contain MaxPointValue error.");
Assert.That(question.IsValid, Is.False, "Question is valid.");
}



By implementing this at work the app's Domain model is now slightly less anemic. However, the auto-gen of partial classes presented an issue that I was not too happy with. The MyGeneration template is currently set up to read from the database the constraints of the columns and then it's hard-coded directly into the property setters (which includes throwing exceptions). This forces the trapping of the validation error to occur OUTSIDE of the domain object which goes against this implementation of the pattern. So unless I modify the template to remove this from the setters (or at least move into some private method) I had to circumvent updating via the setters and use some methods as so:

question.Description = "My Description"; question.MaxPointValue = 101;

becomes using overloads

question.UpdateDescriptionUsingValidation("1234567891011") question.UpdateMaxPointValueUsingValidation (101)

and/or

question.UpdateUsingValidation("1234567891011", 101)

Not really what I wanted but it works for now until I can resolve that auto-gen issue (another reason why auto-gen can sometimes be an anti-pattern)

So let's see the entity 'Question' actually used in a Controller/Presenter context:




// Presenter class
public void SaveChanges()
{
IQuestion question = new Question();
question.Description = _view.Description;
question.MaxPointValue = _view.MaxValuePoint;
question.SequenceNumber = _view.SequenceNumber;

question.RunValidation();
if (question.IsValid)
{
_dao.SaveOrUpdate(question);
_view.DisplaySuccess("The question has now been saved.");
}
else
{
_view.DisplayErrors(question.Rules.Errors);
}

}



Now how does that compare with one of the original LONG save methods? The intent, readability, and therefore maintainability is light years better. (NOTE: As a side note, I had to use the NHibernate's ISession.Evict() to prevent the entity from being persisted to the db.)

OK, finally the UI/View/Code-Behind



// View class
public void DisplayErrors(IList errors)
{
foreach (IBusinessRuleError error in errors)
{
if (error.PropertyName.Equals("Description"))
{
_ctlDescriptionValidator.ErrorMessage = error.Message ;
_ctlDescriptionValidator.IsValid = false;
}

if (error.PropertyName.Equals("MaxPointValue"))
{
_ctlMaxPointValidator.ErrorMessage = error.Message;
_ctlMaxPointValidator.IsValid = false;
}
}
}



The controls '_ctlDescriptionValidator' and '_ctlMaxPointValidator' are ASP.NET custom validators that are now really dumbed down. I also used the asp.net 'ValidationSummary' control on the web page without needing to do hardly any wiring up. Here is some of the related HTML:



<form id="form1" runat="server">
<asp:ValidationSummary ID="_ctlValidationSummary" runat="server" />

<asp:Label ID="_lblSuccessMessage" runat="server"></asp:Label><div>
Description
<asp:TextBox ID="_txtDescription" runat="server" >
</asp:TextBox>
<asp:CustomValidator ID="_ctlDescriptionValidator" runat="server" ControlToValidate="_txtDescription"
ErrorMessage="" OnServerValidate="_ctlDescriptionValidator_ServerValidate">*</asp:CustomValidator><br />
Max Point Value
<asp:TextBox ID="_txtMaxPointValue" runat="server" >
</asp:TextBox>
<asp:CustomValidator ID="_ctlMaxPointValidator" runat="server" ControlToValidate="_txtMaxPointValue"
ErrorMessage="" >*</asp:CustomValidator>



All in all it does not matter if I use the asp.net validators, my own custom message controls, or whatever. The data validation is not tightly coupled with the UI by using the deadly combo of MVP and the notification pattern!!!

I'm certain that aspects of my implementation can be improved and/or extended in some fashion. There are some things I debated as to which is the best approach but I can go into more detail later (for example, I mulled over a couple of other ways on how to pass the messages to the View but settled on the one above. Another was possiblly using reflection to set the property names in the error messages...but like I said I wanted to keep it simple for now. )