Tuesday, November 03, 2009

Using WCF to power a Custom DataTypeAttribute

In a previous post i demonstrated the simplicity in which you can create a custom DataTypeAttribute, this post aims to take it one step further, or totally off track depending on how you look at it.

the scenario

the rule our custom datatypeattribute applies when validating should be supplied from a service, making it available for several different layers all incorporating one and the same basic rule.

The sample project is complete with a MVC 2 (preview 2) web site, and a WCF service project. Note that it’s created with Visual Studio 2010 Ultimate Beta 2, so you’ll need that as well if you want to run it (you can always re-create it if you so choose).

Part 1 – simple WCF service

wcfThe WCF service in this example holds two simple methods, one for the rule and the other for the error message:

namespace ValidationServiceWCF
{
    public class Rules : IRules
    {
        private int maxlength = 2;
        public bool MaxShortTitleLength(string text)
        {
            if (string.IsNullOrEmpty(text))
            {
                return false;
            }
            return text.Length<=maxlength;
        }
 
        public string MaxShortTitleLengthErrorMessage()
        {
            return string.Format("{0} is invalid, can only be up to {1} chars", "{0}", maxlength);
        }
    }
}
Part 2 – the mvc 2 project

mvc2projThe rule created in the WCF service is then applied in a custom datatypeattribute class called MaxLengthAttribute:

namespace ValidationService.Models.DataAnnotations
{
    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
    public class MaxShortTitleLengthAttribute : DataTypeAttribute
    {
        private string errortext = string.Empty; 
        RulesService.IRules rulesengine = new RulesService.RulesClient();
 
        public MaxShortTitleLengthAttribute()
            : base(DataType.Text)
        {
            errortext = rulesengine.MaxShortTitleLengthErrorMessage();
        }
 
 
        public override bool IsValid(object value)
        {
            if (rulesengine.MaxShortTitleLength((string)value))
            {
                return true;
            }
            else
            {
                return false;
            }
        }
 
        public override string FormatErrorMessage(string name)
        {
            return string.Format(errortext, name);
        }
    }
}

that attribute is then applied on our model using the DataTypeAttribute approach we’re now familiar with:

namespace ValidationService.Models
{
    [MetadataType(typeof(Product_MetaData))]
    public partial class Product
    {
    }
    internal class Product_MetaData
    { 
        [Required]
        [ScaffoldColumn(false)]
        public int ID { get; set; }
 
        [Required]
        public string Title { get; set; }
 
        [Required]
        [MaxShortTitleLength()]
        public string ShortTitle { get; set; }
    }
}

After that it was just a matter of creating some actions, wiring a few views, setting up a context and repository class and finally wrapping it all up.

Now when validation is run on the ShortTitle property of the model the custom attribute fires off to the service, calls the validation method and gets the errormessage in case we’ll need it:

errormessage

That’s it, source code available here (VS 2010 Beta 2).

Note: this is a simple example and therefore i’ve just used the HomeController to handle everything. The endpoint for the WCF is currently pointing to localhost:7556/rules.svc.

Regards,

P.