Объяснение проблемы
Когда вы загружаете объект, Session
отслеживает первичный ключ базы данных для объекта, а также объект объекта reference
(расположение в памяти) - на время действияSession
.
NonUniqueObjectReferenceException
выдается, если вы пытаетесь сохранить сущность, имеющую тот же первичный ключ, но другой объект reference
, как уже загруженную сущность для этого Session
.
Другими словами, Session
говорит вам: «У меня есть объект в памяти с тем же первичным ключом, что и объект, который вы пытаетесь сохранить, но объект reference
моей копии не соответствует вашей копии. "
Пример проблемы
- Открыть
Session
( # 1 ). - Загрузить объект ( ссылка на объект)= A , первичный ключ в базе данных = 1 ).
- Закрыть
Session
. - Открыть новый
Session
( # 2 ). - Повторно загрузить тот же объект (на этот раз ссылка на объект = B , первичный ключ в базе данных = 1).
- Измените свойство объекта A и сохраните его внутри
Session
# 2. NonUniqueObjectReferenceException
будет брошено.
Стоит отметить, что это исключение будет выдано, даже если объект A является просто частью более крупного графа объектов, который сохраняется в сеансе №2 (даже если объект A не изменился).
ЭтоТакже стоит отметить, что вы можете загрузить объект напрямую (Session.Load
, Session.Get
, Session.QueryOver
и т. д.) или косвенно (с помощью запроса, который не возвращает объект, но вызывает загрузку объекта в память).NonUniqueObjectReferenceException
может быть выброшено как для непосредственно, так и для непосредственно загруженного объекта.
Важное примечание: Эта проблема может быть вызвана другими способами, например, может быть вызвана с помощьюsingle Session
, если вы загружаете, а затем клонируете объект, а затем используете этот клон, чтобы сохранить некоторые изменения, используя Session
.Причина в том, что объект клона reference
будет отличаться от исходного объекта.
Объяснение решения
Для объекта Session
существует метод с именем Merge
:
object Session.Merge(object obj)
Merge
будет принимать сущность и, используя первичный ключ сущности, извлекать уже загруженную версию этой сущности из текущего Session
.Он также обновит свойства уже загруженного объекта Session
, если они отличаются от объекта, который вы только что передали.
Этот метод не изменяет объект, который вы передаете, но вместо этого возвращает другой объект, который вы должны использовать.
Последнее замечание по поводу Merge
состоит в том, что если в Session
, в котором вы находитесь, нет загруженной копии этого объекта в памяти, Merge
пойдет вперед и загрузит объект из базы данных перед выполнением его обычной функции объединения.
Пример решения
// using the example above, we are at the beginning of step 6 inside session #2
// we have 2 important objects = ISession sessionTwo, Option objectA.
// Option is an entity defined by you, it is not part of NH.
objectA.SomeProperty = "blah";
var optionFromSessionTwo = (Option) sessionTwo.Merge(objectA);
// this will not throw and it will persist the changes to objectA
sessionTwo.Flush();
Надеюсь, это поможет!