1

There is a class with a Type property,

[Serializable]
public class MeasureTask
{
    private Type _typeToStore;

    public MeasureTask()
        : this(null)
    {}

    public virtual Type TypeToStore
    {
        get { return _typeToStore; }
        set { _typeToStore= value; }
    }
}

FluentNHibernate mapping for this class to MSSQL DB.

public class MeasureTaskMap : SubclassMap<MeasureTask>
{
  public MeasureTaskMap()
  {    
    Map(x => x.TypeToStore);
  }
}

By default, this mapping stores the AssemblyQualifiedName of the Type as a string in the db. For Example:

TopNamespace.SubNameSpace.MeasureTask, MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b17a5c561934e089

Now, when the assembly is compiled to a higher version, for example,

TopNamespace.SubNameSpace.MeasureTask, MyAssembly, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b17a5c561934e089

Then Nhibernate tries to load Type from assembly Version=1.0.0.0 which is stored in the database. But the current type has been changed to Version=2.0.0.0 and this will result in TypeNotFoundException.

Now, how to get around this problem. I do not want to manually strip off the Version Information from AssemblyQualifiedName, since it creates overhead. Is there anyother standard way to do this ? Because, there are many classes like this to be mapped and this is becoming a breaking issue.

jero2rome
  • 1,548
  • 1
  • 21
  • 39

1 Answers1

0

You have to do it manually because the default implementation (NHibernate.Type.TypeType) has no parameter for this. A usertype applied with a convention (ImmutableUserType see here) should be convenient.

public class VersionAgnosticType : ImmutableUserType 
{
    public override object NullSafeGet(IDataReader rs, string[] names, object owner)
    {
        var typename = TypeNameParser.Parse((string)NHibernateUtil.String.Get(rs, names[0])));
        return Type.GetType(typename.Type + ", " + new AssemblyName(typename.Assembly).Name);
    }

    public override void NullSafeSet(IDbCommand cmd, object value, int index)
    {
        var type = (Type)value;
        NHibernateUtil.String.Set(cmd, type.FullName + "," + type.Assembly.GetName().Name, index);
    }

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

    public override SqlType[] SqlTypes
    {
        get { return new[] { SqlTypeFactory.GetString(255) }; }
    }
}

public class TypePropertyConvention : IPropertyConvention
{
    public void Apply(IPropertyInstance instance)
    {
        if (instance.Type == typeof(Type))
            instance.CustomType<VersionAgnosticType>();
    }
}
Community
  • 1
  • 1
Firo
  • 30,626
  • 4
  • 55
  • 94
  • What is the usage ? I added the TypePropertyConvention to the list of conventions in AutoPersistenceModel. Should we use this Type instead of System.Type for the property ? Then how do we map the property ? Surely, VersionAgnosticType cannot be cast as System.Type. – jero2rome Oct 14 '14 at 11:05
  • VersionAgnosticType is an IUserType which is responsible for converting the value of the Type-Property to the database representation and back, your domain model has no connection/knowledge of it whatsoever. The convention basically says "all properties which store a Type object will be serialized by VersionAgnosticType" – Firo Oct 14 '14 at 12:10
  • Thanks, it almost works now. I have added few checks. Because sometimes names[] parameter for NullSafeGet() has this wierd string "SourceType_80_0_" as typeName and so NHibernateUtil.String.Get() could not resolve it and returns a empty string. Is this a problem ? – jero2rome Oct 14 '14 at 12:59
  • `SourceType_80_0_` is the alias NHibernate gives to the column to differentiate it because there may be a self join and same column comes back twice. It should always be valid in the result set as index for the column. If string.empty is returned from `NHibernateUtil.String.Get` then the column most likely has an empty string in it. You can do `rs[names[0]]` and see the raw returnvalue – Firo Oct 14 '14 at 14:12