Запрос NHibernate экземпляром Transient приводит к сохранению временного экземпляра-исключения

У меня есть старый код, выполняющий запрос, в котором модель может быть временной. То есть модель с некоторыми полями, заполненными пользовательским вводом, которые затем используются как часть запроса. Он работал в NH 2.1.x, но не работает в последней версии.

Вызвано исключение: «объект ссылается на несохраненный переходный экземпляр — сохраните переходный экземпляр перед очисткой». Это происходит, когда NH пытается выполнить запрос, используя не сохраняемый объект как часть запроса.

Упрощенная версия для иллюстрации проблемы.

abstract class BaseModel
   public virtual long Id { get; set; }

class Car : BaseModel
    public virtual Engine Engine { get;set; }

class Engine : BaseModel
    public virtual string Kind { get; set; }


public static IList<Car> GetByEngine(Engine eng) {
  ICriteria c = Session.CreateCriteria<Car>();
  c.Add(Expression.Eq("Engine", eng));
  return c.List<Car>(); // <--- Error occurs here
}

И код вызова эквивалентен этому:

    Engine obj = new Engine { Id = 42 }; // Transient instance
    var x = GetByEngine(obj);

Я ожидал, что произойдет (что похоже на поведение старой версии NHibernate), так это то, что переданный движок используется только для получения идентификатора. То есть генерация SQl типа select.... from Cars where Engine = 42

Но с новой версией NHibernate, кажется, проверяет, что движок, используемый в Expression, действительно сохраняется.

Есть ли способ избежать загрузки постоянного движка перед выполнением запроса?


person Thomas    schedule 12.01.2012    source источник


Ответы (3)


да, используя Session.Load(), который возвращает объект, если он уже находится в сеансе, или lazyLoadingProxy, если он отсутствует.

public static IList<Car> GetByEngine(Engine eng) {
    ICriteria c = Session.CreateCriteria<Car>();
    c.Add(Expression.Eq("Engine", Session.Load<Engine>(eng.Id)));
    return c.List<Car>();
}
person Firo    schedule 12.01.2012
comment
Извините за задержку с ответом. Это поможет и ответит на мой вопрос в ОП. Это решило проблему под рукой. - person Thomas; 31.01.2012

Вы можете использовать метод Session.Load, который существует для таких сценариев.
Метод Load вернет объекту Proxy и не попадет в базу данных, пока вы не получите доступ к одному из его свойств (за исключением свойства Primary key, которое вообще не попадет в БД).

Использование:

Engine obj = session.Load<Engine>(42);
var x = GetByEngine(obj);

проверьте эту статью о Session.Get и Session.Load

person gdoron is supporting Monica    schedule 12.01.2012

Я думаю, вы могли бы сделать что-то вроде этого:

public static IList<Car> GetByEngine(Engine eng) {
    ICriteria c = Session.CreateCriteria<Car>().CreateCriteria("Engine");
    c.Add(Expression.Eq("Id", eng.Id));
    return c.List<Car>();
}

В любом случае... как возможно, что машина с таким двигателем существует, если вы ее еще не сохранили?

person Ivo    schedule 12.01.2012
comment
Пример надуманный, потому что фактическое приложение слишком сложное, чтобы публиковать его здесь. Это был минимальный сценарий, воспроизводящий поведение. - person Thomas; 31.01.2012