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





Подход, который я бы использовал для моделирования этого, выглядит следующим образом:
Объект клиента содержит ICollection
План PaymentPlan для клиента будет сопоставлен с использованием пакета, который использует таблицу сведений, чтобы установить, какой идентификатор клиента сопоставлен с какими PaymentPlans. При использовании каскада all-delete-orphan, если клиент был удален, будут удалены как записи из подробностей, так и платежные планы, принадлежащие клиенту.
Объект PaymentPlan содержит объект PlanTerms, который представляет условия плана платежей.
PlanTerms будет сопоставлен с PaymentPlan с использованием каскадного сопоставления «многие-к-одному» с сохранением-обновлением, которое просто вставит ссылку на соответствующий объект PlanTerms в PaymentPlan.
Используя эту модель, вы можете создать PlanTerms независимо, а затем, когда вы добавляете новый PaymentPlan клиенту, вы должны создать новый объект PaymentPlan, передав соответствующий объект PlanTerms, а затем добавить его в коллекцию соответствующего клиента. Наконец, вы должны сохранить клиента и позволить nhibernate каскадировать операцию сохранения.
В итоге вы получите объект Customer, объект PaymentPlan и объект PlanTerms, в котором клиент (таблица клиентов) владеет экземплярами PaymentPlans (таблица деталей), которые все соответствуют определенным PlanTerms (таблица плана).
У меня есть еще несколько конкретных примеров синтаксиса сопоставления, если это необходимо, но, вероятно, лучше всего проработать его с вашей собственной моделью, а у меня недостаточно информации о таблицах базы данных, чтобы предоставить какие-либо конкретные примеры.
Я не знаю, возможно ли это из-за того, что мой опыт NHibernate ограничен, но не могли бы вы создать класс BaseDetail, который имеет только свойства для Details, поскольку они отображаются непосредственно в таблице Detail.
Затем создайте второй класс, который наследуется от класса BaseDetail, который имеет дополнительный объект родительского плана, чтобы вы могли создать класс BaseDetail, когда вы хотите просто создать строку деталей и назначить ей PlanId, но если вам нужно заполнить полную деталь запись с объектом родительского плана можно использовать унаследованный класс Detail.
Я не знаю, имеет ли это большой смысл, но дайте мне знать, и я проясню дальше.
Я думаю, что проблема в том, что ваш объект AcceptedOffer содержит объект Plan, а затем ваш объект Plan, по-видимому, содержит коллекцию AcceptedOffers, содержащую объекты AcceptedOffer. То же самое и с клиентами. Я думаю, что тот факт, что объекты являются дочерними по отношению друг к другу, и является причиной вашей проблемы.
Точно так же то, что делает ваш AcceptedOffer сложным, состоит в том, что он выполняет две обязанности: он указывает предложения, включенные в план, и указывает на принятие клиентом. Это нарушает принцип единой ответственности.
Возможно, вам придется различать Предложение, входящее в план, и Предложение, которое принимается клиентами. Итак, вот что я собираюсь сделать:
Я думаю, что это в достаточной мере распутает ваши сопоставления NHibernate и проблемы с сохранением объектов. :)
Я бы воздержался от наличия дочернего объекта, содержащего своего логического родителя, когда вы это сделаете, он может очень быстро стать очень беспорядочным и очень рекурсивным. Я бы посмотрел, как вы собираетесь использовать модель предметной области, прежде чем делать что-то подобное. Вы можете легко сохранить ссылки на идентификаторы в таблицах и просто оставить их несопоставленными.
Вот два примера сопоставления, которые могут подтолкнуть вас в правильном направлении. Мне пришлось изменить имена таблиц и т. д., Но это может помочь. Я бы, вероятно, также предложил сопоставить StatusId с перечислением.
Обратите внимание на то, как сумка эффективно отображает таблицу деталей в коллекции.
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping default-cascade = "save-update" xmlns = "urn:nhibernate-mapping-2.2">
<class lazy = "false" name = "Namespace.Customer, Namespace" table = "Customer">
<id name = "Id" type = "Int32" unsaved-value = "0">
<column name = "CustomerAccountId" length = "4" sql-type = "int" not-null = "true" unique = "true" index = "CustomerPK"/>
<generator class = "native" />
</id>
<bag name = "AcceptedOffers" inverse = "false" lazy = "false" cascade = "all-delete-orphan" table = "details">
<key column = "CustomerAccountId" foreign-key = "AcceptedOfferFK"/>
<many-to-many
class = "Namespace.AcceptedOffer, Namespace"
column = "AcceptedOfferFK"
foreign-key = "AcceptedOfferID"
lazy = "false"
/>
</bag>
</class>
</hibernate-mapping>
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping default-cascade = "save-update" xmlns = "urn:nhibernate-mapping-2.2">
<class lazy = "false" name = "Namespace.AcceptedOffer, Namespace" table = "AcceptedOffer">
<id name = "Id" type = "Int32" unsaved-value = "0">
<column name = "AcceptedOfferId" length = "4" sql-type = "int" not-null = "true" unique = "true" index = "AcceptedOfferPK"/>
<generator class = "native" />
</id>
<many-to-one
name = "Plan"
class = "Namespace.Plan, Namespace"
lazy = "false"
cascade = "save-update"
>
<column name = "PlanFK" length = "4" sql-type = "int" not-null = "false"/>
</many-to-one>
<property name = "StatusId" type = "Int32">
<column name = "StatusId" length = "4" sql-type = "int" not-null = "true"/>
</property>
</class>
</hibernate-mapping>
Совет, который может (а может и не быть) полезен в NHibernate: вы можете сопоставить свои объекты с представлениями, как если бы представление было таблицей. Просто укажите имя представления как имя таблицы; пока все поля NOT NULL включены в представление и отображение будет работать нормально.
Не видел диаграммы вашей базы данных, пока писал.
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping default-cascade = "save-update" xmlns = "urn:nhibernate-mapping-2.2">
<class lazy = "false" name = "Namespace.Customer, Namespace" table = "Customer">
<id name = "Id" type = "Int32" unsaved-value = "0">
<column name = "customer_id" length = "4" sql-type = "int" not-null = "true" unique = "true" index = "CustomerPK"/>
<generator class = "native" />
</id>
<bag name = "AcceptedOffers" inverse = "false" lazy = "false" cascade = "all-delete-orphan">
<key column = "accepted_offer_id"/>
<one-to-many class = "Namespace.AcceptedOffer, Namespace"/>
</bag>
</class>
</hibernate-mapping>
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping default-cascade = "save-update" xmlns = "urn:nhibernate-mapping-2.2">
<class lazy = "false" name = "Namespace.AcceptedOffer, Namespace" table = "Accepted_Offer">
<id name = "Id" type = "Int32" unsaved-value = "0">
<column name = "accepted_offer_id" length = "4" sql-type = "int" not-null = "true" unique = "true" />
<generator class = "native" />
</id>
<many-to-one name = "Plan" class = "Namespace.Plan, Namespace" lazy = "false" cascade = "save-update">
<column name = "plan_id" length = "4" sql-type = "int" not-null = "false"/>
</many-to-one>
</class>
</hibernate-mapping>
Вероятно, это должно помочь (я сделал только примеры сопоставлений для коллекций, вам нужно будет добавить другие свойства).