Per coincidenza, ho appena colpito questo problema io stesso oggi :) Non ho testato questa soluzione a fondo, e sono nuovo di NHibernate, ma sembra funzionare nel caso banale che ho provato.
Per prima cosa è necessario creare un'implementazione IUserType che convertirà da DateTimeOffset a DateTime. C'è un esempio completo di come creare un tipo di utente on the Ayende blog ma le implementazioni di metodo rilevanti per i nostri scopi sono:
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;
}
}
}
Il campo databaseTimeZone
detiene una TimeZone
che descrive il fuso orario che viene utilizzato per memorizzare i valori nel database. Tutti i valori DateTimeOffset
vengono convertiti in questo fuso orario prima della memorizzazione. Nella mia attuale implementazione è hard-coded al fuso orario locale, ma è sempre possibile definire un'interfaccia ITimeZoneProvider e inserirla in un costruttore.
Per utilizzare questo tipo di utente senza modificare tutte le mie mappe di classe, ho creato una convenzione in Fluent NH:
public class NormalizedDateTimeUserTypeConvention : UserTypeConvention<NormalizedDateTimeUserType>
{
}
e ho applicato questa convenzione nei miei mappature, come in questo esempio (il new NormalizedDateTimeUserTypeConvention()
è l'importante parte):
mappingConfiguration.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly())
.Conventions.Add(
PrimaryKey.Name.Is(x => x.EntityType.Name + "Id"),
new NormalizedDateTimeUserTypeConvention(),
ForeignKey.EndsWith("Id"));
Come ho detto, questo non è stato testato a fondo, quindi fate attenzione! Ma ora tutto ciò che devo fare è modificare una riga di codice (la specifica fluente dei mapping) e posso passare da DateTime a DateTimeOffset nel database.
Modifica
Come richiesto, la configurazione Fluent NHibernate:
per costruire una fabbrica di sessione per SQL Server:
private static ISessionFactory CreateSessionFactory(string connectionString)
{
return Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(connectionString))
.Mappings(m => MappingHelper.SetupMappingConfiguration(m, false))
.BuildSessionFactory();
}
per SQLite:
return Fluently.Configure()
.Database(SQLiteConfiguration.Standard.InMemory)
.Mappings(m => MappingHelper.SetupMappingConfiguration(m, true))
.ExposeConfiguration(cfg => configuration = cfg)
.BuildSessionFactory();
Attuazione della 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());
}
}
Molto bello! Dare una possibilità in questo momento, probabilmente cambierà un po 'in modo da poter iniettare la sessione appropriata in base al contesto, ma spero che questo mi spingerà lontano. C'è qualcosa di speciale di cui devo essere consapevole in termini di garanzia che le mie mappature si spingano nello schema SQLite? – bakasan
L'unica cosa che posso pensare è che (come sono sicuro tu sappia) non ci sono vincoli FK applicati in SQLite, quindi probabilmente dovresti avere una serie di test che girano anche sulla piattaforma di produzione. Attualmente mantengo due serie di test per repository, uno che esercita la roba di NHib (cascate, mappature ecc.) Che può essere eseguito su SQLite e uno che viene eseguito su SQL Server per testare la roba DB (vincoli, ecc.). –
Sembra che si stiano verificando problemi di runtime quando tento di applicare il mio schema alla memoria in db-- ti dispiacerebbe condividere la tua configurazione di Fluent NH dove costruisci le tue opzioni db? Il messaggio di errore è fondamentalmente che il mio dialetto non supporta DateTimeOffset (duh;)) .. il mio db è definito come Database (SQLiteConfiguration.Standard.InMemory()) – bakasan