Используют ли какие-либо .NET ORM конструкторы "правильно"? - PullRequest
7 голосов
/ 29 мая 2009

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

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

Существует ли решение для .NET O / RM, которое делает это?

EDIT

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

Ответы [ 8 ]

3 голосов
/ 29 мая 2009

Я думаю, что большинство ORM действительно поддерживают эту концепцию, по крайней мере, 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.

2 голосов
/ 29 мая 2009

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

Подпись метода, сохраненная для каждого конструктора в метаданных сборки, содержит только тип каждого параметра для конструктора. У ЛЮБОГО .NET ORM нет способа узнать, какой конструктор использовать. Все, что видит ORM, выглядит примерно так:

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

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

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

public Customer ([Property ("FirstName")] string FirstName, [Property ("LastName")] string LastName, [Property ("MiddleName")] string MiddleName)

У этого есть 2 недостатка:

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

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

1 голос
/ 01 июня 2009

Боже, думаю, я понял!

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

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

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

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

1 голос
/ 30 мая 2009

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

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

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

1 голос
/ 29 мая 2009

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

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

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

0 голосов
/ 29 мая 2009

г. Скит предложил шаблон «строитель» некоторое время назад. Я сделал это публичным классом в классе POCO. Он имеет те же свойства, что и POCO, но все для чтения / записи. POCO только для чтения, где это необходимо, но ЧАСТНЫЙ КОМПЛЕКТ. Таким образом, конструктор может устанавливать свойства, когда создается экземпляр POCO, и не иметь прикольных аргументов для конструктора. Вид "неизменности попкорна" там.

0 голосов
/ 29 мая 2009

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

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

0 голосов
/ 29 мая 2009

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

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

Обновление:

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

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