NHibernate:在EventListener"PreUpdateEvent"期间更新集合

Pau*_*eld 8 nhibernate event-listener

我正在尝试为Nhibernate编写一个挂钩到PreUpdate事件的审计跟踪.我有一个AuditLogEntry类(when,who等),它包含一个AuditLogEntryDetails列表(即更改的各个属性).如果我将AuditLogEntry类与正在审计的实体隔离,那么我的代码运行时没有错误.但是,如果我将AuditLogEntry列表添加到正在审核的实体中,那么我的代码会抛出一个

flush()未处理集合[DomainObjects.AuditTracking.AuditLogEntry.Details]

当我尝试将修改后的列表保存在事件侦听器中时,断言失败.只有当审计项目在列表中已有一个(或多个)AuditLogEntry实例时,才会发生这种情况.如果没有条目,则创建新列表并将其添加到正在审计的实体中,这很好.

我认为通过将问题隔离到上面,它似乎是(懒惰)加载现有列表以添加AuditLogEntry的新实例.但是我无法继续前进.将"Lazy ="False"'添加到列表映射似乎没有帮助.我真的在使用NHibernate的早期,从HN 3.0 Cookbook和这篇博文中借用了概念.我的代码与此非常相似,但尝试将审核历史记录添加到列表中正在审核的项目中(因此我认为我还需要在pre,而不是post update事件中执行此操作).

有问题的实体接口/类的快照是:

public class AuditLogEntry : Entity
{
    public virtual AuditEntryTypeEnum AuditEntryType { get; set; }
    public virtual string EntityFullName { get; set; }
    public virtual string EntityShortName { get; set; }
    public virtual string Username { get; set; }
    public virtual DateTime When { get; set; }
    public virtual IList<AuditLogEntryDetail> Details { get; set; }
}

public interface IAuditTrackedEntity
{
    Guid Id { get; }
    IList<AuditLogEntry> ChangeHistory { get; set; }
}

public class AuditTrackedEntity : StampedEntity, IAuditTrackedEntity
{
    public virtual IList<AuditLogEntry> ChangeHistory { get; set; }
} 

public class LookupValue : AuditTrackedEntity
{
    public virtual string Description { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

对于我的映射:

AuditTrackedEntry.hbm.xml:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="DomainObjects" namespace="DomainObjects.AuditTracking">
  <class name="AuditLogEntry">
    <id name="Id">
      <generator class="guid.comb" />
    </id>
    <version name="Version" />
    <property name="AuditEntryType"/>
    <property name="EntityFullName"/>
    <property name="EntityShortName"/>
    <property name="Username"/>
    <property name="When" column="`When`"/>
    <list name ="Details" cascade="all">
      <key column="AuditLogEntryId"/>
      <list-index column="DetailsIndex" base="1"/>
      <one-to-many class="AuditLogEntryDetail"/>
    </list>
  </class>
</hibernate-mapping>
Run Code Online (Sandbox Code Playgroud)

lookupvalue.hbm.xml:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="DomainObjects" namespace="DomainObjects">
  <class name="LookupValue">
    <id name="Id">
      <generator class="guid.comb" />
    </id>
    <discriminator type="string">
      <column name="LookupValueType" unique-key="UQ_TypeName" not-null="true" />
    </discriminator>
    <version name="Version" />
    <property name="Description" unique-key="UQ_TypeName" not-null="true" />
    <property name="CreatedBy" />
    <property name="WhenCreated" />
    <property name="ChangedBy" />
    <property name="WhenChanged" />
    <list name ="ChangeHistory">
      <key column="EntityId"/>
      <list-index column="ChangeIndex" base="1"/>
      <one-to-many class="DomainObjects.AuditTracking.AuditLogEntry"/>
    </list>
  </class>
</hibernate-mapping>
Run Code Online (Sandbox Code Playgroud)

EventListener PreUpdate事件处理程序调用以下代码: 导致问题的行在代码块的末尾附近进行注释

    public void TrackPreUpdate(IAuditTrackedEntity entity, object[] oldState, object[] state, IEntityPersister persister, IEventSource eventSource)
    {
        if (entity == null || entity is AuditLogEntry)
            return;

        var entityFullName = entity.GetType().FullName;
        if (oldState == null)
        {
            throw new ArgumentNullException("No old state available for entity type '" + entityFullName +
                                            "'. Make sure you're loading it into Session before modifying and saving it.");
        }

        var dirtyFieldIndexes = persister.FindDirty(state, oldState, entity, eventSource);
        var session = eventSource.GetSession(EntityMode.Poco);

        AuditLogEntry auditLogEntry = null;
        foreach (var dirtyFieldIndex in dirtyFieldIndexes)
        {
            if (IsIngoredProperty(persister, dirtyFieldIndex))
                continue;

            var oldValue = GetStringValueFromStateArray(oldState, dirtyFieldIndex);
            var newValue = GetStringValueFromStateArray(state, dirtyFieldIndex);

            if (oldValue == newValue)
            {
                continue;
            }
            if (auditLogEntry == null)
            {
                auditLogEntry = new AuditLogEntry
                                    {
                                        AuditEntryType = AuditEntryTypeEnum.Update,
                                        EntityShortName = entity.GetType().Name,
                                        EntityFullName = entityFullName,
                                        Username = Environment.UserName,
                                        //EntityId = entity.Id,
                                        When = DateTime.Now,
                                        Details =  new List<AuditLogEntryDetail>()
                                    };


                //**********************
                // The next three lines cause a problem when included,
                // collection [] was not processed by flush()
                //**********************
                if (entity.ChangeHistory == null)
                    entity.ChangeHistory = new List<AuditLogEntry>();
                entity.ChangeHistory.Add(auditLogEntry);

                session.Save(auditLogEntry);    
            }

            var detail = new AuditLogEntryDetail  
                             {
                                 //AuditLogEntryId = auditLogEntry.Id,
                                 PropertyName = persister.PropertyNames[dirtyFieldIndex],
                                 OldValue = oldValue,
                                 NewValue = newValue
                             };
            session.Save(detail);
            auditLogEntry.Details.Add(detail);

        }

        session.Flush();
    }
Run Code Online (Sandbox Code Playgroud)

如前所述,在这种配置中,我得到一个断言失败" collection []没有被flush()处理 ".如果我删除上面的三行和lookupcode.hmb.xml中的列表映射,那么一切都按预期工作,除了被审计的实体不再包含对它自己的审计项的引用.

zby*_*our 5

我们面临着非常相似的问题,完全一样的例外,但是情况不同。尚未找到解决方案...

我们有NH事件监听器实现IPreUpdateEventListenerOnPreUpdate用于审计日志的方法。一切都可以进行简单的属性更新,脏检查效果很好,但是惰性集合存在一些问题。当更新某个具有惰性集合的对象并在事件侦听器OnPreUpdate方法中访问任何对象字段时,将引发与上述相同的异常。当lazy设置为false,问题就消失了。

因此,延迟集合似乎存在一些问题(并且在保存之前没有初始化集合的影响)。我们的问题与创建新的收藏品无关。仅读取现有对象,仅从事件侦听器访问其字段会导致此问题。

因此,在您的情况下,也许lazy仅将关联设置为false可以解决此问题,但另一方面,您可能真的希望集合变得懒惰。很难说,如果问题已经解决,或者IInterceptor必须使用它。