bak*_*san 14 sqlite nhibernate datetimeoffset
移植应用程序以从不同的ORM使用NHibernate.
我已经开始实现对内存SQLite数据库运行单元测试的能力.这适用于前几批测试,但我只是遇到了障碍.我们的应用程序将在现实世界中与SQL 2008服务器通信,因此,几个模型当前具有DateTimeOffset属性.在非测试应用程序中映射到/来自SQL 2008时,这一切都正常.
在配置数据库或其他工具时是否有某种机制,以便当我使用来自我的SQLite测试夹具的会话时,DateTimeOffset的东西被"自动神奇地"处理为更平台无关的DateTime?
Ric*_*ebb 13
巧合的是,我今天自己就遇到了这个问题:)我没有彻底测试过这个解决方案,而且我是NHibernate的新手,但它似乎适用于我尝试过的琐碎案例.
首先,您需要创建一个将从DateTimeOffset转换为DateTime的IUserType实现.有一个关于如何在Ayende博客上创建用户类型的完整示例,但我们目的的相关方法实现是:
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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
该databaseTimeZone
字段包含一个TimeZone
描述用于在数据库中存储值的时区的字段.DateTimeOffset
存储前,所有值都将转换为此时区.在我当前的实现中,它被硬编码到本地时区,但您可以始终定义ITimeZoneProvider接口并将其注入构造函数.
要在不修改所有类映射的情况下使用此用户类型,我在Fluent NH中创建了一个约定:
public class NormalizedDateTimeUserTypeConvention : UserTypeConvention<NormalizedDateTimeUserType>
{
}
Run Code Online (Sandbox Code Playgroud)
我在我的映射中应用了这个约定,就像在这个例子中一样(这new NormalizedDateTimeUserTypeConvention()
是重要的部分):
mappingConfiguration.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly())
.Conventions.Add(
PrimaryKey.Name.Is(x => x.EntityType.Name + "Id"),
new NormalizedDateTimeUserTypeConvention(),
ForeignKey.EndsWith("Id"));
Run Code Online (Sandbox Code Playgroud)
就像我说的,这没有彻底测试,所以要小心!但现在,我需要做的就是改变一行代码(流畅的映射规范),我可以在数据库中的DateTime和DateTimeOffset之间切换.
编辑
根据要求,Fluent NHibernate配置:
为SQL Server构建会话工厂:
private static ISessionFactory CreateSessionFactory(string connectionString)
{
return Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(connectionString))
.Mappings(m => MappingHelper.SetupMappingConfiguration(m, false))
.BuildSessionFactory();
}
Run Code Online (Sandbox Code Playgroud)
对于SQLite:
return Fluently.Configure()
.Database(SQLiteConfiguration.Standard.InMemory)
.Mappings(m => MappingHelper.SetupMappingConfiguration(m, true))
.ExposeConfiguration(cfg => configuration = cfg)
.BuildSessionFactory();
Run Code Online (Sandbox Code Playgroud)
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());
}
}
Run Code Online (Sandbox Code Playgroud)
另一个允许跟踪原始时区偏移的提议:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
DateTimeOffset ICompositeUserType的流畅NNibernate约定将是:
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>();
}
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
3190 次 |
最近记录: |