当前位置:  开发笔记 > 数据库 > 正文

单元测试NHibernate w/SQLite和DateTimeOffset映射

如何解决《单元测试NHibernatew/SQLite和DateTimeOffset映射》经验,为你挑选了2个好方法。

移植应用程序以从不同的ORM使用NHibernate.

我已经开始实现对内存SQLite数据库运行单元测试的能力.这适用于前几批测试,但我只是遇到了障碍.我们的应用程序将在现实世界中与SQL 2008服务器通信,因此,几个模型当前具有DateTimeOffset属性.在非测试应用程序中映射到/来自SQL 2008时,这一切都正常.

在配置数据库或其他工具时是否有某种机制,以便当我使用来自我的SQLite测试夹具的会话时,DateTimeOffset的东西被"自动神奇地"处理为更平台无关的DateTime?



1> Rich Tebb..:

巧合的是,我今天自己就遇到了这个问题:)我没有彻底测试过这个解决方案,而且我是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;
        }
    }
}

databaseTimeZone字段包含一个TimeZone描述用于在数据库中存储值的时区的字段.DateTimeOffset存储前,所有值都将转换为此时区.在我当前的实现中,它被硬编码到本地时区,但您可以始终定义ITimeZoneProvider接口并将其注入构造函数.

要在不修改所有类映射的情况下使用此用户类型,我在Fluent NH中创建了一个约定:

public class NormalizedDateTimeUserTypeConvention : UserTypeConvention
{
}

我在我的映射中应用了这个约定,就像在这个例子中一样(这new NormalizedDateTimeUserTypeConvention()是重要的部分):

mappingConfiguration.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly())
                .Conventions.Add(
                PrimaryKey.Name.Is(x => x.EntityType.Name + "Id"), 
                new NormalizedDateTimeUserTypeConvention(),
                ForeignKey.EndsWith("Id"));

就像我说的,这没有彻底测试,所以要小心!但现在,我需要做的就是改变一行代码(流畅的映射规范),我可以在数据库中的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();
}

对于SQLite:

return Fluently.Configure()
            .Database(SQLiteConfiguration.Standard.InMemory)
            .Mappings(m => MappingHelper.SetupMappingConfiguration(m, true))
            .ExposeConfiguration(cfg => configuration = cfg)
            .BuildSessionFactory();

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



2> nulltoken..:

另一个允许跟踪原始时区偏移的提议:

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

DateTimeOffset ICompositeUserType的流畅NNibernate约定将是:

public class DateTimeOffsetTypeConvention : IPropertyConvention, IPropertyConventionAcceptance
{
    public void Accept(IAcceptanceCriteria criteria)
    {
        criteria.Expect(x => x.Type == typeof(DateTimeOffset));
    }

    public void Apply(IPropertyInstance instance)
    {
        instance.CustomType();
    }
}

推荐阅读
linjiabin43
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有