Поблочное тестирование NHibernate w/SQLite и отображения DateTimeOffset

Портирование по приложению для использования NHibernate от другого ORM.

Я начал помещать на месте способность выполнить наши модульные тесты против в базе данных SQLite памяти. Это работает над первыми несколькими пакетами тестов, но я просто наткнулся на препятствие. Наше приложение было бы в реальном мире говорить с сервером SQL 2008, и как таковое, несколько моделей в настоящее время имеют свойство DateTimeOffset. При отображении на SQL 2008 в нетестовых приложениях это все хорошо работает.

Есть ли некоторый механизм или в конфигурировании базы данных или в некотором другом средстве так, чтобы, когда я использую сессию от своего тестового приспособления SQLite, что материал DateTimeOffset "автоволшебно" обрабатывается как больше агностика платформы DateTime?

14
задан bakasan 21 February 2010 в 05:08
поделиться

1 ответ

По совпадению, я сам только сегодня столкнулся с этой проблемой :) Я не тестировал это решение досконально, и я новичок в NHibernate, но оно, кажется, работает в тривиальном случае, который я пробовал.

Сначала вам нужно создать реализацию IUserType, которая будет конвертировать из DateTimeOffset в DateTime. В блоге 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<NormalizedDateTimeUserType>
{
}

и применил эту конвенцию в моих связках, как в этом примере (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());
    }
}
13
ответ дан 1 December 2019 в 12:52
поделиться
Другие вопросы по тегам:

Похожие вопросы: