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 недостатка:
- нет никакого способа (которые я может подумать, кто-то, вероятно, поправит меня), что это может пойти в файл сопоставления.
- вам все равно нужно написать то же сопоставление, что и всегда, потому что 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, который я написал, и он выдает исключение, если вы пытаетесь установить значение свойства только для чтения.
обновление:
помните, что конструкторы также используются для сохраненных данных. Это общий шаблон, чтобы ваш объект принимал ПК(ы) в конструктор, и он автоматически получит эту запись.