Читайте несвязанные данные из HasMany-отношений с NHibernate

У меня есть карта NHibernate, которая определяет отношение HasMany для типа, то есть у класса есть список другого класса.

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

У меня есть уровень изоляции ReadUncomited, и я могу записать данные и прочитать их перед фиксацией.

Тем не менее, список всегда пуст, если я не фиксирую в первую очередь.

Есть ли способ заставить NHibernate заполнять объекты данными из HasMany-отношений?

РЕДАКТИРОВАТЬ

Оказывается, что любые не примитивные типы в классе не заполняются незафиксированным чтением, если они фактически не добавлены в класс.

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

public class Member
{
     Guid Id{ get; set; }
}

public class Foo
{
    List<Member> MemberList{ get; set; }
}

// the mapping
public class FooMap
{
    HasMany(x => x.MemberList).KeyColumn("Id").PropertyRef("Id");
}

Однако, если я создаю экземпляр Foo и экземпляр Member и установите последний как ссылку на первый и сохраните оба к базе данных, используя NHibermate, затем Foo будет иметь ожидаемый экземпляр Member когда я читаю его обратно до завершения транзакции.

Если я завершу транзакцию, то Foo будет иметь ожидаемый экземпляр Member на последующих чтениях; Пока транзакция завершена, не имеет значения, Member существовал только как запись базы данных с правильным FK для Foo или это была ссылка на Foo,

Пересмотренный вопрос: возможно ли, чтобы NHibernate заполнял сложные элементы, основанные на FK-отношениях, только во время незафиксированного чтения?

2 ответа

Решение

Здесь, кажется, есть некоторая общая путаница, поэтому поясним несколько вещей:

  • Уровень изоляции транзакции влияет только на то, какие изменения по сравнению с другими транзакциями видны. Транзакция всегда может прочитать данные, которые она изменила сама.

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

  • Однако NHibernate не будет изменять уже загруженный экземпляр. Вы, как разработчик модели, несете ответственность за внесение изменений в загруженные объекты, следя за тем, чтобы модель все еще действовала и соответствовала вашим бизнес-правилам, а затем NHibernate сохранит изменения в БД. Задача NHibernate не состоит в том, чтобы привести часть вашей модели в соответствие с изменениями, которые вы сделали в другой части.

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

  • Важно, если вы закроете сеанс и откроете новый сеанс для вашего запроса. Затем NHibernate снова создаст объект и отразит текущее состояние базы данных. Тот же эффект может иметь место в исходном сеансе, если вы используете Clear() или Evict() в сеансе (после вызова Flush() или Commit()). Эти методы удаляют уже загруженный экземпляр из сеанса, поэтому в следующий раз, когда вы запросите его, сеанс создаст новый экземпляр, который затем, конечно, будет отражать текущее состояние базы данных, как видно из текущей транзакции.

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

Это означает, что вы должны запросить объект-сущность, чтобы получить в нем постоянные данные.

Так, например, если у нас есть эти два класса

public class Customer
{
    public virtual Guid Id { get; set; }
    public virtual ICollection<Order> Orders { get; set; }
}

public class Order
{
    public virtual Guid Id { get; set; }

    public virtual string Description { get; set; }
    //todo add items
}

Тогда нам понадобится такой метод:

public Customer GetCustomerWithUncommitedOrders(Guid customerId)
{
    ITransaction t = null;
    Customer customer = null;

    try
    {
        t = session.BeginTransaction(System.Data.IsolationLevel.ReadUncommitted);


        customer = session.QueryOver<Customer>().Where(x => x.Id == customerId).Fetch(x => x.Orders).Eager.List();


        t.Commit();
    }
    catch (Exception ex)
    {
        //todo log

        if (t != null)
            t.Rollback();
    }
    finally
    {
        if (t != null)
            t.Dispose();
    }

    return customer;
}

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

Другие вопросы по тегам