NHibernate在父级之前保存集合元素

时间:2014-09-03 10:50:41

标签: c# sql nhibernate

根据NHibernate文档,SQL语句按以下顺序发布:

  • 所有实体插入,使用ISession.Save()
  • 以相同的顺序保存相应的对象
  • 所有实体更新
  • 所有集合删除
  • 所有集合元素删除,更新和插入
  • 所有收集插入
  • 所有实体删除,按相同顺序使用ISession.Delete()删除相应的对象

在我们的案例中,似乎由于某种原因,集合插入发生在实体更新之前。

代码

我们有两个实体,客户端配置文件,其中客户端拥有并独自负责其配置文件<的子集合/ em>的。个人资料存储有关客户对各种产品的偏好的信息,例如评论和分数。 (简化)类如下所示:

namespace MyProject.Models
{
  public class Client
  {
    public Guid Id { get; set; }
    public string Name { get; set; }
    public IDictionary<Guid, Profile> Profiles { get; set; }
  }

  public class Profile
  {
    public Guid ClientId { get; set; }
    public Guid ProfileId { get; set; }
    public int Score { get; set; }
    public string Comment { get; set; }
  }
}

这被映射为:

namespace MyProject.Mappings
{
  public class ClientMap : ClassMapping<Client>
  {
    public ClientMap()
    {
      Id(c => c.Id, mapping => mapping.Generator(Generators.Assigned));
      Property(c => c.Name);
      Version(c => c.Version, mapping => mapping.Generated(VersionGeneration.Never));
      Map(c => 
        c.Profiles,
        collectionMapping => collectionMapping.Cascade(Cascade.All | Cascade.DeleteOrphans),
        keyMapping => keyMapping.Element(e => e.Column("ProductId")),
        valueMapping => valueMapping.OneToMany()
      );
    }
  }

  public class ProfileMap : ClassMapping<Profile>
  {
    public ProfileMap()
    {
      ComposedId(s =>
      {
        s.Property(s1 => s1.ClientId);
        s.Property(s1 => s1.ProductId);
      });
      Property(p => p.Score);
      Property(p => p.Comment);
    }
  }
}

产品和配置文件之间没有直接关系。除了上述内容之外,我们还插入了实现FindDirty的NHibernate会话拦截器架构,并确保在其任何子节点发生变化时发现父版本是脏的。我们还通过在每个事务上设置IsolationLevel.ReadCommitted来使用乐观并发。

日志

ExpressProfiler 中,我们看到以下事件序列(为便于阅读而简化)

SELECT Id, Version 
  FROM Clients 
  WHERE Id='3F08F098-09C8-CE42-9594-39C9EAA21B64'
SELECT ClientId, ProductId, Score, Comment 
  FROM ClientProfiles 
  WHERE ClientId='3F08F098-09C8-CE42-9594-39C9EAA21B64'
INSERT INTO ClientProfiles (Comment, Score, ClientId, ProductId) 
  VALUES ('some comment', 123, '3F08F098-09C8-CE42-9594-39C9EAA21B64', '487BC856-6EAF-42E2-ADD6-B3DE056EA714')
UPDATE Clients SET Version = 46 WHERE Id = '3F08F098-09C8-CE42-9594-39C9EAA21B64' AND Version = 45

为什么?

我们感到困惑的是INSERTUPDATE之前发生的原因。在高度并发的生产系统中,这会导致重复的主键错误而不是StaleObjectStateException。我们更喜欢后者,因为它更容易确保我们重新安排失败的操作只是因为其他人同时访问了同一条记录。

我们做错了什么?我们是否应该将 Profile 映射为组件而不是类?或者是否有一些我们错过的NHibernate指令?

0 个答案:

没有答案