NHibernate - дублировать результаты при использовании кэша коллекции
Я получаю очень странное поведение от NHibernate, когда использую кэш второго уровня с несколькими уровнями двухсторонних отношений родитель-ребенок (-grandchild) один-ко-многим:
int id;
using(var session = sessionFactory.OpenSession())
{
var parent = new Parent();
var child1 = parent.AddChild();
child1.AddGrandChild();
var child2 = parent.AddChild();
child2.AddGrandChild();
session.Save(parent);
session.Flush(); // force id generation
id = parent.Id;
}
using(var session = sessionFactory.OpenSession())
{
var parent = session.Get<Parent>(id);
parent.Children.Should().HaveCount(2); // but is actually 3; second child duplicated
}
Второй дочерний элемент, имеющий двух внуков, дублируется в коллекции, но с тем же идентификатором объекта (ReferenceEquals()
является true
). Когда я отключаю кеширование для Parent.Children
или установите стратегию извлечения в Join
проблема уходит. Есть идеи как узнать что здесь происходит?
Объекты:
class Parent
{
public virtual int Id { get; protected set; }
public virtual List<Child> children = new List<Child>();
public virtual IEnumerable<Child> Children
{
get { return children.ToList(); }
}
public virtual Child AddChild()
{
var child = new Child(this);
this.children.Add(child);
return child;
}
}
class Child
{
public Child(Parent parent)
{
this.Parent = parent;
}
public virtual int Id { get; protected set; }
public virtual List<GrandChild> grandChildren = new List<GrandChild>();
public virtual IEnumerable<GrandChild> GrandChildren
{
get { return grandChildren.ToList(); }
}
public virtual Parent Parent { get; protected set; }
public virtual GrandChild AddGrandChild()
{
var grandChild = new GrandChild(this);
this.grandChildren.Add(grandChild);
return grandChild;
}
}
class GrandChild
{
public virtual int Id { get; protected set; }
public virtual Child Parent { get; protected set; }
public GrandChild(Child parent)
{
this.Parent = parent;
}
}
Отображения:
public class ParentMap : ClassMap<Parent>
{
public ParentMap()
{
Id(p => p.Id).GeneratedBy.Identity();
HasMany(p => p.Children)
.Access.CamelCaseField()
.Inverse()
.Cascade.AllDeleteOrphan()
.Cache.ReadWrite();
Cache.ReadWrite();
}
}
public class ChildMap : ClassMap<Child>
{
public ChildMap()
{
Id(c => c.Id).GeneratedBy.Identity();
HasMany(c => c.GrandChildren)
.Access.CamelCaseField()
.Inverse()
.Cascade.AllDeleteOrphan()
.Cache.ReadWrite();
References(c => c.Parent);
Cache.ReadWrite();
}
}
public class GrandChildMap : ClassMap<GrandChild>
{
public GrandChildMap()
{
Id(c => c.Id).GeneratedBy.Identity();
References(c => c.Parent);
Cache.ReadWrite();
}
}
[РЕДАКТИРОВАТЬ]
Я обошел эту проблему, не включив кэш для дочерних коллекций. Так как я сделал это в первую очередь, чтобы избежать N+1, выберите проблемы с fetch join
запросы на дочерние элементы кэшированных объектов, я сделал следующее:
public ChildMap()
{
// ...
HasMany(c => c.GrandChildren) // removed Cache.ReadWrite() here
/* ... */;
// added IncludeAll() to make sure lazily fetched collections get cached
Cache.ReadWrite().IncludeAll();
}
Я до сих пор не понимаю, что здесь происходит, поэтому любое понимание этого вопроса будет приветствоваться:-).
1 ответ
Пытались ли вы использовать транзакции вместо промывки? подобно
using(var session = sessionFactory.OpenSession())
using(var transaction = session.BeginTransaction())
{
var parent = new Parent();
[...]
parent.Save();
transaction.Commit();
id = parent.Id;
}
Может быть, это помогает. Однажды у меня была похожая проблема, которую я мог решить с помощью транзакций.