Do any.NET ORMs используют конструкторы "правильно"?

Это связано концептуально с моим вопросом здесь. Тем не менее, я играл с NHibernate и понял, в чем заключается настоящая суть моего вопроса.

в классическом дизайне OO, чтобы правильно инкапсулировать данные, это общий шаблон для передачи значений конструктору объекта, которые хранятся в элементах данных (полях). Те значения, которые должны не быть изменены предоставляются только с аксессорами (только для чтения свойства). Те, которые есть допустимые изменения имеют как аксессоры, так и мутаторы (свойства чтения-записи). Мне кажется, что правильный O / RM должен соблюдать эти соглашения и использовать доступные конструкторы при создании объекта. Полагаться на свойства чтения-записи, отражение или другие, хакерские (IMHO) методы кажется...неправильный.

есть ли решение .NET O / RM, которое делает это?

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

чтобы обратиться к точке Правина, я знаю, что есть проекты, которые имеют алгоритм "по умолчанию" для выбора конструктора - StructureMap, например, всегда использует конструктор с большинством аргументов, если конструктор помечается пользовательским атрибутом. Я вижу, что это эффективный способ справиться с ситуацией. Возможно, используя контейнер IoC кроме ORM обеспечит такое решение, которое мне нужно , хотя мне кажется, что это, хотя и не по своей сути плохо, ненужный дополнительный шаг для использования ORM.

8 ответов


Я думаю, что большинство ORMs фактически поддерживают эту концепцию, по крайней мере DataObject.Net делает. Этот код работает так, как ожидалось:

[HierarchyRoot(typeof(KeyGenerator), "Id")]
public class Message : Entity
{
  [Field]
  public int Id { get; private set; }

  [Field(Length = 100)]
  public string Text { get; private set; }

  public Message(string Text)
  {
    Text = text;
  }
}

EDIT: DataObjects хранит данные во внутреннем транзакционном состоянии и использует специальный конструктор материализации, созданный PostSharp. Конечно, это не так тривиально, если ORM использует объекты poco.


к сожалению, это невозможно в .NET без какой-либо маркировки конструкторов.

сигнатура метода, хранящаяся для каждого конструктора в метаданных сборки, содержит только тип каждого параметра конструктора. Нет никакого способа для любого .NET ORM действительно знать, какой конструктор использовать. Все, что ОРМ видит, это что-то вроде этого:

.ctor()
.ctor(string, string)
.ctor(string, string, string)

нет никакого способа для ORM узнать, который .параметр ctor соответствует FirstName, LastName и Например, MiddleName для объекта Customer.

чтобы предоставить вам эту поддержку, .NET ORM должен поддерживать чтение в пользовательских атрибутах, которые вы определяете для каждого параметра. Вам нужно будет разметить ваши конструкторы следующим образом:

публичный клиент ([свойство ("FirstName")] строка FirstName, [свойство ("LastName")] строка LastName, [свойство ("MiddleName")] строка MiddleName)

Это имеет 2 недостатка:

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

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


Почему вы думаете, что это неправильно ? Вы хотите, чтобы ваш OR / M выполнял бизнес-логику при восстановлении объекта ? ИМХО, нет.

когда вы загружаете объект из БД, то OR / M должен быть в состоянии восстановить объект, несмотря ни на что. Установка некоторого значения при повторной настройке не должна приводить к запуску какой-то логики, которая изменяет другое значение (которое, возможно, также должно быть задано ORM ... )

даже так, я думаю, что конструктор должен только содержат параметры для тех полей, которые являются обязательными во время создания объекта, чтобы объект находился в "допустимом" состоянии. Кроме того, у вас могут быть общедоступные свойства только для чтения, которые были заданы некоторым вычислением и которые также необходимо сохранить.
Если вы чувствуете, что отражение-это "запах" для воссоздания объектов, как вы собираетесь справляться с этой ситуацией ? Вам нужно будет как-то создать общедоступный метод, который сможет установить значение "только для чтения", но это ломается инкапсуляция, и ты тоже не хочешь этого.


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

Что необходимо учитывать, и что все основные ORMs, похоже, отсутствуют на данный момент, это то, что воссоздание объекта домена из его последнего известного состояния, хранящегося в базе данных или любом другом хранилище данных, не является по своей сути построение или изменение операции; поэтому не следует использовать ни конструкторы, ни задатчики свойств. Скорее, механизм фреймворка, который наиболее близко соответствует такому виду операции, - это сериализация! Уже существуют распознанные шаблоны для хранения состояния объекта, а затем его восстановления через интерфейс ISerializable, стандартный конструктор сериализации и т. д. Сохранение объекта в базе данных принципиально не отличается от сохранения его в потоке; фактически, один из значения перечисления StreamingContextStates, используемого во время сериализации, - это постоянство!. ИМХО, это должен быть стандартный подход при разработке любого механизма сохранения. К сожалению, я не знаю ни одного ORM, который поддерживает это из коробки.

следует также отметить, что объект, предназначенный для сериализации, по-прежнему является POCO; упорство не было нарушено. Ключевым моментом здесь является то, что объект домена должен лучше знать, что такое данные требуется сохранять и восстанавливать его, а также в каком порядке эти данные должны быть восстановлены (что часто важнее). Что объект не должен знать, так это конкретный механизм, с помощью которого он будет храниться: реляционная база данных, плоский файл, двоичный blob и т. д.


клянусь Юпитером, кажется, я понял!

спасибо всем за их вклад, но я собираюсь ответить на свой собственный вопрос. Я только что провел час или около того, копаясь в каталог PoEAA, размышляя о принципах OO и смешивая это с глубокими размышлениями о языке C# и .NET framework.

ответ, который я придумал, то один нужно, чтобы я не мог правильно решить, используя конструкторы, в конечном итоге не связан с сам конструктор в конце концов. Это загрузка!

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

Я все еще думаю, что использование конструктора, а не отражение или какой-либо другой метод для заполнения полей родительский класс был бы лучше (по крайней мере, для не-коллекций...ленивая загрузка имеет свое место), но я определенно вижу, где конструктор no-arg имеет свое место.


Это - в общем случае - невозможно Для или mapper использовать только конструкторы. Предположим, что объект инициализируется значениями, переданными конструктору, и после этого состояние изменяется вызовами метода. В этом случае объект может сохраняться с состоянием, которое не является допустимым начальным состоянием и поэтому отклоняется конструктором.

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


Мистер скит недавно предложил шаблон "строитель". Я сделал это публичным классом в классе POCO. Он имеет те же свойства, что и POCO, но все чтение/запись. POCO читается только там, где это необходимо, но частный набор. Таким образом, строитель может установить properites при создании экземпляра POCO и не иметь фанковых args для конструктора. Рода неизменность popcicle там.


Это зависит от того, что вы считаете значениями, которые не следует изменять. Автоинкременты, вычисляемые столбцы и т.д., являются хорошими кандидатами для этого.

Это точно возможно, Я использую ORM, который я написал, и он выдает исключение, если вы пытаетесь установить значение свойства только для чтения.

обновление:

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