gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-04 07:44:54 25 4
gpt4 key购买 nike

我正在尝试为 Nhibernate 编写一个与 PreUpdate 事件 Hook 的审计跟踪。我有一个 AuditLogEntry 类(何时、谁等),其中包含一个 AuditLogEntryDetails 列表(即更改的各个属性)。如果我将 AuditLogEntry 类与被审计的实体隔离开,那么我的代码运行时不会出现错误。但是,如果我将 AuditLogEntry 列表添加到被审计的实体,那么我的代码会抛出一个

集合 [DomainObjects.AuditTracking.AuditLogEntry.Details] 未被处理
冲洗()


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

我认为通过将问题隔离到上述问题,它似乎是(懒惰地)加载现有列表以添加 AuditLogEntry 的新实例。但是,我已经无法进一步取得进展。将 'Lazy="False"' 添加到列表映射似乎没有帮助。我真的处于使用 NHibernate 的早期阶段,从 HN 3.0 Cookbook 和这个 blog post 中借用了一些概念。 .我的代码与此非常相似,但尝试将审核历史记录添加到列表中正在审核的项目中(因此我认为我也需要在更新前而不是更新后事件中这样做)。

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

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; }
}

对于我拥有的映射:

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>

查找值.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>

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();
}

如前所述,在此配置中,我收到断言失败“ 集合 [] 未由 flush() 处理”。如果我删除上面的三行和 lookupcode.hmb.xml 中的列表映射,那么一切都按预期工作,除了被审计的实体不再包含对其自己的审计项目的引用。

最佳答案

我们面临着非常相似的问题,完全相同的异常,但在不同的情况下。还没找到解决办法...

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

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

所以在你的情况下,也许,lazy仅将关联设置为 false 可以解决问题,但另一方面,您可能真的想让集合变得懒惰。很难说,如果问题有解决方案或IInterceptor必须改为使用。

关于NHibernate:在 EventListener "PreUpdateEvent"期间更新集合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4346094/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com