sharing about .NET and technology RSS 2.0
# Thursday, March 01, 2007

CSLA.NET framework from Lhotka contains a lot of mechanisms for adding validations and business rules. Through CSLA.NET you can easily provide your own custom rules. Enterprise Library v3.0 now also contains a validation application block (VAB) that can be used through attributes and even from a configuration file.

The two validation mechanisms of validation are complementary. This can be done by adding a custom rule that uses the ValidationFactory of the VAB. This means we have something like:

public class VABRules
{
    public class VABRuleArgs : RuleArgs
    {
        private string _ruleset;

        public string Ruleset
        {
            get { return _ruleset; }
        }


        public VABRuleArgs(string propertyName) : this(propertyName, null)
        {
        }

        public VABRuleArgs(string propertyName, string ruleset) : base(propertyName)
        {
            _ruleset = ruleset;
        }
    }

    public static bool VABValid<T>(object target, RuleArgs e)
    {
        Validator<T> validator = ValidationFactory.CreateValidator<T>(((VABRuleArgs)e).Ruleset);

        if (validator == null)
            return true;

        ValidationResults results = validator.Validate(target);

        if (results == null)
            return true;

        foreach (ValidationResult result in results)
        {
            if (result.Key == e.PropertyName)
            {
                e.Description = result.Message;
                return false;
            }
        }

        return true;
    }
}

Having the VAB rule we simply need to decorate our properties with the validation attributes of VAB and an override of the AddBusinessRules method is needed to take into account the VAB rules. For example we can define a customer business object as follow:

[Serializable()]
public class Customer : Csla.BusinessBase<Customer>
{
    private int _id = 0;
    private string _firstName = string.Empty;
    private string _email = string.Empty;
    private int _rewardPoints;
    private string _countryCode = string.Empty;

    [Browsable(false), System.ComponentModel.DataObjectField(true, true)]
    public int Id
    {
        get
        {
            CanReadProperty("Id", true);
            return _id;
        }
    }

    [NotNullValidator(MessageTemplate="First Name may not be empty")]
    [StringLengthValidator(1, 60, MessageTemplate = "First Name must be between 1 and 60 characters long")]
    public string FirstName
    {
        get
        {
            CanReadProperty("FirstName", true);
            return _firstName;
        }
        set
        {
            CanWriteProperty("FirstName", true);
            if (!_firstName.Equals(value))
            {
                _firstName = value;
                PropertyHasChanged("FirstName");
            }
        }
    }

    [RegexValidator(@"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*")]
    public string Email
    {
        get
        {
            CanReadProperty("Email", true);
            return _email;
        }
        set
        {
            CanWriteProperty("Email", true);
            if (!_email.Equals(value))
            {
                _email = value;
                PropertyHasChanged("Email");
            }
        }
    }

    [Int32RangeValidator(0, 1000000, MessageTemplate = "Rewards points cannot exceed 1,000,000")]
    public int RewardPoints
    {
        get
        {
            CanReadProperty("RewardPoints", true);
            return _rewardPoints;
        }
        set
        {
            CanWriteProperty("RewardPoints", true);
            if (!_rewardPoints.Equals(value))
            {
                _rewardPoints = value;
                PropertyHasChanged("RewardPoints");
            }
        }
    }

    [NotNullValidator(MessageTemplate = "Country may not be empty")]
    public string CountryCode
    {
        get
        {
            CanReadProperty("CountryCode", true);
            return _countryCode;
        }
        set
        {
            CanWriteProperty("CountryCode", true);
            if (!_countryCode.Equals(value))
            {
                _countryCode = value;
                PropertyHasChanged("CountryCode");
            }
        }
    }

    protected override object GetIdValue()
    {
        return _id;
    }

    protected override void AddBusinessRules()
    {
        ValidationRules.AddRule(VABRules.VABValid<Customer>, new VABRules.VABRuleArgs("FirstName"));
        ValidationRules.AddRule(VABRules.VABValid<Customer>, new VABRules.VABRuleArgs("Email"));
        ValidationRules.AddRule(VABRules.VABValid<Customer>, new VABRules.VABRuleArgs("RewardPoints"));
        ValidationRules.AddRule(VABRules.VABValid<Customer>, new VABRules.VABRuleArgs("CountryCode"));
    }
}
Thursday, March 01, 2007 2:39:25 PM (Romance Standard Time, UTC+01:00) -  # -  Comments [3] -
.NET | CSLA.NET | EntLib
# Saturday, December 30, 2006

The Validation Application Block (VAB) of the upcoming Enterprise Library v3, uses attributes to describe validations. This gives us for example the opportunity to generate ASP.NET validators based on the attributes decorated on the properties.

Take for example the NotNullValidator of VAB, this can be translated to a RequiredFieldValidator, whereas the RegexValidator can be translated to RegularExpressionValidator. You can go further with the NotNullValidator and mark required fields with a different backcolor and adding an asterix (*) to the end of the control.

I am big fan of the DetailsView control, you can simply bind a DataSource control to it, and it will automatically provide you with a caption to each control and two-way binding. Below you find an example how you can extend the BoundField control, that investigates the NotNullValidator attribute of VAB. Note that I am currently extending it for the other set of validators and in a more OO way. More info will follow later.

public class BoundFieldEx : System.Web.UI.WebControls.BoundField
{      
    public override void InitializeCell(
        DataControlFieldCell cell, DataControlCellType cellType, 
        DataControlRowState rowState, int rowIndex)
    {
        base.InitializeCell(cell, cellType, rowState, rowIndex);

        if ((((rowState & DataControlRowState.Edit) != DataControlRowState.Normal) && !this.ReadOnly) || 
             ((rowState & DataControlRowState.Insert) != DataControlRowState.Normal))
        {
            TextBox textBox = null;

            if (cell != null && cell.Controls.Count > 0)
                textBox = cell.Controls[0] as TextBox;

            if (textBox != null)
            {
                Type dataItemType = null;
                
                if (DataBinder.GetDataItem(base.Control) != null)
                    dataItemType = DataBinder.GetDataItem(base.Control).GetType();

                if (dataItemType != null)
                {                        
                    ValidatorAttribute attribute = IsRequired(dataItemType, base.DataField);

                    if (attribute != null)                        
                    {
                        string textBoxID = this.DataField;
                        textBox.ID = textBoxID;

                        RequiredFieldValidator validator = new RequiredFieldValidator();
                        validator.ControlToValidate = textBoxID;
                        validator.ID = string.Concat("RequiredValidatorOf", textBoxID);
                        validator.Display = ValidatorDisplay.Dynamic;                            
                        validator.ErrorMessage = attribute.MessageTemplate;
                        cell.Controls.Add(validator);
                    }
                }
            }
        }                                      
    }

    private ValidatorAttribute IsRequired(Type dataType, string property)
    {
        PropertyInfo propertyInfo = dataType.GetProperty(property);

        if (propertyInfo != null)
        {
            foreach (Attribute attribute in propertyInfo.GetCustomAttributes(true))
            {
                if (attribute is NotNullValidatorAttribute)
                    return attribute as ValidatorAttribute;
            }
        }

        return null;
    }
}

Now you create a custom business object, called Customer, and bind it to the DetailsView through an ObjectDataSource.

public class Customer
{        
    private string _firstName;
    private string _lastName;
    
    [NotNullValidator(MessageTemplate="Firstname cannot be empty")]        
    public string FirstName
    {
        get { return _firstName; }
        set { _firstName = value; }
    }

    [NotNullValidator(MessageTemplate="Lastname cannot be empty")]        
    public string LastName
    {
        get { return _lastName; }
        set { _lastName = value; }
    }
    
    public Customer Fill()
    {    
        Customer customer = new Customer();
        customer.FirstName = "Christoph";
        customer.LastName = "De Baene";        
        return customer;
    }
}

Inside your aspx page you have something like

<asp:ObjectDataSource id="customerDataSource"
    TypeName="IStaySharp.Business.Customer, IStaySharp.Business"
    DataObjectTypeName="IStaySharp.Business.Customer, IStaySharp.Business"
    SelectMethod="Fill"     
    runat="server">
 </asp:ObjectDataSource>
         
 
 <asp:ValidationSummary runat="server"/>
        
<asp:DetailsView DataSourceID="customerDataSource" DefaultMode="Edit" AutoGenerateRows="false" runat="server">
    <Fields>
        <rfx:BoundField HeaderText="Firstname"  DataField="FirstName"/>
        <rfx:BoundField HeaderText="Lastname"   DataField="LastName"/>                                               
    </Fields>
</asp:DetailsView>

Here is the result if you leave the properties empty:

Saturday, December 30, 2006 12:31:05 AM (Romance Standard Time, UTC+01:00) -  # -  Comments [1] -
.NET | ASP.NET | EntLib
# Sunday, December 24, 2006

That's what I call a christmas present. A CTP for Enterprise Library v3.0 has been released on CodePlex. More info can be found in this post.

Sunday, December 24, 2006 1:16:47 PM (Romance Standard Time, UTC+01:00) -  # -  Comments [0] -
.NET | EntLib | Links
# Tuesday, November 28, 2006

Tom Hollander revealed some features/scenarios about the upcoming Validation Application Block that will be included in Enterprise Library v3. There are already some validation libraries available, for example

The fact that you can define rules through configuration is really cool. I can't wait for the CTP!

Tuesday, November 28, 2006 1:18:44 AM (Romance Standard Time, UTC+01:00) -  # -  Comments [1] -
.NET | EntLib | Links
# Saturday, July 02, 2005

A new version of the Enterprise Library has been released. More information about the changes since the January release can be found here.

Saturday, July 02, 2005 11:54:17 PM (Romance Daylight Time, UTC+02:00) -  # -  Comments [0] -
.NET | EntLib | Links
# Tuesday, September 28, 2004

From Patterns and Practices we can expect soon an Enterprise Library. This is a single application block that will integrate the most used application blocks. For the first release it will include:

  • Data Access
  • Exception Handling
  • Caching
  • Configuration
  • Logging
  • Security
  • Cryptography
I am convinced that the Enterprise Library will be the defacto standard application block for each application/project. All the functionalities are so fundamental that you can't without it. Like the site mentions, it enforces Consistency, Extensibility, Ease of Use and Integration. The GodDotNet workspace for Enterprise Library can be found here.
Tuesday, September 28, 2004 6:26:56 PM (Romance Daylight Time, UTC+02:00) -  # -  Comments [1] -
.NET | EntLib | Links
Navigation
Archive
<September 2010>
SunMonTueWedThuFriSat
2930311234
567891011
12131415161718
19202122232425
262728293012
3456789
About the author/Disclaimer

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2010
Christoph De Baene
Sign In
Statistics
Total Posts: 176
This Year: 2
This Month: 0
This Week: 0
Comments: 283
All Content © 2010, Christoph De Baene
DasBlog theme 'Business' created by Christoph De Baene