Skip to content

Commit

Permalink
Fix for NullReferenceException in lazy bidirectional many-to-one prop…
Browse files Browse the repository at this point in the history
…erties
  • Loading branch information
David Uvenman committed Jun 24, 2024
1 parent 7729a58 commit f0747a3
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using NHibernate.Envers.Configuration.Attributes;

namespace NHibernate.Envers.Tests.NetSpecific.Integration.ManyToOne.LazyProperty.Bidirectional
{
[Audited]
public class Car
{
public virtual long Id { get; set; }
public virtual int Number { get; set; }
public virtual Person Owner { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System.Linq;
using NUnit.Framework;
using SharpTestsEx;

namespace NHibernate.Envers.Tests.NetSpecific.Integration.ManyToOne.LazyProperty.Bidirectional
{
public partial class LazyPropertyBidirectionalTest : TestBase
{
private long id_pers1;

public LazyPropertyBidirectionalTest(AuditStrategyForTest strategyType) : base(strategyType)
{
}

protected override void Initialize()
{
var pers1 = new Person {Name = "Hernan"};

using (var tx = Session.BeginTransaction())
{
id_pers1 = (long) Session.Save(pers1);
tx.Commit();
}
}

[Test]
public void SavePersonProxyForFieldInterceptor()
{
long carId;
using (var tx = Session.BeginTransaction())
{
var pers = Session.Query<Person>().Single(x => x.Id == id_pers1);
var car = new Car
{
Owner = pers
};
pers.Cars.Add(car);
carId = (long) Session.Save(car);
tx.Commit();
}

AuditReader().Find<Car>(carId, 2).Owner.Name
.Should().Be.EqualTo("Hernan");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="NHibernate.Envers.Tests"
namespace="NHibernate.Envers.Tests.NetSpecific.Integration.ManyToOne.LazyProperty.Bidirectional">
<class name="Person" table="DRIVERS">
<id name="Id" column="ID_PERSON" type="long">
<generator class="native"/>
</id>
<property name="Name" type="string" length="255"
column="NAME" not-null="true" lazy="true"/>

<set name="Cars" inverse="true" cascade="none">
<key column="person_id" />
<one-to-many class="Car" />
</set>
</class>

<class name="Car">
<id name="Id" column="ID_CAR" type="long">
<generator class="native"/>
</id>
<property name="Number" type="int" not-null="true" column="numb"/>
<many-to-one name="Owner" class="Person" column="person_id"/>
</class>
</hibernate-mapping>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using NHibernate.Envers.Configuration.Attributes;
using System.Collections.Generic;

namespace NHibernate.Envers.Tests.NetSpecific.Integration.ManyToOne.LazyProperty.Bidirectional
{
[Audited]
public class Person
{
public virtual long Id { get; set; }
public virtual string Name { get; set; }
public virtual ISet<Car> Cars { get; set; }
}
}
17 changes: 7 additions & 10 deletions Src/NHibernate.Envers/Event/AuditEventListener.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,20 +81,17 @@ private void addCollectionChangeWorkUnit(AuditProcess auditProcess, ISessionImpl
{
// relDesc.getToEntityName() doesn't always return the entity name of the value - in case
// of subclasses, this will be root class, no the actual class. So it can't be used here.
string toEntityName;
object id;

var toEntityName = session.BestGuessEntityName(value);
if (value is INHibernateProxy newValueAsProxy)
{
toEntityName = session.BestGuessEntityName(value);
id = newValueAsProxy.HibernateLazyInitializer.Identifier;
// We've got to initialize the object from the proxy to later read its state.
value = Toolz.GetTargetFromProxy(session, newValueAsProxy);
}
else
{
toEntityName = session.GuessEntityName(value);

var idMapper = VerCfg.EntCfg[toEntityName].IdMapper;
id = idMapper.MapToIdFromEntity(value);
}
Expand Down Expand Up @@ -223,10 +220,10 @@ private void generateBidirectionalCollectionChangeWorkUnits(AuditProcess auditPr
var toPropertyName = toPropertyNames.First();

auditProcess.AddWorkUnit(new CollectionChangeWorkUnit(evt.Session,
evt.Session.BestGuessEntityName(relatedObj),
evt.Session.BestGuessEntityName(relatedObj),
toPropertyName,
VerCfg,
relatedId,
VerCfg,
relatedId,
relatedObj));
}
}
Expand Down Expand Up @@ -299,7 +296,7 @@ private void onCollectionAction(AbstractCollectionEvent evt,
}
else
{
var workUnit = new PersistentCollectionChangeWorkUnit(evt.Session, entityName, VerCfg, newColl,
var workUnit = new PersistentCollectionChangeWorkUnit(evt.Session, entityName, VerCfg, newColl,
collectionEntry, oldColl, evt.AffectedOwnerIdOrNull,
referencingPropertyName);
verSync.AddWorkUnit(workUnit);
Expand Down Expand Up @@ -400,7 +397,7 @@ public virtual void Initialize(Cfg.Configuration cfg)

private static void checkIfTransactionInProgress(ISessionImplementor session)
{
if (!session.TransactionInProgress && session.TransactionContext==null)
if (!session.TransactionInProgress && session.TransactionContext == null)
{
// Historical data would not be flushed to audit tables if outside of active transaction
// (AuditProcess#doBeforeTransactionCompletion(SessionImplementor) not executed).
Expand All @@ -412,7 +409,7 @@ private bool shouldGenerateRevision(AbstractCollectionEvent evt)
{
var entityName = evt.GetAffectedOwnerEntityName();
return VerCfg.GlobalCfg.GenerateRevisionsForCollections
&& VerCfg.EntCfg.IsVersioned(entityName);
&& VerCfg.EntCfg.IsVersioned(entityName);
}

private static object initializeCollection(AbstractCollectionEvent evt)
Expand Down

0 comments on commit f0747a3

Please sign in to comment.