Работа с идентификаторами при проектировании объектов entity

#c# #.net #database #entity #class-design

#c# #.net #База данных #сущность #класс-дизайн

Вопрос:

Некоторое время я думал о том, как обращаться с объектами, которым назначены идентификаторы базой данных.

Типичный объект, представляющий объект table, может выглядеть следующим образом:

 public class Test
{
    public int Id { get; private set; }
    public string Something { get; set; }
}
  

Предположим, мы хотели бы использовать этот объект для вставки, извлечения и обновления объектов в базе данных. Что касается извлечения и обновления, у нас нет проблем, поскольку поле Id всегда имеет значение.

Однако, если мы хотим вставить новый объект типа Test в базу данных, поле Id все равно должно иметь значение. Мы могли бы просто использовать «0», поскольку его вряд ли можно использовать в качестве ключа базы данных, но на самом деле это не очень хороший дизайн.

Аналогично, если мы изменим ситуацию и сделаем свойство Id равным нулю, мы могли бы использовать null для объектов, которым база данных еще не присвоила идентификатор. Однако теперь объект, извлеченный из базы данных, может не иметь идентификатора (что разрешено дизайном класса, но не дизайном базы данных)

Есть хорошие идеи о том, как создать хороший дизайн для этой проблемы?

Комментарии:

1. невозможно получить объект без идентификатора, если он был извлечен из базы данных .. как только вы его вставите, он сразу получит идентификатор (если у вас нет какой-то действительно странной базы данных)

2. @xs0 Очевидно. Однако, если смотреть строго на определение типа объекта, это возможно.

3. Ну, вы говорите обратное в своем вопросе.. Но в любом случае, что вы хотите делать с этими объектами? Пока вы не объясните, какие операции будут выполняться с ними, неясно, в чем проблема..

4. Вы используете Entity Framework или вы просто имели в виду «сущность» в общем смысле?

5. Я имею в виду entity в общем смысле, хотя проблема, очевидно, существует и при использовании Entity Framework.

Ответ №1:

Если вы рассматриваете id это как способ идентификации / предоставления уникальности объекту в вашем приложении, это должно быть обработано database (если, конечно, у вас есть другие способы присвоения идентификаторов объектам).

Если это не так (например, это свойство объекта, определяемое бизнес-потребностями) — является ли 0 допустимым / хорошим значением дизайна или нет, зависит исключительно от этих бизнес-потребностей. Является 0 допустимым значением, скажем, с точки зрения конечного пользователя?

Вы всегда можете перенести свойства вашего объекта в отдельный класс, если чувствуете, что наличие объектов без ids set в вашем приложении проблематично. Вы будете использовать такой класс по существу только для переноса параметров для еще не созданного объекта (процесс создания завершается вставкой в базу данных). Как только объект будет вставлен, идентификатор присвоен и прочее — вы сможете работать с вашим обычным объектом. Скажет ли ваш пользователь «О, щелчок! Что это?» или «Ок .. Я знаю, что делать». однажды к id = 0 обратился?

Редактировать

Этот вопрос (или, скорее, мой ответ о переносе параметров) напомнил мне о факте, который однажды меня удивил. Когда рождается ребенок, он не существует в системе до тех пор, пока его родители официально не зарегистрируют его и ему не присвоят личный идентификационный номер. Итак, технически, without id — child не существует (по крайней мере, с системной точки зрения), даже если все знают, что она родилась и все такое. То же самое с базой данных / вашим приложением — объект без id (тот, который база данных не может идентифицировать) не существует — это просто набор параметров. Немного странно, но я надеюсь, что моя точка зрения ясна 🙂

Ответ №2:

Нет ничего плохого в разработке класса таким образом, чтобы идентификатор 0 указывал, что объект еще не был сериализован. В прошлом я создавал системы, которые успешно использовали этот подход. Просто убедитесь, что эта семантика четко определена в вашем API и что это значение соблюдается во всем коде.

Одной из ловушек, которой следует остерегаться, является использование идентификатора для определения отношения равенства (например, для генерации хэш-кодов для словаря). Это должно быть сделано только для ненулевых идентификаторов. Для проверки равенства с двумя нулевыми идентификаторами может использоваться ссылочное равенство.

Однако, поскольку идентификатор не сохраненных объектов может измениться в какой-то момент в будущем, очень важно, чтобы такие объекты никогда не сохранялись в словаре. Или, по крайней мере, такие элементы должны быть удалены из любых словарей перед сохранением, а затем восстановлены впоследствии с использованием нового идентификатора.

С этой единственной оговоркой этот дизайн должен работать нормально.

 public class Test : IEquatable<Test>
{ 
    /// <summary>
    /// The unique identifier for this Test entity, or zero if this
    /// Test entity has not yet been serialized to the database.
    /// </summary>
    public int Id { get; private set; } 

    public string Something { get; set; }

    public override bool Equals(object obj)
    {
        return Equals(obj as Test);
    }

    public bool Equals(Test other)
    {
        if (other == null)
            return false;
        // Distinct entities may exist with the Id value of zero.
        if (Id == 0)
            return object.ReferenceEquals(this, other);
        return Id == other.Id;
    }

    /// <summary>
    /// Gets a hash code for this Test entity. Warning: an instance with
    /// an Id of zero will change its identity when saved to the DB. Use with care.
    /// </summary>
    /// <returns>The hash code.</returns>
    public override int GetHashCode()
    {
        return Id;
    }
} 
  

Комментарии:

1. Я понимаю, что идентификатор 0, указывающий на то, что у объекта нет идентификатора, безусловно, является допустимым рабочим решением; Мне просто интересно, смогу ли я сделать что-то лучше. Я считаю, что всегда лучше не допускать «неправильных» данных и документировать свой выход из этого, в отличие от того, чтобы это было вообще невозможно.

2. @DEHAAS — Да, это был один из вариантов в вашем первоначальном вопросе. Я просто говорил, что, на мой взгляд, это нормально. Я не вижу разницы между определением 0 как несохраненного и определением null таким образом, за исключением того, что первое вызывает немного меньше проблем, чем второе. На мой взгляд, правильное документирование и реализация специального значения является хорошим решением.

3. @DEHAAS — Подумайте об этом так: в double первый бит интерпретируется, чтобы указать, является ли числовое значение положительным или отрицательным. Однако он немного похож на любой другой и может быть интерпретирован другими способами в других контекстах. Определение определенных комбинаций битов как означающих одно, а не другое, является базовой частью программирования и не указывает на плохой дизайн.

Ответ №3:

Итак, у вас есть класс, который содержит идентификатор таблицы базы данных в виде поля, поэтому в случае извлечения / удаления и обновления идентификатор вашей сущности совпадает с идентификатором записи базы данных. В случае вставки вы могли бы получить значение identify только что вставленной записи и обновить свой объект этим значением. Таким образом, у вас могли бы быть отдельные процедуры хранения для вставки / обновления / извлечения и удаления. SP вставки может вернуть вам идентификатор записи, только что вставленной как out parameter . Надеюсь, я отвечу на ваш вопрос.

Комментарии:

1. Я согласен с утверждением Абдула о том, что ответственность за определение новых значений идентификаторов и установку любых других значений по умолчанию, подходящих для новых записей, лежит на базе данных. Вам нужно запросить новую строку из базы данных, а затем разрешить пользователю изменять новую строку, если они хотят.

2. Конечно, объекту будет присвоен идентификатор после вставки в базу данных, и этот идентификатор может быть установлен для вставляемого объекта. Однако я все еще вижу проблему с представлением объекта до присвоения идентификатора.

3. Итак, в чем проблема, которую вы видите?