Сохранение дочерних коллекций с помощью NHibernate - PullRequest
1 голос
/ 14 апреля 2010

Я нахожусь в процессе или изучаю NHibernate, так что терпите меня.

У меня есть класс Order и класс Transaction. Заказ имеет отношение один ко многим с транзакцией. Таблица транзакций в моей базе данных имеет ненулевое ограничение на внешний ключ OrderId.

Класс заказа:

    public class Order {
    public virtual Guid Id { get; set; }
    public virtual DateTime CreatedOn { get; set; }
    public virtual decimal Total { get; set; }

    public virtual ICollection<Transaction> Transactions { get; set; }

    public Order() {
        Transactions = new HashSet<Transaction>();
    }
}

Отображение заказа:

  <class name="Order" table="Orders">
<cache usage="read-write"/>
<id name="Id">
  <generator class="guid"/>
</id>
<property name="CreatedOn" type="datetime"/>
<property name="Total" type="decimal"/>
<set name="Transactions" table="Transactions" lazy="false" inverse="true">
  <key column="OrderId"/>
  <one-to-many class="Transaction"/>
</set>

Класс транзакции:

    public class Transaction {
    public virtual Guid Id { get; set; }
    public virtual DateTime ExecutedOn { get; set; }
    public virtual bool Success { get; set; }

    public virtual Order Order { get; set; }
}

Отображение транзакции:

  <class name="Transaction" table="Transactions">
<cache usage="read-write"/>
<id name="Id" column="Id" type="Guid">
  <generator class="guid"/>
</id>
<property name="ExecutedOn" type="datetime"/>
<property name="Success" type="bool"/>
<many-to-one name="Order" class="Order" column="OrderId" not-null="true"/>

На самом деле я не хочу двунаправленной ассоциации. Нет необходимости, чтобы мои объекты транзакций ссылались на объект своего заказа напрямую (мне просто нужен доступ к транзакциям заказа). Однако я должен был добавить это, чтобы Order.Transactions сохранялся в базе данных:

Repository:

        public void Update(Order entity)
    {
        using (ISession session = NHibernateHelper.OpenSession()) {
            using (ITransaction transaction = session.BeginTransaction()) {
                session.Update(entity);

                foreach (var tx in entity.Transactions) {
                    tx.Order = entity;
                    session.SaveOrUpdate(tx);
                }
                transaction.Commit();
            }
        }
    }

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

То, что я пытался обойти, - это явное сохранение транзакции перед сохранением заказа, вместо этого просто добавьте транзакции в заказ и затем сохраните заказ:

        public void Can_add_transaction_to_existing_order()
    {
        var orderRepo = new OrderRepository();
        var order = orderRepo.GetById(new Guid("aa3b5d04-c5c8-4ad9-9b3e-9ce73e488a9f"));

        Transaction tx = new Transaction();
        tx.ExecutedOn = DateTime.Now;
        tx.Success = true;

        order.Transactions.Add(tx);

        orderRepo.Update(order);
    }

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

Большое спасибо, Бен

1 Ответ

3 голосов
/ 14 апреля 2010

Вам необходимо установить атрибут каскада в вашем отображении, чтобы постоянство каскадно связывалось с дочерними объектами:

<set name="Transactions" table="Transactions" lazy="false" inverse="true" cascade="all-delete-orphan">

Ваш объект Order должен иметь метод AddTransaction, который устанавливает родительскую ссылку для дочернего элемента. Что-то вроде:

public void AddTransaction(Transaction txn)
{
    txn.Order = this;
    Transactions.Add(txn);
}

Это приведет к сохранению объекта транзакции при сохранении ордера. Вы можете выставить свойство Order в транзакции с модификатором internal, чтобы оно не было публично видно.

...