I was not exactly overwhelmed with responses so I came up with a solution myself. It will only currently work for SQL Server and for classes that use a SingleTableEntityPersister and some of the concerns need to be separted out but it's a start:
Code:
/// <summary>
/// Validates the length of strings in a persistent class.
/// </summary>
public class StringValidator : IStringValidator
{
private Type _currentType;
private SingleTableEntityPersister _persister;
private Dictionary<int, string> _propertyNames;
private Dictionary<int, string> _columnMappings;
private Dictionary<string, int> _columnSizes;
/// <summary>
/// Determines whether an instance of type T is valid by comparing the length of any strings to the maximum length
/// defined in the database
/// </summary>
/// <typeparam name="T">The type</typeparam>
/// <param name="instance">The instance</param>
/// <returns>The result</returns>
public bool IsValid<T>(T instance)
{
if (instance == null) return true;
Initialize<T>();
foreach (int position in _columnMappings.Keys)
{
if (GetValueLength(_propertyNames[position], instance) > GetMaxLength(_columnMappings[position]))
{
return false;
}
}
return true;
}
/// <summary>
/// Determines whether a list of instances of type T are valid by comparing the length of any strings to the maximum length
/// defined in the database
/// </summary>
/// <typeparam name="T">The type</typeparam>
/// <param name="instanceList">The instance list</param>
/// <returns>the result</returns>
public bool IsListValid<T>(IEnumerable<T> instanceList)
{
foreach (T instance in instanceList)
{
if (!IsValid(instance))
{
return false;
}
}
return true;
}
/// <summary>
/// Determines whether initialization is required
/// </summary>
/// <typeparam name="T">The type</typeparam>
/// <returns>The result</returns>
public bool ShouldInitialize<T>()
{
return _currentType == null || typeof(T) != _currentType;
}
/// <summary>
/// Initializes the class if required
/// </summary>
/// <typeparam name="T">The type</typeparam>
public void Initialize<T>()
{
if (ShouldInitialize<T>())
{
_persister =
(SingleTableEntityPersister)
NHibernateSessionManager.Instance.GetSession().SessionFactory.GetClassMetadata(typeof(T));
_propertyNames = GetPropertyNames();
_columnMappings = GetStringMappings();
_columnSizes = GetColumnSizes();
_currentType = typeof(T);
}
}
/// <summary>
/// Returns a Dictionary of properties that are strings keyed by the index of the property
/// </summary>
/// <returns>The Dictionary</returns>
public Dictionary<int, string> GetPropertyNames()
{
Dictionary<int, string> propertyNames = new Dictionary<int, string>();
for (int i = 0; i < _persister.PropertyTypes.Length; i++)
{
if (_persister.PropertyTypes[i] is StringType)
{
propertyNames.Add(i, _persister.PropertyNames[i]);
}
}
return propertyNames;
}
/// <summary>
/// Retiurns a Dictionary of database column names that are mapped to strings, keyed by the index of the property
/// </summary>
/// <returns>The Dictionary</returns>
public Dictionary<int, string> GetStringMappings()
{
Dictionary<int, string> columnMapping = new Dictionary<int, string>();
for (int i = 0; i < _persister.PropertyTypes.Length; i++)
{
if (_persister.PropertyTypes[i] is StringType)
{
columnMapping.Add(i, _persister.GetPropertyColumnNames(i)[0]);
}
}
return columnMapping;
}
/// <summary>
/// Returns a Dictionary of database column sizes for the varchar data type from the table that type T is mapped
/// to, keyed by the column name
/// </summary>
/// <returns>The Dictioary</returns>
public Dictionary<string, int> GetColumnSizes()
{
Dictionary<string, int> columnSizes = new Dictionary<string, int>();
ISQLQuery q =
NHibernateSessionManager.Instance.GetSession().CreateSQLQuery(
"select c.name, c.max_length from sys.columns c inner join sys.tables t" +
" on c.object_id = t.object_id where t.name = :p0 and c.system_type_id = 167");
q.SetString("p0", GetTableName());
q.AddScalar("name", NHibernateUtil.String);
q.AddScalar("max_length", NHibernateUtil.Int32);
IList results = q.List();
foreach (object[] o in results)
{
columnSizes.Add(o[0].ToString(), int.Parse(o[1].ToString()));
}
return columnSizes;
}
/// <summary>
/// Removes the database, schema etc. from the table that type T is mapped to
/// </summary>
/// <returns>The table name</returns>
public string GetTableName()
{
return _persister.TableName.Substring(_persister.TableName.LastIndexOf('.') + 1);
}
/// <summary>
/// Returns the length of the string for the supplied property in the supplied instance
/// </summary>
/// <typeparam name="T">The type</typeparam>
/// <param name="propertyName">The property name</param>
/// <param name="instance">The instance</param>
/// <returns>The length</returns>
public int GetValueLength<T>(string propertyName, T instance)
{
object tmp = typeof(T).GetProperty(propertyName).GetValue(instance, null);
return tmp == null || tmp.ToString() == null
? 0
: tmp.ToString().Length;
}
/// <summary>
/// Returns the max length in the database for the supplied property
/// </summary>
/// <param name="propertyName">The property name</param>
/// <returns>The length</returns>
public int GetMaxLength(string propertyName)
{
return _columnSizes[propertyName];
}
}