See also Part 1 - Compound foreign keys
Simple one this but it drove me crazy for a while. For some reason *cough* incompetence *cough* the technical manager at my previous company had a penchant for using fixed-length fields for data that wasn't, um, fixed length. So if you looked through the application code you'd find .Trim()
statements liberally sprinkled throughout the code, including on fields that didn't actually need them (because keeping track of those that did and those that didn't became a near-impossible task). Of course, sometimes developers forgot to trim the strings and you'd get subtle bugs from incorrect string concatenation occurring, or users would click in a text field and find themselves having to delete masses of white space to edit their text.
Putting aside the fact that we shouldn't have been using fixed-length strings in the first place, how data gets stored in the database is a persistence concern and details of this should not be allowed to leak into the application. Trimming the fields of fixed length strings should be taken care of in the data access code if necessary. When using ADO.NET directly it's relatively easy to apply this as the data is mapped into your classes, of course if you have lot's of different functions calling different stored procs it's also easy to forget to do it. In an NHibernate data-layer we don't have the same direct access to the data coming in, but thankfully it's relatively easy to get NHibernate to automatically trim any strings for us by using a custom type.
The code is pretty simple, by and large it doesn't need to do anything except return the trimmed value in the
NullSafeGet
function:
public class TrimmedString : IUserType, IEnhancedUserType
{
#region IUserType Members
public object Assemble(object cached, object owner)
{
throw new NotImplementedException();
}
public object DeepCopy(object value)
{
return value;
}
public object Disassemble(object value)
{
throw new NotImplementedException();
}
public int GetHashCode(object x)
{
return x.GetHashCode();
}
public bool IsMutable
{
get { return false; }
}
public object NullSafeGet(System.Data.IDataReader rs, string[] names, object owner)
{
object obj = NHibernateUtil.String.NullSafeGet(rs, names[0]);
if (obj == null) return null;
return obj.ToString().TrimEnd();
}
public void NullSafeSet(System.Data.IDbCommand cmd, object value, int index)
{
if (value == null)
{
((IDataParameter)cmd.Parameters[index]).Value = DBNull.Value;
}
else
{
((IDataParameter)cmd.Parameters[index]).Value = value;
}
}
public object Replace(object original, object target, object owner)
{
throw new NotImplementedException();
}
public Type ReturnedType
{
get { return typeof(string); }
}
public global::NHibernate.SqlTypes.SqlType[] SqlTypes
{
get { return new global::NHibernate.SqlTypes.SqlType[] { NHibernateUtil.String.SqlType }; }
}
public new bool Equals(object x, object y)
{
if (object.ReferenceEquals(x, y)) return true;
if (x == null || y == null) return false;
return x.Equals(y);
}
#endregion
#region IEnhancedUserType Members
public object FromXMLString(string xml)
{
return xml;
}
public string ObjectToSQLString(object value)
{
return value.ToString();
}
public string ToXMLString(object value)
{
return value.ToString();
}
#endregion
}
Then in the .hbm mapping, set the type of the field to be our new type:
<property name="Title" column="ctitle" access="property" type="TrimmedString" />
Now the field will be automatically trimmed as NHibernate reads it.
I am currently a developer at a management consultancy company.
In addition to all things .NET I have a keen interest in Japan and the Japanese language and hope to be able to combine these passions one day soon.