Это длинный выстрел, который кто-нибудь имел бы дело с этим, но здесь идет.
Во-первых, у меня отключена автоматическая проверка NHibernate. Я сделал это потому, что не хочу, чтобы NHibernate сохранял каждый измененный объект, о котором он знает, когда я фиксирую транзакцию (FlushMode = Commit), потому что иногда он становится слишком усердным и пытается сохранить множество огромных графов объектов только потому, что я изменил что-то в объекте. Я следовал инструкциям, найденным здесь: http://fabiomaulo.blogspot.com/2009/03/ensuring-updates-on-flush.html
Во-вторых, у меня есть ситуация, когда мне нужно загружать объекты, используя пользовательский SQL, поэтому у меня есть динамический SQL, который загружает этот один граф объектов, используя ISession.CreateSQLQuery (). После этого я вызываю ISession.Lock () для загруженных объектов. Я действительно не знаю, что это делает, но если я этого не сделаю, изменения в загруженных пользовательских объектах никогда не будут сохранены.
Проблема теперь в том, что если я добавляю или удаляю объекты из коллекций внутри объектов, которые я загрузил с помощью своего пользовательского запроса SQL, NHibernate не сохраняет их. Если включена автоматическая проверка (по умолчанию), они сохраняются, поскольку она просто сохраняет весь граф объекта, поскольку все отношения помечены как cascade = "save-update". Но, поскольку я отключил авто-грязную проверку, она не проходит через все отношения.
Кажется, что так как я вызвал ISession.Lock (), NHibernate знает обо всех моих объектах, но что-то не так с коллекциями в этих объектах.
Если у кого-нибудь есть идеи, они будут оценены.
РЕДАКТИРОВАТЬ: Подробнее о том, что я делаю. У меня есть этот метод, где делает запрос. Как видно из сложности запроса, HQL - это не вариант (я всегда предпочитаю HQL или ISession.CreateCriteria (), а не CreateSQLQuery ()).
public IList<MaterialGroup> GetResults(IList<long> takeOffItemIds)
{
if (takeOffItemIds == null || takeOffItemIds.Count == 0)
return new List<MaterialGroup>();
var query =
@"with MaterialGroupsByTakeOffItem (MaterialGroupId, TakeOffItemId) as
(
select MaterialGroupId, ParentTakeOffItemId
from MaterialGroups mg
inner join TakeOffItems toi on toi.TakeOffItemId = mg.ParentTakeOffItemId
where ParentTakeOffItemId is not null
union all
select grp.MaterialGroupId, parent.TakeOffItemId
from MaterialGroups grp
inner join MaterialGroupsByTakeOffItem parent on parent.MaterialGroupId = grp.ParentMaterialGroupId
)
select {mg.*}, {md.*}, {mi.*}, {mc.*} from MaterialGroupsByTakeOffItem mgbtoi
inner join MaterialGroups {mg} on {mg}.MaterialGroupId = mgbtoi.MaterialGroupId
left outer join MaterialDetails {md} on {md}.MaterialDetailsId = {mg}.MaterialGroupId
left outer join MaterialItems {mi} on {mi}.MaterialItemId = {md}.PartId
left outer join MaterialCodes {mc} on {mc}.MaterialCodeId = {mi}.CodeId
";
if (takeOffItemIds.Count == 1)
query += "where mgbtoi.TakeOffItemId = " + takeOffItemIds[0];
else
query += "where mgbtoi.TakeOffItemId in (" + takeOffItemIds.ToCommaDelimitedString() + ")";
return _session.CreateSQLQuery(query)
.AddEntity("mg", typeof(MaterialGroup))
.AddJoin("md", "mg.MaterialDetails")
.AddJoin("mi", "md.Part")
.AddJoin("mc", "mi.Code")
.List<MaterialGroup>();
}
Этот метод возвращает список объектов MaterialGroup, которые будут принадлежать другому объекту под названием TakeOffItem. Я беру список объектов MaterialGroup и помещаю их в TakeOffItem.MaterialGroups.
public override TakeOffItem Get(long id)
{
var materialGroups = _getMaterialGroupsForTakeOffItemQuery.GetResults(id);
var fabricationNotesByMaterialDetailsId = _getFabricationNotesForTakeOffItemQuery.GetFabricationNotesForMaterialDetails(
materialGroups.Where(mg => mg.MaterialDetails != null).Select(mg => mg.MaterialDetails.Id));
var takeOffItem = base.Get(id);
_assignMaterialGroupsService.AssignMaterialGroups(id, takeOffItem, materialGroups, fabricationNotesByMaterialDetailsId);
_session.Lock(takeOffItem, LockMode.None);
return takeOffItem;
}
public void AssignMaterialGroups(long takeOffItemId, IHasMaterialGroups parent, IList<MaterialGroup> allMaterialGroups,
Dictionary<long, IList<FabricationNote>> fabricationNotesByMaterialDetailsId)
{
if (parent is TakeOffItem)
parent.MaterialGroups = allMaterialGroups.Where(mg => mg.ParentTakeOffItem != null && mg.ParentTakeOffItem.Id == takeOffItemId).ToList();
else if (parent is MaterialGroup)
{
var group = (MaterialGroup) parent;
parent.MaterialGroups = allMaterialGroups.Where(mg => mg.ParentMaterialGroup != null && mg.ParentMaterialGroup.Id == parent.Id).ToList();
if (group.MaterialDetails != null)
{
if (fabricationNotesByMaterialDetailsId.ContainsKey(group.MaterialDetails.Id))
group.MaterialDetails.FabricationNotes = fabricationNotesByMaterialDetailsId[group.MaterialDetails.Id];
else
group.MaterialDetails.FabricationNotes = new List<FabricationNote>();
}
}
else
throw new NotSupportedException();
foreach (var group in parent.MaterialGroups)
{
_session.Lock(group, LockMode.None);
if (group.MaterialDetails != null)
_session.Lock(group.MaterialDetails, LockMode.None);
AssignMaterialGroups(takeOffItemId, group, allMaterialGroups, fabricationNotesByMaterialDetailsId);
}
}
Есть несколько проблем с этим. Во-первых, поскольку я вручную заполняю коллекцию в сущности TakeOffItem, NHibernate считает, что TakeOffItem теперь грязный. У меня есть cascade = "save-update" в TakeOffItem.MaterialGroups (и в нем есть больше каскадных отношений), и, поскольку TakeOffItem считается грязным, он сохранит весь граф объекта TakeOffItem при сохранении. Это нормально, если я действительно хочу сохранить TakeOffItem, но если я не хочу сохранять TakeOffItem, он завершает выполнение большого количества запросов, которые в принципе не нужны.
Чтобы обойти некоторые из этих проблем, я внедрил код Фабио, который отключит функцию автоматической проверки на грязные состояния в NHibernate. Теперь он будет сохранять только те вещи, которые я вызываю с помощью SaveOrUpdate () (вместе с каскадными отношениями), поэтому больше не имеет значения, что NHibernate считает, что все эти другие объекты являются грязными, потому что он не сохранит их при очистке сессия. Но теперь что-то еще не работает, потому что если я изменю коллекции в объекте MaterialGroup (например, TakeOffItem.MaterialGroups [0] .MaterialGroups.Add (что-то)), NHibernate не осознает, что ему нужно сохранить эти объекты. Если я удаляю весь свой код загрузки, код Фабио работает нормально. Но для оптимизации мне нужен пользовательский код загрузки.
Я думаю, что часть проблемы также связана с тем, что я не могу сказать NHibernate, что сущность НЕ грязная (если есть способ, я хотел бы знать!). Я действительно хотел бы иметь возможность выполнить свою пользовательскую загрузку, а затем сказать NHibernate: «Эй, весь этот граф объектов не грязный, притворись, будто ты только что загрузил его».
Еще раз спасибо за любую помощь.