NHibernate mapping-by-code and IUsertype not working

2.3k views Asked by At

I am trying to get a custom type working with NHibernate (v3.3) mapping by code. I tried following this example here, but with no luck. The custom type I'm trying to achieve is a one that trims the strings coming from a database.

I am getting the following exception:

PropertyAccessException: Invalid Cast (check your mapping for property type mismatches). {"Unable to cast object of type 'System.String' to type 'ConsoleApplication1.TrimmedString'."}

Here is my full attempt (gist).

public class TrimmedString : IUserType
{
    public object NullSafeGet(IDataReader rs, string[] names, object owner)
    {
        //treat for the posibility of null values
        string resultString = (string) NHibernateUtil.String.NullSafeGet(rs, names[0]);
        if (resultString != null)
            return resultString.Trim();
        return null;
    }

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

        value = ((string) value).Trim();

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

    public object DeepCopy(object value)
    {
        if (value == null) return null;
        return string.Copy((String) value);
    }

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

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

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

    public Type ReturnedType
    {
        get { return typeof (String); }
    }

    public bool IsMutable
    {
        get { return false; }
    }

    public new bool Equals(object x, object y)
    {
        if (ReferenceEquals(x, y)) return true;

        var xString = x as string;
        var yString = y as string;
        if (xString == null || yString == null) return false;

        return xString.Equals(yString);
    }

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

Here is my mapping:

public class Person
{
    public virtual int Id { get; set; }
    public virtual TrimmedString FirstName { get; set; }
    public virtual string LastName { get; set; }
}

public class PersonMap : ClassMapping<Person>
{
    public PersonMap()
    {
        Table("Source");
        Id(i => i.Id);
        Property(i => i.FirstName, map => map.Type<TrimmedString>());
        Property(i => i.LastName);
    }
}

Not sure if I have to do anything special in the NHibernate configuration object, but I have included that in the Gist linked above.

1

There are 1 answers

0
Daniel Schilling On BEST ANSWER

In Person, it should be...

public virtual string FirstName { get; set; }

..., not TrimmedString. TrimmedString is just the class that instructs NHibernate how you want that property to be hydrated and dehydrated. The property it is applied to should be of the type specified by ReturnedType - in other words, String. NHibernate is trying to set the FirstName property with a string value (because that's what the TrimmedString said it should do), but it can't because FirstName only allows TrimmedStrings, hence the "Invalid Cast" error.