How to serialize immutable struct - serialization

I am trying to serialize an immutable struct using JSon.NETand i do not how to do it .The result of the serialization is an empty json {}.I would prefer to use JsonNET and not something heavy like the BinaryFormatter.
Struct
[Serializable]
public struct Settings : IEquatable<Settings> {
private readonly (
TimeSpan from,
TimeSpan until,
TimeSpan repeatInterval,
TimeSpan popupInterval,
string notes
) _value;
[JsonIgnore]
public TimeSpan From => _value.from;
[JsonIgnore]
public TimeSpan Until => _value.until;
[JsonIgnore]
public TimeSpan Repeat => _value.repeatInterval;
[JsonIgnore]
public TimeSpan PopUpInterval => _value.popupInterval;
[JsonIgnore]
public string Notes => _value.notes;
public Settings(
TimeSpan from,
TimeSpan until,
TimeSpan repeatInterval,
TimeSpan popUpInterval,
string notes
) => _value = (
from,
until,
repeatInterval,
popUpInterval,
notes
);
public bool Equals(Settings other) => _value == other._value;
public override bool Equals(object obj) => obj is Settings other && this.Equals(other);
public override int GetHashCode() => _value.GetHashCode();
public override string ToString() => _value.ToString();
public static bool operator ==(Settings a, Settings b) => a.Equals(b);
public static bool operator !=(Settings a, Settings b) => !(a == b);
}
Program
static void Main(string[] args) {
Settings settings = new Settings(new TimeSpan(0),
new TimeSpan(0,1,1),
new TimeSpan(1,2,3),
new TimeSpan(2,4,3),
"adisor");
var obj = JsonConvert.SerializeObject(settings);
var newone = JsonConvert.DeserializeObject<Settings>(obj);
}

For serialization the JsonIgnore attribute need to be removed from target properties. For deserialization, constructor which will be used during deserialization need to be marked with JsonConstruct attribute. Optionally serialization names (properties) need to be reconciled with deserialization names (here constructor parameters) via JsonProperty attributes.
public TimeSpan From => _value.from;
public TimeSpan Until => _value.until;
public TimeSpan Repeat => _value.repeatInterval;
public TimeSpan PopUpInterval => _value.popupInterval;
public string Notes => _value.notes;
[JsonConstructor] //choose a constructor for deserialization
public Settings(
TimeSpan from,
TimeSpan until,
[JsonProperty("Repeat")]TimeSpan repeatInterval, //same name used for serialization
TimeSpan popUpInterval,
string notes
) => _value = (
from,
until,
repeatInterval,
popUpInterval,
notes
);

Related

How do I map an Enumeration class without the discriminator being passed into the constructor?

Based on Jimmy's Enumeration classes idea I am wanting to see if I can avoid using the constructor to instantiate my type (which I assume is happening with the discriminator-value) but rather use a "factory method"-esque way of getting my instance mapped from the db.
Here is my type:
public class Impact : Enumeration
{
public static readonly Impact Carbon
= new Impact(1, "Carbon dioxide equivalent", CommonUnit.CO2e);
public static readonly Impact Energy
= new Impact(2, "Energy", CommonUnit.MJ);
public static readonly Impact Cost
= new Impact(3, "Cost", CommonUnit.Dollars);
public Impact(int index, string name, CommonUnit unit)
: base(index, name)
{
this.Unit = unit;
}
public CommonUnit Unit { get; private set; }
}
And here is the definition for Enumeration:
public class Enumeration : ValueObject
{
public Enumeration(int index, string displayName)
{
this.Index = index;
this.DisplayName = displayName;
}
public int Index { get; private set; }
public string DisplayName { get; private set; }
public override string ToString()
{
return this.DisplayName;
}
public static IEnumerable<T> GetAllFor<T>() where T : Enumeration
{
foreach (var publicStatic in typeof(T).GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly))
{
Enumeration item = null;
item = (Enumeration)publicStatic.GetValue(null);
yield return item as T;
}
}
public static T With<T>(int index) where T : Enumeration
{
return GetAllFor<T>().SingleOrDefault(i => i.Index == index);
}
}
ValueObject simply covers off Equality functionality.
Elsewhere I use the static methods to get items from this enum (kinda like how you could use the core Enumeration static methods):
impact = Impact.With<Impact>(index.ImpactId.Value);
This is pretty handy but I want to know if I can get NHibernate to do this too when rehydrating objects.
Can it be done and how?
With an NHibernate Custom Type:
public class EnumerationType<T> : PrimitiveType where T : Enumeration
{
public EnumerationType()
: base(new SqlType(DbType.Int32))
{
}
public override object Get(IDataReader rs, int index)
{
object o = rs[index];
var value = Convert.ToInt32(o);
return Enumeration.With<T>(value);
}
public override object Get(IDataReader rs, string name)
{
int ordinal = rs.GetOrdinal(name);
return Get(rs, ordinal);
}
public override Type ReturnedClass
{
get { return typeof(T); }
}
public override object FromStringValue(string xml)
{
return int.Parse(xml);
}
public override string Name
{
get { return "Enumeration"; }
}
public override void Set(IDbCommand cmd, object value, int index)
{
var parameter = (IDataParameter)cmd.Parameters[index];
var val = (Enumeration)value;
parameter.Value = val.Value;
}
public override string ObjectToSQLString(object value, Dialect dialect)
{
return value.ToString();
}
public override Type PrimitiveClass
{
get { return typeof(int); }
}
public override object DefaultValue
{
get { return 0; }
}
}
If you're doing an HBM.xml-based mapping, you can set the custom type like this:
<property name="Impact" column="Impact" type="Namespace.To.EnumerationType`1[[Impact, AssemblyWithDomainEnum]], AssemblyWithNHibCustomType"/>
Alternatively, if you're using Fluent NHibernate, you can create a convention to map all enumeration types without having to configure each one individually:
public class EnumerationTypeConvention : IPropertyConvention, IPropertyConventionAcceptance
{
private static readonly Type _openType = typeof(EnumerationType<>);
public void Apply(IPropertyInstance instance)
{
var closedType = _openType.MakeGenericType(instance.Property.PropertyType);
instance.CustomType(closedType);
}
public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
{
criteria.Expect(x => typeof(Enumeration).IsAssignableFrom(x.Property.PropertyType));
}
}
And then add that convention however you like in your Fluent NHibernate configuration.
This seemed to work too, but perhaps Jimmy's way seems easier:
public class ImpactEnumType : IUserType
{
public SqlType[] SqlTypes
{
get
{
//We store our Impact in a single column in the database that can contain a int (for the index value)
SqlType[] types = new SqlType[1];
types[0] = new SqlType(DbType.Int32);
return types;
}
}
public Type ReturnedType
{
get { return typeof(Impact); }
}
public bool Equals(object x, object y)
{
// Impact is derived from ValueObject which implements Equals
return x.Equals(y);
}
public int GetHashCode(object x)
{
// as above
return x.GetHashCode();
}
public object NullSafeGet(IDataReader rs, string[] names, object owner)
{
//We get the string from the database using the NullSafeGet used to get ints
int impactIndex = (int)NHibernateUtil.Int32.NullSafeGet(rs, names[0]);
// then pull the instance from the Enumeration type using the static helpers
return Impact.With<Impact>(impactIndex);
}
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
//Set the value using the NullSafeSet implementation for int from NHibernateUtil
if (value == null)
{
NHibernateUtil.Int32.NullSafeSet(cmd, null, index);
return;
}
value = (value as Impact).Index;
NHibernateUtil.Int32.NullSafeSet(cmd, value, index);
}
public object DeepCopy(object value)
{
//We deep copy the Impact by creating a new instance with the same contents
if (value == null) return null;
return Impact.With<Impact>((value as Impact).Index);
}
public bool IsMutable
{
get { return false; }
}
public object Replace(object original, object target, object owner)
{
//As our object is immutable we can just return the original
return original;
}
public object Assemble(object cached, object owner)
{
//Used for casching, as our object is immutable we can just return it as is
return cached;
}
public object Disassemble(object value)
{
//Used for casching, as our object is immutable we can just return it as is
return value;
}
}
My HBM XML:
<property name="Impact" column="ImpactIndex" type="namespace.childnamespace.ImpactEnumType, namespace.childnamespace" />

composite Key and inheritance

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,
}
};

NHibernate "Invalid Cast (check your mapping for property type mismatches); setter of System.Object"

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.

Unit testing NHibernate w/ SQLite and DateTimeOffset mappings

Porting over an application to use NHibernate from a different ORM.
I've started to put in place the ability to run our unit tests against an in memory SQLite database. This works on the first few batches of tests, but I just hit a snag. Our app would in the real world be talking to a SQL 2008 server, and as such, several models currently have a DateTimeOffset property. When mapping to/from SQL 2008 in non-test applications, this all works fine.
Is there some mechanism either in configuring the database or some other facility so that when I use a session from my SQLite test fixture that the DateTimeOffset stuff is "auto-magically" handled as the more platform agnostic DateTime?
Coincidentally, I just hit this problem myself today :) I haven't tested this solution thoroughly, and I'm new to NHibernate, but it seems to work in the trivial case that I've tried.
First you need to create an IUserType implementation that will convert from DateTimeOffset to DateTime. There's a full example of how to create a user type on the Ayende blog but the relevant method implementations for our purposes are:
public class NormalizedDateTimeUserType : IUserType
{
private readonly TimeZoneInfo databaseTimeZone = TimeZoneInfo.Local;
// Other standard interface implementations omitted ...
public Type ReturnedType
{
get { return typeof(DateTimeOffset); }
}
public SqlType[] SqlTypes
{
get { return new[] { new SqlType(DbType.DateTime) }; }
}
public object NullSafeGet(IDataReader dr, string[] names, object owner)
{
object r = dr[names[0]];
if (r == DBNull.Value)
{
return null;
}
DateTime storedTime = (DateTime)r;
return new DateTimeOffset(storedTime, this.databaseTimeZone.BaseUtcOffset);
}
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
if (value == null)
{
NHibernateUtil.DateTime.NullSafeSet(cmd, null, index);
}
else
{
DateTimeOffset dateTimeOffset = (DateTimeOffset)value;
DateTime paramVal = dateTimeOffset.ToOffset(this.databaseTimeZone.BaseUtcOffset).DateTime;
IDataParameter parameter = (IDataParameter)cmd.Parameters[index];
parameter.Value = paramVal;
}
}
}
The databaseTimeZone field holds a TimeZone which describes the time zone that is used to store values in the database. All DateTimeOffset value are converted to this time zone before storage. In my current implementation it is hard-coded to the local time zone, but you could always define an ITimeZoneProvider interface and have it injected into a constructor.
To use this user type without modifying all my class maps, I created a Convention in Fluent NH:
public class NormalizedDateTimeUserTypeConvention : UserTypeConvention<NormalizedDateTimeUserType>
{
}
and I applied this convention in my mappings, as in this example (the new NormalizedDateTimeUserTypeConvention() is the important part):
mappingConfiguration.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly())
.Conventions.Add(
PrimaryKey.Name.Is(x => x.EntityType.Name + "Id"),
new NormalizedDateTimeUserTypeConvention(),
ForeignKey.EndsWith("Id"));
Like I said, this isn't tested thoroughly, so be careful! But now, all I need to do is to alter one line of code (the fluent mappings specification) and I can switch between DateTime and DateTimeOffset in the database.
Edit
As requested, the Fluent NHibernate configuration:
To build a session factory for SQL Server:
private static ISessionFactory CreateSessionFactory(string connectionString)
{
return Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(connectionString))
.Mappings(m => MappingHelper.SetupMappingConfiguration(m, false))
.BuildSessionFactory();
}
For SQLite:
return Fluently.Configure()
.Database(SQLiteConfiguration.Standard.InMemory)
.Mappings(m => MappingHelper.SetupMappingConfiguration(m, true))
.ExposeConfiguration(cfg => configuration = cfg)
.BuildSessionFactory();
Implementation of SetupMappingConfiguration:
public static void SetupMappingConfiguration(MappingConfiguration mappingConfiguration, bool useNormalizedDates)
{
mappingConfiguration.FluentMappings
.AddFromAssembly(Assembly.GetExecutingAssembly())
.Conventions.Add(
PrimaryKey.Name.Is(x => x.EntityType.Name + "Id"),
ForeignKey.EndsWith("Id"));
if (useNormalizedDates)
{
mappingConfiguration.FluentMappings.Conventions.Add(new NormalizedDateTimeUserTypeConvention());
}
}
Another proposal which allow to keep track of the original timezone offset:
public class DateTimeOffsetUserType : ICompositeUserType
{
public string[] PropertyNames
{
get { return new[] { "LocalTicks", "Offset" }; }
}
public IType[] PropertyTypes
{
get { return new[] { NHibernateUtil.Ticks, NHibernateUtil.TimeSpan }; }
}
public object GetPropertyValue(object component, int property)
{
var dto = (DateTimeOffset)component;
switch (property)
{
case 0:
return dto.UtcTicks;
case 1:
return dto.Offset;
default:
throw new NotImplementedException();
}
}
public void SetPropertyValue(object component, int property, object value)
{
throw new NotImplementedException();
}
public Type ReturnedClass
{
get { return typeof(DateTimeOffset); }
}
public new bool Equals(object x, object y)
{
if (ReferenceEquals(x, null) && ReferenceEquals(y, null))
return true;
if (ReferenceEquals(x, null) || ReferenceEquals(y, null))
return false;
return x.Equals(y);
}
public int GetHashCode(object x)
{
return x.GetHashCode();
}
public object NullSafeGet(IDataReader dr, string[] names, ISessionImplementor session, object owner)
{
if (dr.IsDBNull(dr.GetOrdinal(names[0])))
{
return null;
}
var dateTime = (DateTime)NHibernateUtil.Ticks.NullSafeGet(dr, names[0], session, owner);
var offset = (TimeSpan)NHibernateUtil.TimeSpan.NullSafeGet(dr, names[1], session, owner);
return new DateTimeOffset(dateTime, offset);
}
public void NullSafeSet(IDbCommand cmd, object value, int index, ISessionImplementor session)
{
object utcTicks = null;
object offset = null;
if (value != null)
{
utcTicks = ((DateTimeOffset)value).DateTime;
offset = ((DateTimeOffset)value).Offset;
}
NHibernateUtil.Ticks.NullSafeSet(cmd, utcTicks, index++, session);
NHibernateUtil.TimeSpan.NullSafeSet(cmd, offset, index, session);
}
public object DeepCopy(object value)
{
return value;
}
public bool IsMutable
{
get { return false; }
}
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;
}
}
Fluent NNibernate convention from DateTimeOffset ICompositeUserType would be:
public class DateTimeOffsetTypeConvention : IPropertyConvention, IPropertyConventionAcceptance
{
public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
{
criteria.Expect(x => x.Type == typeof(DateTimeOffset));
}
public void Apply(IPropertyInstance instance)
{
instance.CustomType<DateTimeOffsetUserType>();
}
}
As i'm short on rep I can not add this as a comment to the accepted answer, but wanted to add some additional information I found while implementing the solution in the accepted answer. I too was getting the error that the dialect doesn't support DateTimeOffset when calling schema export. After adding in log4net logging support, I was able to figure out that my properties that were of type DateTimeOffset? were not being handled by the convention. That is, the convention wasn't being applied to nullable DateTimeOffset properties.
To solve this I created a class which derrives from NormalizedDateTimeUserType and overrides the ReturnedType property (had to mark the original as virtual). Then I created a second UserTypeConvention for my derrived class, and finally added the second convention to my configuration.

How do you map a DateTime property to 2 varchar columns in the database with NHibernate (Fluent)?

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>();
}