Tuesday, December 13, 2011

Automatically set MAXLENGTH on text input fields in ASP.net MVC 3

It seemed to me that ASP.net MVC 3 should know what the max string length is for any given database field and set it in the MAXLENGTH attribute on the corresponding web form.

I went looking for a way to get the built-in Html.EditorFor to do it, but didn't find any.

Here's a new helper called "LimitedEditorFor" which automatically adds the correct MAXLENGTH attribute to the text input tag. Call this exactly the same way you would call Html.EditorFor.


 
namespace ExtensionMethods
{
public static class MyExtensions
{
public static MvcHtmlString LimitedEditorFor<TModel, TValue>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TValue>> expression)
{
string S = htmlHelper.EditorFor(expression).ToString().Replace("/>", "MAXLENGTH=\"" + GetLengthLimit(expression) + "\"/>");
return new MvcHtmlString(S);
}

private static int GetLengthLimit(Expression expression)
{
MemberExpression ME = GetMemberInfo(expression);
Type type = ME.Member.ReflectedType;
string field = ME.Member.Name;
PropertyInfo prop = type.GetProperty(field);
// Find the Linq 'Column' attribute
// e.g. [Column(Storage="_FileName", DbType="NChar(256) NOT NULL", CanBeNull=false)]
object[] info = prop.GetCustomAttributes(typeof(ColumnAttribute), true);

if (info.Length != 1) return 0; // Assume there is just one

ColumnAttribute ca = (ColumnAttribute)info[0];
string dbtype = ca.DbType;
if (!dbtype.StartsWith("NChar") && !dbtype.StartsWith("NVarChar")) return 0;

Regex pattern = new Regex("\\((.+)\\)");
Match m = pattern.Match(dbtype);
if (!m.Success) return 0;

return Convert.ToInt16(m.Groups[1].Value);
}

private static MemberExpression GetMemberInfo(Expression method)
{
LambdaExpression lambda = method as LambdaExpression;
if (lambda == null) throw new ArgumentNullException("method");
MemberExpression memberExpr = null;

if (lambda.Body.NodeType == ExpressionType.Convert)
{
memberExpr = ((UnaryExpression)lambda.Body).Operand as MemberExpression;
}
else if (lambda.Body.NodeType == ExpressionType.MemberAccess)
{
memberExpr = lambda.Body as MemberExpression;
}

if (memberExpr == null) throw new ArgumentException("method");

return memberExpr;
}
}
}


This was a huge help: http://joaodepaula.com/articles/columnsizereflection.html