nhibernate: каковы лучшие практики для реализации равенства? - PullRequest
7 голосов
/ 20 января 2010

Я думаю, что сущности должны реализовать равенство путем сравнения первичных ключей по умолчанию, но документация nhibernate рекомендует использовать бизнес-идентичность:

Наиболее очевидный способ - реализовать Equals () / GetHashCode () путем сравнения значения идентификатора обоих объектов. Если значение одинаковое, оба должны быть одной и той же строкой базы данных, поэтому они равны (если оба будут добавлены в ISet, у нас будет только один элемент в ISet). К сожалению, мы не можем использовать этот подход. NHibernate будет присваивать значения идентификаторов только постоянным объектам, у вновь созданного экземпляра не будет никакого значения идентификатора! Мы рекомендуем реализовать Equals () и GetHashCode (), используя равенство бизнес-ключей.

Равенство бизнес-ключей означает, что метод Equals () сравнивает только свойства, которые образуют бизнес-ключ, ключ, который идентифицирует наш экземпляр в реальном мире (естественный ключ-кандидат)

И пример (также из документа):

public override bool Equals(object other)
{
    if (this == other) return true;

    Cat cat = other as Cat;
    if (cat == null) return false; // null or not a cat

    if (Name != cat.Name) return false;
    if (!Birthday.Equals(cat.Birthday)) return false;

    return true;
}

Это заставило меня задуматься, потому что понятие бизнес-идентичности (в соответствии с примером) такое же, как сравнение по синтаксису, которое в основном является типом семантики, которую я ассоциирую с ValueObjects. Причина, по которой не используются первичные ключи базы данных в качестве значений сравнения, заключается в том, что это изменит хеш-код объекта, если первичный ключ не генерируется на стороне клиента (например, для инкремента), и вы используете какой-то набор хеш-таблиц (например, ISet) для хранения ваших сущностей.

Как создать хорошую реализацию равенства, которая не нарушает общие правила для равенства / хэш-кода (http://msdn.microsoft.com/en-us/library/bsc2ak47.aspx) и также соответствует правилам nhibernate?

1 Ответ

13 голосов
/ 20 января 2010

Это известная проблема с ORM. Здесь я обрисовываю решения, о которых знаю, и даю несколько советов.

1 Суррогатный / первичный ключ: автоматически сгенерированный

Как вы упомянули, если объект не был сохранен, это не сработает.

2 Суррогатный / первичный ключ: присвоенное значение

Вы можете решить присвоить значение PK в коде, таким образом, объект всегда имеет идентификатор и может использоваться для сравнения. См. Не позволяйте гибернации украсть вашу личность .

3 Натуральный ключ

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

4 Значения объекта

Использование значений объекта для равенства возможно. Но есть и другие недостатки, вы упомянули. Это может быть проблематично, если значения изменяются и объект находится в коллекции . Например, если у вас есть Set с двумя объектами, которые сначала отличались, но затем вы изменяете значения, пока они являются ссылочными в наборе, чтобы они стали равными. Тогда вы нарушаете договор Set. См Hibernate равно и хэш-код .

5 Смешанный: значение + автогенерация первичных / суррогатных ключей

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

У всех есть свои плюсы и минусы. ИМХО, лучше всего 3, если это возможно с вашей моделью домена. В остальном я использовал 5, и это сработало, хотя при использовании коллекций все еще есть ловушка. Я никогда не использовал 2, но это звучит также разумное решение, если вы найдете способ генерировать PK в коде. Может быть, у других людей есть указатели для этого.

...