sâmbătă, 15 septembrie 2012

Making Twitter Bootstrap, jQuery.validate and ASP MVC play well together

I never liked the default error highlighting around input elements of my form and after playing around with Twitter Bootstrap for a few months, I wanted to change this. My first thought was to use this the built-in popovers that are in this library to add a bit of pseudo-elegant nuance to my pages.
I started with a dumb class in ASP MVC 4, adding bootstrap to my project and created a form:
public class User
    {        
        public string Name { get; set; }
    }

@using (Html.BeginForm("Post", "Home", FormMethod.Post, new { id = "myform" }))
    {       
        
        @Html.TextBoxFor(x => x.Name, 
         new { data_content = "Name is required", 
               data_original_title = "Name" })
                        
        
    }
The data_content and data_original_title helpers are there to configure the popover's title and content to display whatever notifications I would show to my users. These are transformed at runtime into data-content and data-original-title HTML5 attributes by ASP MVC / Razor and then are captured by twitter bootstrap javascript component.

How is this done? Basically, I need to instruct jQuery Validate to do its job and in case of an error, I showed the popover near the 'invalid' input. For this to work, I should create validation rules (in JavaScript), pass them to jQuery Validate and set a callback function to display validations:
$(document).ready(function () {

        var allValidationRules = {
            Name: "required"           
        };
        $("#myform").validate({
            rules: allValidationRules,
            showErrors: function (element, errorClass, validClass) {
                for (var i in errorClass) {
                    $(errorClass[i].element).popover('show');
                }
            }
        });
    });
What is going on here?
Setting up validations like this means that I should specify the rules in JavaScript and that is why allValidationRules is there and it's passed to jQuery validate. Name should match the name of the input that is validated; in this case I used .TextBoxFor(....) in the form. Another thing that I should mention - although might seem obvious - is that the validation rules are Case Sensitive. In this case if you change 'Name' to 'name', the validation won't work.

When showErrors callback gets invoked, I grab the input elements that are supposed to be invalid and show the popover right next to them. Like this:
Doesn't look that bad, right?
Cheers.

vineri, 25 mai 2012

NHibernate List to CSV-like column mapping

List<string> to CSV (and vice-versa) type mapping using NHibernate

Experimenting with new stuff is often fun, except when it's even more fun...

For example, today I was trying out NHibernate as I've heared a few times how more mature ORM is in the .NET space comapred to Entity Framework in many ways (you can have global filters defined for records, different cascades, 2nd level caching, ect...).

So, I fired up Visual Studio, downloaded FluentNHibernate using NuGet and wanted to try out to map a list of strings to a CSV-like (varchar) column. Searching the web for this information didn't give me the results I wanted, so I started to dig a bit more.
Since this is my first try-out of NHibernate, I had some issues getting it configured properly building an ISessionFactory to my SQL Server database. I kept getting exceptions that didn't have an unambiguous meaning/message and had problems finding the root problems - how I hate this. Anyway, after a while it all worked out fine and having in mind that it's my first interaction with it, it was OK, I was expecting this.

Continuing my mapping, I built the simplest persistent data model: Employee having some tags stored in a collection and it goes like this:


public class Employee
{
        public Employee()
        {
            Tags = new List<string>();
        }
        public virtual int Id { get; set; }
        public virtual string Name { get; set; }
        public virtual List<string> Tags { get; set; }
}


In my database, the Employees table has a simple nvarchar column named 'Tags' that I map to. With the help of FluentNHibernate, I built my ClassMap<Employee>:

public class EmployeeMap : ClassMap<Employee>
    {
        public EmployeeMap()
        {
            Table("Employees");
            Id(x => x.Id);
            Map(x => x.Name);
            Map(x => x.Tags).CustomType<StringListType>();
        }
    }


The interesting thing happens in StringListType class that implements NHibernate.UserTypes.IUserType.
From what I've read, to be able to do this mapping is to have a class that implements IUserType. I was a bit scared when I saw the number of methods that I need to implement, but here's what I came up with:

public class StringListType : IUserType
    {
        public SqlType[] SqlTypes
        {
            get
            {
                SqlType[] types = new SqlType[1];
                types[0] = new SqlType(DbType.String);
                return types;
            }
        }

        public Type ReturnedType
        {
            get { return typeof(IList<string>); }
        }


        public new bool Equals(object x, object y)
        {
            if (x == null)
            {
                return false;
            }

            return x.Equals(y);
        }


        public int GetHashCode(object x)
        {
            return x.GetHashCode();
        }


        public object NullSafeGet(IDataReader rs, string[] names, object owner)
        {
            string uriString = (string)NHibernateUtil.String.NullSafeGet(rs, names[0]);

            if (string.IsNullOrWhiteSpace(uriString))
                return new List<string>();

            return uriString.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
        }


        public void NullSafeSet(IDbCommand cmd, object value, int index)
        {
            if (value == null)
            {
                NHibernateUtil.String.NullSafeSet(cmd, null, index);
                return;
            }

            NHibernateUtil.String.NullSafeSet(cmd, value, index);
        }


        public object DeepCopy(object value)
        {
            if (value == null) return null;

            return string.Join(",", (IList<string>)value);
        }


        public bool IsMutable
        {
            get { return false; }
        }


        public object Replace(object original, object target, object owner)
        {
            return original;
        }


        public object Assemble(object cached, object owner)
        {
            return cached;
        }


        public object Disassemble(object value)
        {
            return value;
        }
    }


This is quite simple because it just involves transforming from and to a list<string> using some basic operations. As you can see, most of the stuff happens in NullSafeGet and DeepCopy.

I don't know if this is the most efficient way to do this and honestly I haven't given too much thought, but this solved my problem in a quite decent way. If you find a better way, let me know.