Given the following Fluent NHibernate maps:
public class FastTrackPackageClassMap : ClassMap<FastTrackPackage>
{
public FastTrackPackageClassMap()
{
Id(x => x.Id);
References(x => x.UserCreated, "UserIdCreated").Cascade.None();
References(x => x.UserSent, "UserIdSent").Nullable().Cascade.None();
HasMany(x => x.GetFileHeaders()).KeyColumn("PackageId").ForeignKeyConstraintName("PackageId").Cascade.AllDeleteOrphan().Not.LazyLoad().Inverse();
HasMany(x => x.GetEmailRecords()).KeyColumn("PackageId").ForeignKeyConstraintName("PackageId").Cascade.AllDeleteOrphan().Not.LazyLoad().Inverse();
HasMany(x => x.GetPaymentRecords()).KeyColumn("PackageId").ForeignKeyConstraintName("PackageId").Cascade.AllDeleteOrphan().Not.LazyLoad().Inverse();
HasMany(x => x.GetFileTrailers()).KeyColumn("PackageId").ForeignKeyConstraintName("PackageId").Cascade.AllDeleteOrphan().Not.LazyLoad().Inverse();
Map(x => x.TestPackage);
Map(x => x.DateTimeCreated).Generated.Insert();
Map(x => x.DateTimeSent).Nullable();
}
}
public class FileHeaderFastTrackRecordClassMap : ClassMap<FileHeaderFastTrackRecord>
{
public FileHeaderFastTrackRecordClassMap()
{
Id(FileHeaderFastTrackRecord.Expressions.Id);
References(x => x.Package, "PackageId");
Map(x => x.FileDateTime);
Map(x => x.CustomerId);
Map(x => x.TestPackage, "TestIndicator").CustomType(typeof (TestPackageUserType));
Map(x => x.BankId);
}
}
... and the given custom user type:
public class TestPackageUserType : IUserType
{
public bool Equals(object x, object y)
{
if (x == null)
return false;
return x.Equals(y);
}
public int GetHashCode(object x) { return x.GetHashCode(); }
public object NullSafeGet(IDataReader rs, string[] names, object owner)
{
var testIndicator = (char) NHibernateUtil.Character.NullSafeGet(rs, names[0]);
return testIndicator == 'T';
}
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
if (value == null)
{
NHibernateUtil.Character.NullSafeSet(cmd, null, index);
return;
}
if ((bool) value) { NHibernateUtil.Character.NullSafeSet(cmd, 'T', index); }
else { NHibernateUtil.Character.NullSafeSet(cmd, 'P', index); }
}
public object DeepCopy(object value)
{
if (value == null) return true;
return (bool) value;
}
public object Replace(object original, object target, object owner)
{
if (target != null && !(bool) target)
{
return 'P';
}
return 'T';
}
public object Assemble(object cached, object owner) { throw new NotImplementedException(); }
public object Disassemble(object value)
{
if (value != null)
{
var testIndicator = (char[]) value;
if (testIndicator[0] == 'P')
{
return false;
}
}
return true;
}
public SqlType[] SqlTypes { get { return new[] {new SqlType(DbType.String)}; } }
public Type ReturnedType { get { return typeof (bool); } }
public bool IsMutable { get; private set; }
}
I receive the following exception and stack when I Update Merge the FastTrackPackage with a child FileHeaderFastTrackRecord:
NHibernate.PropertyAccessException: Invalid Cast (check your mapping for property type mismatches); setter of BNYM_Extranet.Common.RemEDIFastTrack.FileHeaderFastTrackRecord
at NHibernate.Tuple.Entity.PocoEntityTuplizer.SetPropertyValuesWithOptimizer(Object entity, Object[] values)
at NHibernate.Tuple.Entity.PocoEntityTuplizer.SetPropertyValues(Object entity, Object[] values)
at NHibernate.Persister.Entity.AbstractEntityPersister.SetPropertyValues(Object obj, Object[] values, EntityMode entityMode)
at NHibernate.Event.Default.DefaultMergeEventListener.CopyValues(IEntityPersister persister, Object entity, Object target, ISessionImplementor source, IDictionary copyCache)
at NHibernate.Event.Default.DefaultMergeEventListener.EntityIsDetached(MergeEvent event, IDictionary copyCache)
at NHibernate.Event.Default.DefaultMergeEventListener.OnMerge(MergeEvent event, IDictionary copyCache)
at NHibernate.Impl.SessionImpl.FireMerge(IDictionary copiedAlready, MergeEvent event)
at NHibernate.Impl.SessionImpl.Merge(String entityName, Object obj, IDictionary copiedAlready)
at NHibernate.Engine.CascadingAction.MergeCascadingAction.Cascade(IEventSource session, Object child, String entityName, Object anything, Boolean isCascadeDeleteEnabled)
at NHibernate.Engine.Cascade.CascadeToOne(Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled)
at NHibernate.Engine.Cascade.CascadeAssociation(Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled)
at NHibernate.Engine.Cascade.CascadeProperty(Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled)
at NHibernate.Engine.Cascade.CascadeCollectionElements(Object child, CollectionType collectionType, CascadeStyle style, IType elemType, Object anything, Boolean isCascadeDeleteEnabled)
at NHibernate.Engine.Cascade.CascadeCollection(Object child, CascadeStyle style, Object anything, CollectionType type)
at NHibernate.Engine.Cascade.CascadeAssociation(Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled)
at NHibernate.Engine.Cascade.CascadeProperty(Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled)
at NHibernate.Engine.Cascade.CascadeOn(IEntityPersister persister, Object parent, Object anything)
at NHibernate.Event.Default.DefaultMergeEventListener.CascadeOnMerge(IEventSource source, IEntityPersister persister, Object entity, IDictionary copyCache)
at NHibernate.Event.Default.DefaultMergeEventListener.EntityIsDetached(MergeEvent event, IDictionary copyCache)
at NHibernate.Event.Default.DefaultMergeEventListener.OnMerge(MergeEvent event, IDictionary copyCache)
at NHibernate.Event.Default.DefaultMergeEventListener.OnMerge(MergeEvent event)
at NHibernate.Impl.SessionImpl.FireMerge(MergeEvent event)
at NHibernate.Impl.SessionImpl.Merge(String entityName, Object obj)
at NHibernate.Impl.SessionImpl.Merge(Object obj)
at DAL.NHibernate.RepositoryWithTypedId`2.Update(T entity) in Repository.cs: line 72
at UnitTests.DAL.NHibernate.RemEDIFastTrack.FastTrackPackageRepositoryFixture.Update() in FastTrackPackageRepositoryFixture.cs: line 290
When I Save the FastTrackPackage with a FileHeaderFastTrackRecord I receive no error and it saves properly. I can also Save and Update FileHeaderFastTrackRecords independently.
At this point I'm at a loss. I suspect the custom user type, but I can't be sure.
How can I diagnose or fix this problem?
It turns out the 'TestPackageUserType.Replace()' method was wrong. It was returning a char when it should be returning a bool and code further down the stack choked on it (providing the cryptic exception message). My new Replace() method looks like this:
public object Replace(object original, object target, object owner)
{
return original;
}
Related
I have the following struct and class:
public struct DepartmentId{
public int Value {get; set;}
}
public class Department{
public virtual DepartmentId Id{get;set;}
public virtual string Name {get; set;}
}
I created a mapping file for Department as follows:
public class DepartmentMapping : ClassMapping<Department>{
public DepartmentMapping{
Table("Department");
Id(dept => dept.Id, mapper => {
mapper.Column("Id");
mapper.Type(new DepartmentIdType());
});
Property(dept => dept.Name, mapper => mapper.Column("Name"));
}
}
where DepartmentIdType implements IIdentifierType:
class DepartmentIdType : PrimitiveType, IIdentifierType
{
public DepartmentIdType() : base(SqlTypeFactory.Int32)
{
}
public override object DeepCopy(object val, EntityMode entityMode, ISessionFactoryImplementor factory)
{
return val;
}
public override object Replace(object original, object current, ISessionImplementor session, object owner, IDictionary copiedAlready)
{
return original;
}
public override Type ReturnedClass
{
get { return typeof(DepartmentId); }
}
public object StringToObject(string xml)
{
return new DepartmentId {Value = int.Parse(xml)};
}
public override string Name
{
get { return typeof(DepartmentId).Name; }
}
public override void Set(IDbCommand cmd, object value, int index)
{
var id = (DepartmentId) value;
((IDataParameter) cmd.Parameters[index]).Value = id.Value;
}
public override object Get(IDataReader rs, int index)
{
int value = rs.GetInt32(index);
return new DepartmentId {Value = value};
}
public override object Get(IDataReader rs, string name)
{
return Get(rs, rs.GetOrdinal(name));
}
public override string ToString(object val)
{
if (val == null) return "";
return val.ToString();
}
public override object FromStringValue(string xml)
{
return new DepartmentId {Value = Int32.Parse(xml)};
}
public override object DefaultValue
{
get { return new DepartmentId {Value = 0}; }
}
public override string ObjectToSQLString(object value, NHibernate.Dialect.Dialect dialect)
{
return value.ToString();
}
public override Type PrimitiveClass
{
get { return typeof(DepartmentId); }
}
}
However, at the time of creating the HbmMapping, I get the following error:
Could not compile the mapping document: mapping_by_code
NHibernate.MappingException: Could not compile the mapping document:
mapping_by_code ---> NHibernate.MappingException:
Could not determine type for: Demo.Models.DepartmentId, Demo.Models,
for columns: NHibernate.Mapping.Column(Id)
at NHibernate.Mapping.SimpleValue.get_Type()
at NHibernate.Cfg.XmlHbmBinding.ClassIdBinder.CreateIdentifierProperty(HbmId idSchema, PersistentClass rootClass, SimpleValue id)
at NHibernate.Cfg.XmlHbmBinding.ClassIdBinder.BindId(HbmId idSchema, PersistentClass rootClass, Table table)
at NHibernate.Cfg.XmlHbmBinding.RootClassBinder.Bind(HbmClass classSchema, IDictionary`2 inheritedMetas)
at NHibernate.Cfg.XmlHbmBinding.MappingRootBinder.AddRootClasses(HbmClass rootClass, IDictionary`2 inheritedMetas)
at NHibernate.Cfg.XmlHbmBinding.MappingRootBinder.AddEntitiesMappings(HbmMapping mappingSchema, IDictionary`2 inheritedMetas)
at NHibernate.Cfg.XmlHbmBinding.MappingRootBinder.Bind(HbmMapping mappingSchema)
at NHibernate.Cfg.Configuration.AddDeserializedMapping(HbmMapping mappingDocument, String documentFileName)
How do I fix this issue (without changing DepartmentId from struct to class)?
Thanks in advance for your help.
I suggest using class instead of struct and ComponentAsId instead of Id method for this case. If you don't use id generator it is a straightforward approach without any hacking.
public class DepartmentMapping : ClassMapping<Department> {
public DepartmentMapping{
Table("Department");
ComponentAsId(dept => dept.Id);
Property(dept => dept.Name, mapper => mapper.Column("Name"));
}}
public class DepartmentIdMapping : ComponentMapping<DepartmentId> {
public DepartmentIdMapping{
Property(x=> x.Value, mapper => mapper.Column("Id"));
}}
I tried your approach when investigating this strong typed ids together with id generation but I finally decided to implement custom Hi/Lo generator and use NHibernate assigned ids.
I'm just going to put this here for a reference.
In XML mapping, I did this through mapping as a composite id:
<class name"Department" ... >
<composite-id name="Id" access="property"
class="DepartmentId">
<key-property name="Value"
column="Id"
access="property"
type="System.Int32"/>
</composite-id>
... other stuff
</class>
I have a legacy database that uses zero-padded numbers stored as VARCHAR(8) for the primary key values in the tables. (For example, "00032982")
I want to map these to Int32 properties in my entity objects instead of string.
My first attempt was to create a class that implemented IUserType. But I'm mapping an Id, not a Property, and the mapping-by-code wouldn't compile: (Maybe I'm not doing that part right?)
Argument 1: cannot convert from 'TestQWebApi.Infrastructure.StringIntUserType' to 'NHibernate.Type.IIdentifierType'
I looked at IIdentifierType and it has over 40 members. I looked all over the place and it seemed like creating a custom type that subclasses NHibernate.Type.ImmutableType would be the solution. (...maybe this was my first mistake?)
So, I made a new class based on ImmutableType that zero-pads it in the Set() method:
public class LegacyIntKeyType : ImmutableType, ILiteralType, IDiscriminatorType
{
public LegacyIntKeyType() : base(SqlTypeFactory.GetString(8))
{
}
public override string Name
{
get { return "LegacyIntKeyType"; }
}
public override object Get(IDataReader rs, int index)
{
try
{
return Convert.ToInt32(rs[index]);
}
catch (Exception ex)
{
throw new FormatException(string.Format("Input string '{0}' was not in the correct format.", rs[index]), ex);
}
}
public override object Get(IDataReader rs, string name)
{
try
{
return Convert.ToInt32(rs[name]);
}
catch (Exception ex)
{
throw new FormatException(string.Format("Input string '{0}' was not in the correct format.", rs[name]), ex);
}
}
public override System.Type ReturnedClass
{
get { return typeof(Int32); }
}
public override void Set(IDbCommand rs, object value, int index)
{
((IDataParameter)rs.Parameters[index]).Value = string.Format("{0:d8}", value.ToString());
}
public object StringToObject(string xml)
{
return FromStringValue(xml);
}
public override object FromStringValue(string xml)
{
return Int32.Parse(xml);
}
public string ObjectToSQLString(object value, Dialect dialect)
{
return value.ToString();
}
public override string ToString(object value)
{
return value.ToString();
}
}
In my base entity class, I have it defined as an Int32:
public abstract class Entity
{
public virtual Int32 Id { get; protected set; }
}
Then I tried to map it:
public class ProcedureMap : ClassMapping<Procedure>
{
public ProcedureMap()
{
Table("procfile");
Id(x => x.Id, m =>
{
m.Column("key_proced");
m.Type(new LegacyIntKeyType());
});
Property(x => x.Name, m => m.Column("procname"));
}
}
Then I get this exception...which I have the feeling is probably indicating something else. Here's the stack trace:
[MappingException: Could not determine type for: TestQWebApi.Models.LegacyIntKeyType, TestQWebApi, for columns: NHibernate.Mapping.Column(key_proced)]
NHibernate.Mapping.SimpleValue.get_Type() +317
NHibernate.Cfg.XmlHbmBinding.ClassIdBinder.CreateIdentifierProperty(HbmId idSchema, PersistentClass rootClass, SimpleValue id) +301
NHibernate.Cfg.XmlHbmBinding.ClassIdBinder.BindId(HbmId idSchema, PersistentClass rootClass, Table table) +396
NHibernate.Cfg.XmlHbmBinding.RootClassBinder.Bind(HbmClass classSchema, IDictionary`2 inheritedMetas) +987
NHibernate.Cfg.XmlHbmBinding.MappingRootBinder.AddRootClasses(HbmClass rootClass, IDictionary`2 inheritedMetas) +104
NHibernate.Cfg.XmlHbmBinding.MappingRootBinder.AddEntitiesMappings(HbmMapping mappingSchema, IDictionary`2 inheritedMetas) +165
NHibernate.Cfg.XmlHbmBinding.MappingRootBinder.Bind(HbmMapping mappingSchema) +117
NHibernate.Cfg.Configuration.AddDeserializedMapping(HbmMapping mappingDocument, String documentFileName) +244
Thanks in advance!
you have to implement NHibernates IUserType
class MyIdUserType : IUserType
{
public object Assemble(object cached, object owner)
{
return DeepCopy(cached);
}
public object DeepCopy(object value)
{
return value;
}
public object Disassemble(object value)
{
return DeepCopy(value);
}
bool IUserType.Equals(object x, object y)
{
return Object.Equals(x, y);
}
public virtual int GetHashCode(object x)
{
return (x != null) ? x.GetHashCode() : 0;
}
public bool IsMutable { get { return false; } }
public object NullSafeGet(IDataReader rs, string[] names, object owner)
{
return Int32.Parse((string)NHibernateUtil.String.Get(rs, names[0]));
}
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
NHibernateUtil.String.Set(cmd, string.Format("{0:d8}", value), index);
}
public object Replace(object original, object target, object owner)
{
return original;
}
public Type ReturnedType { get { return typeof(Int32); } }
public SqlType[] SqlTypes { get { return new []{ SqlTypeFactory.GetString(8) }; } }
}
i have the following classes and mappings
abstract class BaseClass
{
public virtual int Keypart1 { get; set; }
public virtual int Keypart2 { get; set; }
// overridden Equals() and GetHashCode()
}
class InheritingClass : BaseClass
{
}
class BaseClassMap : ClassMap<BaseClass>
{
public BaseClassMap()
{
CompositeId()
.KeyProperty(x => x.Keypart1)
.KeyProperty(x => x.Keypart2);
}
}
class InheritingClassMap : SubclassMap<InheritingClass>
{
public InheritingClassMap()
{
KeyColumn("Keypart1");
KeyColumn("Keypart2");
}
}
inserting, updating and session.Get() work fine but querying like
var result = session.CreateCriteria<InheritingClass>().List<InheritingClass>();
throws
NHibernate.InstantiationException: Cannot instantiate abstract class or interface: ConsoleApplication1.BaseClass
bei NHibernate.Tuple.PocoInstantiator.Instantiate()
bei NHibernate.Tuple.Component.AbstractComponentTuplizer.Instantiate()
bei NHibernate.Type.ComponentType.Instantiate(EntityMode entityMode)
bei NHibernate.Type.ComponentType.Instantiate(Object parent, ISessionImplementor session)
bei NHibernate.Type.EmbeddedComponentType.Instantiate(Object parent, ISessionImplementor session)
bei NHibernate.Type.ComponentType.ResolveIdentifier(Object value, ISessionImplementor session, Object owner)
bei NHibernate.Type.ComponentType.NullSafeGet(IDataReader rs, String[] names, ISessionImplementor session, Object owner)
bei NHibernate.Loader.Loader.GetKeyFromResultSet(Int32 i, IEntityPersister persister, Object id, IDataReader rs, ISessionImplementor session)
bei NHibernate.Loader.Loader.GetRowFromResultSet(IDataReader resultSet, ISessionImplementor session, QueryParameters queryParameters, LockMode[] lockModeArray, EntityKey optionalObjectKey, IList hydratedObjects, EntityKey[] keys, Boolean returnProxies)
...
it seems NH tries to instantiate the abstract baseclass as a compositekey and fails. Can i somehow work around that?
UPDATE: my testcode
var config = Fluently.Configure()
.Database(SQLiteConfiguration.Standard.InMemory().ShowSql().FormatSql())
.Mappings(m => m.FluentMappings
.Add<BaseClassMap>()
.Add<InheritingClassMap>()
)
.BuildConfiguration();
var sf = config.BuildSessionFactory();
using (var session = sf.OpenSession())
{
new SchemaExport(config).Execute(false, true, false, session.Connection, null);
var obj = new InheritingClass
{
Keypart1 = 1,
Keypart2 = 2,
};
session.Save(obj);
session.Flush();
session.Clear();
// throws here
var result = session.CreateCriteria<InheritingClass>().List<InheritingClass>();
}
How does your database look like? According to your mapping, you are using a table-per-subclass mapping. In this case NHibernate will try to create an instance of BaseClass if no row is found in the table of InheritingClass.
edit: There is a abstract="true" attribute for a <class> in NHibernate mapping which might fix your problem. But it seems like this isn't exposed in Fluent NHibernate for a ClassMap, only for SubclassMap (which won't help you).
But maybe you could also fix the problem by using a component for the composite ID (so that NHibernate doesn't need to create a BaseClass object for its EntityKey. See here for infos about that.
thx to cremor this is what i end up with
abstract class BaseClass
{
public virtual BaseClassId Key { get; set; }
}
class BaseClassId
{
public virtual int Keypart1 { get; set; }
public virtual int Keypart2 { get; set; }
public override bool Equals(object obj)
{
var other = obj as BaseClassId;
return (other != null) && (Keypart1 == other.Keypart1) && (Keypart2 == other.Keypart2);
}
public override int GetHashCode()
{
unchecked
{
return Keypart1 + Keypart2;
}
}
}
// mapping
CompositeId(b => b.Key)
.KeyProperty(x => x.Keypart1)
.KeyProperty(x => x.Keypart2);
var obj = new InheritingClass
{
Key = new BaseClassId
{
Keypart1 = 1,
Keypart2 = 2,
}
};
I've been upgrading my NHibernate installation (was still on 1.2!). All has been good until I tried to setup an Interceptor. If I add an Interceptor to the OpenSession() call, then I get this error when trying to load an entity from the DB:
"Invalid Cast (check your mapping for property type mismatches); setter of System.Object"
If there is no Interceptor, this error does not occur.
The Interceptor is very simple, and in fact I pretty much commented out everything in order to test:
public class Interceptor : EmptyInterceptor
{
public override void AfterTransactionBegin(ITransaction tx)
{
}
public override void AfterTransactionCompletion(ITransaction tx)
{
}
public override void BeforeTransactionCompletion(ITransaction tx)
{
}
//public override int[] FindDirty(object entity, object id, object[] currentState, object[] previousState, string[] propertyNames, IType[] types)
//{
//}
//public override object GetEntity(string entityName, object id)
//{
//}
//public override string GetEntityName(object entity)
//{
//}
public override object Instantiate(string entityName, EntityMode entityMode, object id)
{
//((EntityBase)entity).Instantiate(entityName, entityMode, id)
return new object();
}
//public override bool? IsTransient(object entity)
//{
//}
public override void OnCollectionRecreate(object collection, object key)
{
}
public override void OnCollectionRemove(object collection, object key)
{
}
public override void OnCollectionUpdate(object collection, object key)
{
}
public override void OnDelete(object entity, object id, object[] state, string[] propertyNames, IType[] types)
{
//((EntityBase)entity).OnDelete(entity, id, state, propertyNames, types);
}
//public override bool OnFlushDirty(object entity, object id, object[] currentState, object[] previousState, string[] propertyNames, IType[] types)
//{
//}
public override bool OnLoad(object entity, object id, object[] state, string[] propertyNames, IType[] types)
{
//if (entity.GetType().FullName.Equals("EntityBase"))
// return ((EntityBase)entity).OnLoad(entity, id, state, propertyNames, types);
//else
return false;
}
//public override NHibernate.SqlCommand.SqlString OnPrepareStatement(NHibernate.SqlCommand.SqlString sql)
//{
//}
public override bool OnSave(object entity, object id, object[] state, string[] propertyNames, IType[] types)
{
//return ((EntityBase)entity).OnSave(entity, id, state, propertyNames, types);
return true;
}
public override void PostFlush(System.Collections.ICollection entities)
{
}
public override void PreFlush(System.Collections.ICollection entities)
{
}
public override void SetSession(ISession session)
{
}
}
As such I'm totally baffled. Like I said, with the Interceptor it errors on loading and hydrating entities (after the OnLoad event fires), without the Interceptor it's fine. I'm using nHibernate 3.2.0.2001 with .Net4 against SQL Server 2008.
Any help appreciated! Ben
Unfortunately I've been unable to progress this issue, I had to switch to Linq-SQL because of this specific problem, and that I couldn't find a solution in the timeframe required to use these templates. Therefore I can't provide any more information at this time.
I'm dealing with a legacy database that has date and time fields as char(8) columns (formatted yyyyMMdd and HH:mm:ss, respectively) in some of the tables. How can i map the 2 char columns to a single .NET DateTime property? I have tried the following, but i get a "can't access setter" error of course because DateTime Date and TimeOfDay properties are read-only:
public class SweetPocoMannaFromHeaven
{
public virtual DateTime? FileCreationDateTime { get; set; }
}
.
mapping.Component<DateTime?>(x => x.FileCreationDateTime,
dt =>
{
dt.Map(x => x.Value.Date,
"file_creation_date");
dt.Map(x => x.Value.TimeOfDay,
"file_creation_time");
});
I have also tried defining a IUserType for DateTime, but i can't figure it out. I've done a ton of googling for an answer, but i can't figure it out still. What is my best option to handle this stupid legacy database convention? A code example would be helpful since there's not much out for documentation on some of these more obscure scenarios.
You need an ICompositeUserType to handle more than one column. You need to beef up the error checking, parsing formats, etc, but here is a starting point for you.
HTH,
Berryl
public class LegacyDateUserType : ICompositeUserType
{
public new bool Equals(object x, object y)
{
if (x == null || y == null) return false;
return ReferenceEquals(x, y) || x.Equals(y);
}
public int GetHashCode(object x) {
return x == null ? typeof (DateTime).GetHashCode() + 473 : x.GetHashCode();
}
public object NullSafeGet(IDataReader dr, string[] names, ISessionImplementor session, object owner)
{
if (dr == null) return null;
var datePortion = NHibernateUtil.String.NullSafeGet(dr, names[0], session, owner) as string;
var timePortion = NHibernateUtil.String.NullSafeGet(dr, names[1], session, owner) as string;
var date = DateTime.Parse(datePortion);
var time = DateTime.Parse(timePortion);
return date.AddTicks(time.Ticks);
}
///<summary>
/// Write an instance of the mapped class to a prepared statement. Implementors
/// should handle possibility of null values. A multi-column type should be written
/// to parameters starting from index.
///</summary>
public void NullSafeSet(IDbCommand cmd, object value, int index, ISessionImplementor session) {
if (value == null) {
// whatever
}
else {
var date = (DateTime) value;
var datePortion = date.ToString("your date format");
NHibernateUtil.String.NullSafeSet(cmd, datePortion, index, session);
var timePortion = date.ToString("your time format");
NHibernateUtil.String.NullSafeSet(cmd, timePortion, index + 1, session);
}
}
public object GetPropertyValue(object component, int property)
{
var date = (DateTime)component;
return property == 0 ? date.ToString("your date format") : date.ToString("your time format");
}
public void SetPropertyValue(object component, int property, object value)
{
throw new NotSupportedException("DateTime is an immutable object.");
}
public object DeepCopy(object value) { return value; }
public object Disassemble(object value, ISessionImplementor session) { return value; }
public object Assemble(object cached, ISessionImplementor session, object owner) { return cached; }
public object Replace(object original, object target, ISessionImplementor session, object owner) { return original; }
///<summary>Get the "property names" that may be used in a query.</summary>
public string[] PropertyNames { get { return new[] { "DATE_PORTION", "TIME_PORTION" }; } }
///<summary>Get the corresponding "property types"</summary>
public IType[] PropertyTypes { get { return new IType[] { NHibernateUtil.String, NHibernateUtil.String }; } }
///<summary>The class returned by NullSafeGet().</summary>
public Type ReturnedClass { get { return typeof(DateTime); } }
///<summary>Are objects of this type mutable?</summary>
public bool IsMutable { get { return false; } }
}
=== fluent mapping (assuming automapping w/override classes) ====
public void Override(AutoMapping<MyClass> m)
{
....
m.Map(x => x.MyDateTime).CustomType<LegacyDateUserType>();
}