Есть ли способ заставить NHibernate рассматривать строковые ключи как нечувствительные к регистру? - PullRequest
4 голосов
/ 20 декабря 2011

Я использую кэш второго уровня NHibernate для сущности, первичный ключ которой является строковым столбцом. База данных нечувствительна к регистру, однако, когда я получаю объект, используя тот же Id, но с другим регистром, тогда NHibernate рассматривает объект как другой объект в кэше. Например, у меня есть таблица настроек со столбцом имени в качестве первичного ключа. Если я попытаюсь извлечь сущность с помощью session.Get<Setting>("TestMode"), Get<Setting>("testmode") или Get<Setting>("TESTMODE"), то я попаду в базу данных 3 раза и поместу 3 отдельных сущности в кэш 2-го уровня. Если я затем что-то изменю в своем объекте Setting с помощью NHibernate, он обновит кэшированный объект, который фактически соответствует регистру на объекте, поскольку он хранится в базе данных (в данном случае тот, который называется «TestMode»). Проблема в том, что другие объекты все еще будут в кеше, но будут устаревшими.

Я понимаю, почему NHibernate рассматривает идентификаторы как регистрозависимые по умолчанию, но, насколько я могу найти, не существует способа установить регистронезависимость на уровне объекта или конфигурации. Можно ли это сделать, и если да, то как?

Если это поможет, я использую Fluent NHibernate. Мой Id столбец указан так:

Id(x => x.Name).GeneratedBy.Assigned().Column("Name");

UPDATE Ниже приведены реализации Equals () и GetHashCode () из моей сущности Setting.

public override bool Equals(object obj)
{
    if (obj == null)
        return false;
    if (obj is Setting)
    {
        if (object.ReferenceEquals(this, obj))
            return true;
        if (this.Name.Equals(((Setting)obj).Name, StringComparison.OrdinalIgnoreCase)) // Ignore case because our databases are case insensitive!
            return true;
    }
    return false;
}

public override int GetHashCode()
{
    return Name.ToUpperInvariant().GetHashCode();
}

ОБНОВЛЕНИЕ 2 Я профилировал с помощью NHibernate Profiler, и при выполнении поиска для «TestMode», «testmode», «TestMode», «Testmode», «TESTMODE» (в таком порядке) отображается только попадание в кэш для второго «TestMode». Он показывает «Настройка загрузки кэша 2-го уровня (TestMode / * id * /)», все остальные показывают SELECT ... FROM Setting setting0_ WHERE setting0_.Name = "testmode" (в любом случае, о котором просил вызов). При нажатии на ссылку «См. 1 строку (и), полученную в результате этого оператора», он показывает строку с столбцом «Имя», имеющую правильный регистр «TestMode», поэтому он явно извлекает объект с правильным регистром для самого столбца, но кажется, что он определенно ассоциирует сущность с идентификатором, с которым была вызвана функция Get (). Это не похоже на нормальное поведение, но я не смог найти никакой информации об особенностях работы кэша 2-го уровня, а только об основах его использования из поваренной книги NHibernate.

Хотя я сомневаюсь, что это должно иметь значение, я использую HashtableCacheProvider для всех этих тестов. (для производства мы будем использовать платный провайдер кэширования)

Ответы [ 3 ]

1 голос
/ 21 декабря 2011

Если вы рассматриваете кеш как словарь со строковым ключом, очевидно, что ключи "TEST" и "test" различны, потому что C # чувствителен к регистру.

Но что-то здесь не так. Откуда вы знаете, что NH помещает три объекта в кэш 2-го уровня? Предполагая, что вы используете базу данных без учета регистра, все три запроса, сгенерированные методом Get, будут возвращать одну и ту же строку, а свойство ID объекта будет установлено с использованием значения, возвращаемого запросом, а не значения, предоставленного Get метод. Объекты будут кэшироваться с использованием идентификатора объекта. Все три Gets возвращают экземпляры с идентификатором в одном и том же случае («TestMode»)?


Обновление на основе изменений вашего вопроса, если я вас правильно понимаю:

  • Get ("TestMode") кэширует объект должным образом и извлекает его из кэша при последующих запросах для "TestMode"
  • Get ("testmode") или любой другой случай возвращается в базу данных

Такого поведения я бы ожидал. Значение, предоставленное для идентификатора, используется для поиска объекта в кэше. Если совпадение найдено, то сущность извлекается из кэша; если нет, база данных запрашивается. После извлечения сущности NHibnernate пытается добавить его в кеш, используя его идентификатор в качестве ключа, но он уже существует, поэтому кэшированное значение обновляется, заменяется или игнорируется.

Ключевым моментом является то, что идентификатор, предоставленный для запроса, используется в качестве ключа для определения местоположения объекта в кэше, но идентификатор объекта используется в качестве ключа при добавлении в кэш.

0 голосов
/ 20 декабря 2011

Я думаю, что вы можете обойти это, определив пользовательский тип строки, скажем CaseInsensitiveStringType, а затем

Id(x => x.Name)
    .GeneratedBy
    .Assigned()
    .Column("Name")
    .CustomType<CaseInsensitiveStringType>();
0 голосов
/ 20 декабря 2011

Кэш зависит от предоставленной реализации Equals().

Итак, измените вашу реализацию Equals() для этого класса так, чтобы она учитывала проблемы с кейсами.Либо сравните с нечувствительными настройками, либо просто .ToUpper()/.ToLower() идентификаторы.

ОБНОВЛЕНИЕ: кажется, что на * 1008 есть повторяющийся вопрос: проблема NHibernate с назначенным идентификатором строки и различными символами регистра .Попробуйте воспользоваться принятым решением ответа

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...