Wednesday, November 23, 2011

Castle Validator Component for Beginners

Introduction

Castle validator component is an open source validation framework that uses property attribute to specify validation rules on class. In this article, I will explain how to use it and how it works from inside out.

Castle Validator Sample

Let’s start by looking at a class that has Castle validator attributes on it.
public class User
{
 [ValidateLength(3, 10)]
 public string Name { get; set; }

 [ValidateDate("Invalid date format.")]
 public string DOB { get; set; }
 
 [ValidateEmail("Invalid email address.")]
 [ValidateLength(5, 20, RunWhen = RunWhen.Insert)]
 public string Email { get; set; }
}
There are three validator attributes used here: ValidateLengthValidateDate, and ValidateEmail.
  • ValidateLength attribute is used to specify a fixed length or length range for the property.
  • ValidateDate attribute is used to specify the property as date type.
  • ValidateEmail attribute is used to specify the property allows email format.
A castle validator attribute inherits from AbstractValidationAttribute that ultimately inherits fromAttribute and IValidatorBuilder. The IValidatorBuilder is the contract that Castle validator attribute must implement. By implementing this contract, validate attribute is able to create a corresponding validator for validation process by ValidatorRunner.
ValidateLengthAttributeClass.gif
The following code sample shows how to use ValidatorRunner validates the class that has validator attributes on it.
[TestMethod]
public void CreateUser_User_NoError()
{
 //Assign
 ValidatorRunner runner = new ValidatorRunner(new CachedValidationRegistry());

 User user = new User();
 user.Name = "Henry";
 user.DOB = "1/1/2011";
 user.Email = "henry@aaa.com";

 //Act
 bool result = runner.IsValid(user);

 //Assert
 Assert.IsTrue(result);
 ErrorSummary errorSummary = runner.GetErrorSummary(user);
 Assert.AreEqual(0, errorSummary.ErrorsCount);
}
The ValidatorRunner is the class that has knowledge of how to execute validators. All validators are fromCachedValidationRegistry. When runner is calling IsValid method, it will ask CachedValidationRegistryfor all validators of the current object. Then use Validation Performer internally to perform validation process on the object. The result is stored in ErrorSummary for later use.
IsValid.gif

Some Good Validation Features

As a useful validation framework, some features are designed for advanced scenarios. I am not going to go through the entire advanced features, just a few of them that I feel make Castle Validator distinguished with other validation framework.
RunWhen
If the validation is only legitimate for certain situations, you can use RunWhen parameter with validator attribute to specify when you want to have this validation. RunWhen is an enum that can be EverytimeInsertUpdate, and Custom. The default is Everytime. Following is a Validator attribute that has RunWhen used.
[ValidateLength(5, 20, RunWhen = RunWhen.Insert)]
public string Email { get; set; }
Let’s see how to validate it:
[TestMethod]
public void CreateUser_User_EmailErrorWhenInsert()
{
 //Assign
 ValidatorRunner runner = new ValidatorRunner(new CachedValidationRegistry());

 User user = new User();
 user.Name = "Henry";
 user.DOB = "1/1/2011";
 user.Email = "henry@aaaaaaaaaaaaaaa.com";

 //Act
 bool result = runner.IsValid(user, RunWhen.Insert);

 //Assert
 Assert.IsFalse(result);
 ErrorSummary errorSummary = runner.GetErrorSummary(user);
 Assert.AreEqual(1, errorSummary.ErrorsCount);
 Assert.AreEqual("Field must be between 5 and 20 characters long", 
   errorSummary.ErrorMessages[0]);
}
Execution Order
When there are multiple validator attributes applied on a property, you can use execution order on validator attribute to specify which one executes first.
[ValidateNonEmpty(FriendlyName="Country Field", ExecutionOrder=1)]
[ValidateLength(3, 10, "Invalid country length.", ExecutionOrder=2)]
public string Country { get; set; }
ValidateSelf
If you have a pretty complicated validation logic and don’t want to create a custom validator attribute for it because it’s used once in that particular class only, you can create a method to do validation and use ValidateSelfattribute to let Castle validator component find it.
[ValidateSelf()]
public void Validate(ErrorSummary errorSummary)
{
 if ((Street == null || !Street.Equals
  ("Main Street", StringComparison.InvariantCultureIgnoreCase)) && 
  (City != null && City.Equals("Big City")))
  errorSummary.RegisterErrorMessage("Street", 
   "Street name must be Main Street when City is Big City.");

}

Custom Validation

For situations in which you want to have specific business logic validation and also want to apply it to multiple places, custom validation is the best option. To create a custom validation, you need to create a custom validator attribute and custom validator. The validator attribute is used to decorate property and the validator is the one doing validation.
ValidateCityAttribute
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter | 
 AttributeTargets.ReturnValue, AllowMultiple = true), CLSCompliant(false)]
[Serializable]
class ValidateCityAttribute : AbstractValidationAttribute
{
 private readonly IValidator _validator;

 public ValidateCityAttribute()
 {
  _validator = new CityValidator();
 }

 public ValidateCityAttribute(string errorMessage)
  : base(errorMessage)
 {
  _validator = new CityValidator();
 }

 public override IValidator Build()
 {
  base.ConfigureValidatorMessage(_validator);
  return _validator;
 }
}
CityValidator
[Serializable]
public class CityValidator : AbstractValidator
{
 public override bool SupportsBrowserValidation
 {
  get
  {
   return false;
  }
 }

 public CityValidator()
 {
 }

 public override bool IsValid(object instance, object fieldValue)
 {
  if (fieldValue == null)
  {
   return true;
  }
  if (fieldValue != "Big City" && fieldValue != "Small City")
  {
   return false;
  }
  else
  {
   return true;
  }
 }

 protected override string BuildErrorMessage()
 {
  return "City must be Big City or Small City.";
 }
}
Let’s also take a look at the class diagram:
CustomValidation.gif

Summary

Castle validator component is a very simple and flexible validation framework. It can be used in all kinds of applications: Web, Windows, Console, etc. Even better, this validation framework allows customization easily and deeply to fit your exact need.

Using the Code

The code is developed in Visual Studio 2010. Castle validator component (Castle.Components.Validator.dll) 4.0.30319 is used with the code.

Using SQL Server 2012 T-SQL New Features

Introduction

SQL Server 2012 “Denali” is the next major release of Microsoft database server. There are some new features are added to T-SQL to make common tasks much easier. I will show how to use some of the new features in this article.

Sequence

Generating a sequence number, a.k.a. auto number, is a common task in an enterprise application. For a single table, you can specify identity field. But, if you want to have database wide sequential number, then you must devise something by yourself before SQL Server 2012. One solution to this problem is to create a table that has a numeric field can be used to store sequential number, then use SQL to increase it every time used one. In SQL Server 2012, we have a new solution - use Sequence.
Create Sequence
To create a Sequence in SQL Server 2012 is very simple. You can create it with SQL Server Management Studio or T-SQL.
1. Create Sequence with SQL Server Management Studio
In Object Explorer window of SQL Server Management Studio, there is a Sequences node under Database -> [Database Name] -> Programmability. You can right click on it to bring up context menu, and then choose New Sequence… to open the New Sequence window. In New Sequence window, you can define the new Sequence, like Sequence Name, Sequence schema, Data type, Precision, Start value, Increment by, etc. After entered all required information, click OK to save it. The new Sequence will show up in Sequences node.
2. Create Sequence with T-SQL
The following T-SQL script is used to create a new Sequence
CREATE SEQUENCE DemoSequence
START WITH 1
INCREMENT BY 1;
Use Sequence
The new NEXT VALUE FOR T-SQL keyword is used to get the next sequential number from a Sequence.
SELECT VALUE FOR DemoSequence
One thing I want to mention in here is Sequence doesn’t support transaction, if you run this script
BEGIN TRAN
SELECT NEXT VALUE FOR dbo.DemoSequence
ROLLBACK TRAN
You can see even transaction is rolled back at the end. The NEXT VALUE FOR will still return the next sequential number. This behavior is consistent with identity field.

Page Data

A common situation for displaying page is how to display large amount of data in DataGrid. Before, programmer usually uses the paging feature of DataGrid to handle this situation. Therefore, by choosing different page number, different set of data are displayed on the screen. However, how to retrieve data from database is multiplicity. Developer could
1. Retrieve all data from database, and then let DataGrid to only display the current page data.
2. Retrieve the current page data from database by using temp table.
3. Retrieve the current page data from database by using ROW_NUMBER() function.
The SQL Server 2012 provided a new way to retrieve current page data from database,
SELECT *
FROM Customers
ORDER BY CustomerID
OFFSET 10 ROWS
FETCH NEXT 10 ROWS ONLY;
The OFFSET keyword and FETCH NEXT keyword allow developer to only retrieve certain range data from database. If you compare this script with ROW_NUMBER() function introduced in SQL Server 2008, you can see this script is shorter and more intuitive.
SELECT *
FROM (
SELECT ROW_NUMBER() OVER(ORDER BY CustomerID) AS sequencenumber, *
FROM Customers) AS TempTable
WHERE sequencenumber > 10 and sequencenumber <= 20

Exception Handling

SQL Server 2005 introduced TRY CATCH block to handle exception in T-SQL. The TRY CATCH block is similar to whatever in C# language except you need always raise a new exception after catches it. There is no way to simply re-throw it.
A sample of T-SQL script with exception handling in SQL Server 2005
BEGIN TRY
 BEGIN TRANSACTION – Start the transaction

 -- Delete the Customer
 DELETE FROM Customers
 WHERE EmployeeID = ‘CACTU’

 -- Commit the change
 COMMIT TRANSACTION
END TRY
BEGIN CATCH
 -- There is an error
 IF @@TRANCOUNT > 0
  ROLLBACK TRANSACTION

 -- Raise an error with the details of the exception
 DECLARE @ErrMsg nvarchar(4000), @ErrSeverity int
 SELECT @ErrMsg = ERROR_MESSAGE(),
  @ErrSeverity = ERROR_SEVERITY()

 RAISERROR(@ErrMsg, @ErrSeverity, 1)
END CATCH
In SQL Server 2012, by using Throw keyword, the above script will be changed to this
BEGIN TRY
 BEGIN TRANSACTION -- Start the transaction

 -- Delete the Customer
 DELETE FROM Customers
 WHERE EmployeeID = ‘CACTU’

 -- Commit the change
 COMMIT TRANSACTION
END TRY
BEGIN CATCH
 -- There is an error
 ROLLBACK TRANSACTION

 -- Re throw the exception
 THROW
END CATCH
Also, you can use Throw to replace RAISERROR function
THROW 51000, ‘The record does not exist.’, 1;

Enhanced EXECUTE keyword

The EXECUTE keyword is used to execute a command string. The previous version SQL Server only has WITH RECOMPILE option to force new plan to be re-compiled. The SQL Server 2012 dramatically improved this part. The option part is like this right now
[ WITH <execute_option> [ ,…n ] ]

<execute_option>::=
{
 RECOMPILE
 | { RESULT SETS UNDEFINED }
 | { RESULT SETS NONE }
 | { RESULT SETS ( <result_sets_definition> [,…n] ) }
}

<result_sets_definition> ::=
{
 (
  { column_name
    data_type
  [ COLLATE collation_name ]
  [ NULL | NOT NULL ] }
  [,…n ]
 )
 | AS OBJECT
  [ db_name . [ schema_name ] . | schema_name . ]
  {table_name | view_name | table_valued_function_name }
 | AS TYPE [ schema_name.]table_type_name
 | AS FOR XML
}
</result_sets_definition></result_sets_definition></execute_option></execute_option>
The way to use the new added options is like this
EXEC CustOrderDetail ‘2’
WITH RESULT SETS
(
 (
 ProductName1 varchar(100),
 Unitprice1 varchar(100),
 Quantity1 varchar(100),
 Discount1 varchar(100),
 ExtendedPrice1 varchar(100)
 )
);

Get Metadata

Application sometimes needs more insight of the SQL script result set. In the past, you need to write a complicate script to query system tables or views, e.g. sys.objects, to get all the information. In SQL Server 2012, the new system stored procedure sp_describe_first_set makes the work trivial.
sp_describ_first_result_set @tsql = N’SELECT * FROM customers’

Summary

There are more T-SQL new features in the upcoming SQL Server 2012. Majority of them are designed to improve development efficiency and reduce development effort.

Installing Drupal on Windows 7

Introduction

Drupal is an open source content management platform powering millions of websites and applications. In this article, I will show you how to install Drupal 7.8 on Windows 7 with Apache HTTP Server 2.2.21, PHP 5.3.8 and MySQL 5.5.1.

Before Installation

Before you start the installation process, you must have the following installer or package downloaded,
1. Drupal 7.8 (http://www.drupal.org)
2. Apache HTTP Server 2.2.21 (http://httpd.apache.org/)
3. PHP 5.3.8 (http://windows.php.net)
4. MySQL 5.5.1 (http://www.mysql.com/) 

Install Apache HTTP Server

1. Run httpd-2.2.21-win32-x86-no_ssl.msi to start the Apache HTTP Server installation.
InstallApache.gif
2. Enter Network Domain, Server Name, and Adminstrator's Email Address
InstallApacheServerInformation.gif
Because Apache is used in my laptop as development environment, so I put my laptop name “laptop” in both Network Domain and Server Name textbox and entered a fictional email “webmaster@laptop” in Administrator’s Email Address textbox.
3. Follow the wizard to complete configuration steps and clicked Install button to start the installation, you will probably see following error messages in command windows.
InstallApacheError.gif
http:exe: Could not reliably determine the server’s fully qualified domain name, using 192.168.1.105 for ServerName
(OS 10013)An attempt was made to access a socket in a way forbidden by its access permissions. : make_sock: could not bind to address 0.0.0.0:80
no listening sockets available, shutting down
The ServerName “laptop” can’t be recognized error is because I haven’t map “laptop” to my laptop IP yet. The port 80 is forbidden error is because access permission reason. I will use a different port 8888 instead 80 later.
4. Follow the rest of installation wizard steps to complete the installation.
5. Use Windows “Search programs and files” box to find the Notepad.
FindNotepad.gif
Right click on Notepad and select “Run as administrator” to open the Notepad.
RunAsAdministrator.gif
The reason to do this is because Windows 7 has a more restrict security feature - UAC, in order to change a system file, we needs administrator right. Another way to allow change a system file is turning off UAC.
6. Open httpd.conf file in C:\Program Files\Apache Software Foundation\Apache2.2\conf.
7. Find the line
Listen 80
And change it to
Listen 8888
8. Save and close httpd.conf file.
9. Open hosts file in C:\Windows\System32\drivers\etc.
10. Add a line in it
127.0.0.1 laptop
11. Open a browse and enter http://laptop:8888. You suppose to see this
ItWorks.gif

Install PHP

1. Run php-5.3.8-Win32-VC9-x86.msi to start the installation.
InstallPHP.gif
2. Select Apache 2.2.x Module
InstallPHPWebServerSetup.gif
3. Follow the rest of installation wizard steps to complete installation.

Install MySQL

1. Run mysql-installer-5.5.15.0.msi to start the MySQL installation.
InstallMySQL.gif
2. Follow the wizard to complete MySQL installation.

Install Drupal

1. Create a server folder: C:\server.
2. Create a www folder in C:\server.
3. Create a Demo folder in C:\server\www.
4. Extract drupal-7.8.zip into the Demo folder.
5. Open httpd.conf in C:\Program Files\Apache Software Foundation\Apache2.2\conf
6. Find the line
#LoadModule rewrite_module modules/mod_rewrite.so
Change it to
LoadModule rewrite_module modules/mod_rewrite.so
7. Add following if not in httpd.conf (PHP windows installer will add PHP5 section, but I found it’s not always success) 
#PHP5
LoadModule php5_module "C:\Program Files\PHP\php5apache2_2.dll"
PHPIniDir "C:\Program Files\PHP"
8. Find the line
AddType application/x-gzip .gz .tgz
Add following
AddType application/x-httpd-php .php
AddType application/x-httpd-php-source .phps
9. Find the line
DirectoryIndex index.html
Change it to
DirectoryIndex index.html index.php
10. Find the line
#Include conf/extra/httpd-vhosts.conf
Change it to
Include conf/extra/httpd-vhosts.conf
11. Save and close httpd.conf.
12. Open http-vhosts.conf in C:\Program Files\Apache Software Foundation\Apache2.2\conf\extra
13. Find the line
NameVirtualHost *:80
Change it to
NameVirtualHost *:8888
14. Find the section
<VirtualHost *:80></VirtualHost>
There are two matched sections. 
Change the first section to
<VirtualHost *:8888>
    ServerAdmin webmaster@laptop
    DocumentRoot "C:\Program Files\Apache Software Foundation\Apache2.2\htdocs"
    ServerName laptop
    ServerAlias laptop
    ErrorLog "logs/laptop-error.log"
    CustomLog "logs/laptop-access.log" common
</VirtualHost>
Change the second section to
<VirtualHost *:8888>
    ServerAdmin webmaster@demo.com
    DocumentRoot "C:/Server/www/Demo "
    ServerName demo.com
    ServerAlias www.demo.com
    ErrorLog "logs/demo-error.log"
CustomLog "logs/demo-access.log" common
<directory "C:/Server/www/Demo">
AllowOverride All
Options Indexes FollowSymLinks
Order allow,deny
Allow from all
</directory>
</VirtualHost>
15. Save and close httpd-vhosts.conf.
16. Restart Apache HTTP Server.
17. Open hosts in C:\Windows\System32\drivers\etc.
18. Add line
127.0.0.1 www.demo.com
19. Save and close hosts file.
20. Open MySQL Workbench 5.2 CE from Program menu MySQL group.
MySQLWorkbench.gif
21. Click Local instance MySQL55 to open the database.
MySQLLocalInstance.gif
22. Click Add schema to add a new schema Demo
23. Run script in Query window to create demoadmin acount
CREATE USER 'demoadmin'@'localhost' IDENTIFIED BY 'demoadmin123';
24. Run script in Query window to grant permission to demoadmin account
GRANT ALL ON demo.* TO 'demoadmin'@'localhost';
25. Open http://www.demo.com:8888 in browser. You should see drupal installation page.
InstallDrupal.gif
26. Follow the wizard to complete installation. Your Drupal site is ready.
CompleteInstallDrupal.gif

Summary

Installing Drupal on Windows 7 is not a simple work for a beginner. You need to use trial and error to figure out how to install and configure each component. I was stuck on PHP configuration for one week. Hope this article could save a little bit your time.