PreUpdateイベントにフックするNhibernateの監査追跡を作成しようとしています。 AuditLogEntryDetails(変更された個々のプロパティ)のリストを含むAuditLogEntryクラス(when、whoなど)があります。監査対象のエンティティからAuditLogEntryクラスを分離すると、エラーなしでコードが実行されます。私はエンティティにAuditLogEntryのリストを追加した場合しかし、その後、監査されている私のコードはNHibernate:EventListener "PreUpdateEvent"中にコレクションを更新する
コレクションは[DomainObjects.AuditTracking.AuditLogEntry.Details] フラッシュ()
アサーションの失敗によって処理されなかったスロー私は変更されたリストをイベントリスナーの中に保存しようとします。これは、監査対象のアイテムにすでにリスト内に1つ(またはそれ以上)のAuditLogEntryインスタンスがある場合にのみ発生します。エントリがない場合、新しいリストが作成され、監査対象のエンティティに追加されます。これは問題ありません。
上記の問題を特定することで、既存のリストを読み込んでAuditLogEntryの新しいインスタンスを追加する(遅延している)ように見えます。しかし私はこれ以上進歩することができませんでした。リストのマッピングに 'Lazy = "False"を追加することは役に立ちません。私はHibernate Cookbookとblog postの両方から概念を借りて、NHibernateを使用していた初期の段階にあります。私のコードはこれに非常に似ていますが、監査履歴をリストで監査されている項目に追加しようとします(更新イベントをポストするのではなく、事前にそれを行う必要があると思います)。
問題の実体インタフェース/クラスのスナップショットは、次のとおりです。私が持っているマッピングのために
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>
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>
はのEventListener更新前イベントハンドラは以下のコードを呼び出します。問題が発生 ラインは、コードブロック先に述べたように
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()によって処理されませんでした。上記の3行を削除し、lookupcode.hmb.xmlのリストマッピングをすべて削除すると、監査対象のエンティティに自身の監査対象項目への参照が含まれていることを除いて、すべてが正常に機能します。
うまくいけば、私は明日のコードを再検討し、あなたのソリューションを試すチャンスがあるでしょうアップデートのおかげで、。私はあなたに私が見つけたものを知らせます。 –
あなたの助けていただきありがとうございますが、私はまだ問題があると思っています。異なる構成では、異なるエラーを得ることができますが、すべて同じエラーになります。私はそれがコレクションと思われることに同意し、コードがエンティティへのコレクションの追加を反映するために新しい状態を更新していなかったためだと思っていましたが、それを追加すると古い状態または更新中のエラー2つのセッションでオブジェクト。 –