Skip to content

Commit 0672320

Browse files
authored
Fix issue that EntityUpdateAction increments version despite veto on update (#3199)
Fixes #3198
1 parent 53d1de7 commit 0672320

File tree

4 files changed

+251
-14
lines changed

4 files changed

+251
-14
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
//------------------------------------------------------------------------------
2+
// <auto-generated>
3+
// This code was generated by AsyncGenerator.
4+
//
5+
// Changes to this file may cause incorrect behavior and will be lost if
6+
// the code is regenerated.
7+
// </auto-generated>
8+
//------------------------------------------------------------------------------
9+
10+
11+
using System;
12+
using System.Threading;
13+
using System.Threading.Tasks;
14+
using NHibernate.Cfg;
15+
using NHibernate.Cfg.MappingSchema;
16+
using NHibernate.Event;
17+
using NHibernate.Mapping.ByCode;
18+
using NHibernate.Type;
19+
using NUnit.Framework;
20+
21+
namespace NHibernate.Test.NHSpecificTest.GH3198
22+
{
23+
[TestFixture]
24+
public partial class ByCodeFixtureAsync : TestCaseMappingByCode
25+
{
26+
private const int EXAMPLE_ID_VALUE = 1;
27+
28+
protected override void Configure(Configuration configuration)
29+
{
30+
// A listener always returning true
31+
configuration.EventListeners.PreUpdateEventListeners = new IPreUpdateEventListener[]
32+
{
33+
new TestEventListener()
34+
};
35+
base.Configure(configuration);
36+
}
37+
38+
protected override HbmMapping GetMappings()
39+
{
40+
var mapper = new ModelMapper();
41+
mapper.Class<Entity>(rc =>
42+
{
43+
rc.Table("Entity");
44+
rc.Id(x => x.Id, m => m.Generator(Generators.Assigned));
45+
rc.Property(x => x.Name, x => x.Type<StringType>());
46+
rc.Version(x => x.Version, vm => { });
47+
});
48+
49+
return mapper.CompileMappingForAllExplicitlyAddedEntities();
50+
}
51+
52+
protected override void OnSetUp()
53+
{
54+
using (var session = OpenSession())
55+
using (var transaction = session.BeginTransaction())
56+
{
57+
var e1 = new Entity { Id = EXAMPLE_ID_VALUE, Name = "old_name" };
58+
session.Save(e1);
59+
transaction.Commit();
60+
}
61+
}
62+
63+
protected override void OnTearDown()
64+
{
65+
using (var session = OpenSession())
66+
using (var transaction = session.BeginTransaction())
67+
{
68+
session.CreateQuery("delete from System.Object").ExecuteUpdate();
69+
70+
transaction.Commit();
71+
}
72+
}
73+
74+
[Test]
75+
public async Task TestVersionNotChangedWhenPreUpdateEventVetodAsync()
76+
{
77+
using (var session = OpenSession())
78+
{
79+
var entity = await (session.LoadAsync<Entity>(EXAMPLE_ID_VALUE));
80+
81+
entity.Name = "new_name";
82+
await (session.UpdateAsync(entity));
83+
84+
var versionBeforeFlush = entity.Version;
85+
86+
await (session.FlushAsync());
87+
88+
var versionAfterflush = entity.Version;
89+
90+
Assert.That(versionAfterflush, Is.EqualTo(versionBeforeFlush), "The entity version must not change when update is vetoed");
91+
}
92+
}
93+
94+
// A listener always returning true
95+
public partial class TestEventListener : IPreUpdateEventListener
96+
{
97+
public bool Executed { get; set; }
98+
public bool FoundAny { get; set; }
99+
100+
public Task<bool> OnPreUpdateAsync(PreUpdateEvent @event, CancellationToken cancellationToken)
101+
{
102+
return Task.FromResult<bool>(true);
103+
}
104+
105+
public bool OnPreUpdate(PreUpdateEvent @event)
106+
{
107+
return true;
108+
}
109+
}
110+
111+
public partial class Entity
112+
{
113+
public virtual int Id { get; set; }
114+
public virtual string Name { get; set; }
115+
public virtual int Version { get; set; }
116+
}
117+
}
118+
public partial class ByCodeFixture : TestCaseMappingByCode
119+
{
120+
121+
public partial class TestEventListener : IPreUpdateEventListener
122+
{
123+
124+
public Task<bool> OnPreUpdateAsync(PreUpdateEvent @event, CancellationToken cancellationToken)
125+
{
126+
return Task.FromResult<bool>(true);
127+
}
128+
}
129+
}
130+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
using System;
2+
using System.Threading;
3+
using System.Threading.Tasks;
4+
using NHibernate.Cfg;
5+
using NHibernate.Cfg.MappingSchema;
6+
using NHibernate.Event;
7+
using NHibernate.Mapping.ByCode;
8+
using NHibernate.Type;
9+
using NUnit.Framework;
10+
11+
namespace NHibernate.Test.NHSpecificTest.GH3198
12+
{
13+
[TestFixture]
14+
public partial class ByCodeFixture : TestCaseMappingByCode
15+
{
16+
private const int EXAMPLE_ID_VALUE = 1;
17+
18+
protected override void Configure(Configuration configuration)
19+
{
20+
// A listener always returning true
21+
configuration.EventListeners.PreUpdateEventListeners = new IPreUpdateEventListener[]
22+
{
23+
new TestEventListener()
24+
};
25+
base.Configure(configuration);
26+
}
27+
28+
protected override HbmMapping GetMappings()
29+
{
30+
var mapper = new ModelMapper();
31+
mapper.Class<Entity>(rc =>
32+
{
33+
rc.Table("Entity");
34+
rc.Id(x => x.Id, m => m.Generator(Generators.Assigned));
35+
rc.Property(x => x.Name, x => x.Type<StringType>());
36+
rc.Version(x => x.Version, vm => { });
37+
});
38+
39+
return mapper.CompileMappingForAllExplicitlyAddedEntities();
40+
}
41+
42+
protected override void OnSetUp()
43+
{
44+
using (var session = OpenSession())
45+
using (var transaction = session.BeginTransaction())
46+
{
47+
var e1 = new Entity { Id = EXAMPLE_ID_VALUE, Name = "old_name" };
48+
session.Save(e1);
49+
transaction.Commit();
50+
}
51+
}
52+
53+
protected override void OnTearDown()
54+
{
55+
using (var session = OpenSession())
56+
using (var transaction = session.BeginTransaction())
57+
{
58+
session.CreateQuery("delete from System.Object").ExecuteUpdate();
59+
60+
transaction.Commit();
61+
}
62+
}
63+
64+
[Test]
65+
public void TestVersionNotChangedWhenPreUpdateEventVetod()
66+
{
67+
using (var session = OpenSession())
68+
{
69+
var entity = session.Load<Entity>(EXAMPLE_ID_VALUE);
70+
71+
entity.Name = "new_name";
72+
session.Update(entity);
73+
74+
var versionBeforeFlush = entity.Version;
75+
76+
session.Flush();
77+
78+
var versionAfterflush = entity.Version;
79+
80+
Assert.That(versionAfterflush, Is.EqualTo(versionBeforeFlush), "The entity version must not change when update is vetoed");
81+
}
82+
}
83+
84+
// A listener always returning true
85+
public partial class TestEventListener : IPreUpdateEventListener
86+
{
87+
public bool Executed { get; set; }
88+
public bool FoundAny { get; set; }
89+
90+
public bool OnPreUpdate(PreUpdateEvent @event)
91+
{
92+
return true;
93+
}
94+
}
95+
96+
public partial class Entity
97+
{
98+
public virtual int Id { get; set; }
99+
public virtual string Name { get; set; }
100+
public virtual int Version { get; set; }
101+
}
102+
}
103+
}

src/NHibernate/Action/EntityUpdateAction.cs

+9-7
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,11 @@ public override void Execute()
5454
{
5555
stopwatch = Stopwatch.StartNew();
5656
}
57-
58-
bool veto = PreUpdate();
57+
58+
if (PreUpdate())
59+
{
60+
return;
61+
}
5962

6063
ISessionFactoryImplementor factory = Session.Factory;
6164

@@ -74,10 +77,9 @@ public override void Execute()
7477
slock = persister.Cache.Lock(ck, previousVersion);
7578
}
7679

77-
if (!veto)
78-
{
79-
persister.Update(id, state, dirtyFields, hasDirtyCollection, previousState, previousVersion, instance, null, session);
80-
}
80+
81+
persister.Update(id, state, dirtyFields, hasDirtyCollection, previousState, previousVersion, instance, null, session);
82+
8183

8284
EntityEntry entry = Session.PersistenceContext.GetEntry(instance);
8385
if (entry == null)
@@ -128,7 +130,7 @@ public override void Execute()
128130

129131
PostUpdate();
130132

131-
if (statsEnabled && !veto)
133+
if (statsEnabled)
132134
{
133135
stopwatch.Stop();
134136
factory.StatisticsImplementor.UpdateEntity(Persister.EntityName, stopwatch.Elapsed);

src/NHibernate/Async/Action/EntityUpdateAction.cs

+9-7
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,11 @@ public override async Task ExecuteAsync(CancellationToken cancellationToken)
3939
{
4040
stopwatch = Stopwatch.StartNew();
4141
}
42-
43-
bool veto = await (PreUpdateAsync(cancellationToken)).ConfigureAwait(false);
42+
43+
if (await (PreUpdateAsync(cancellationToken)).ConfigureAwait(false))
44+
{
45+
return;
46+
}
4447

4548
ISessionFactoryImplementor factory = Session.Factory;
4649

@@ -59,10 +62,9 @@ public override async Task ExecuteAsync(CancellationToken cancellationToken)
5962
slock = await (persister.Cache.LockAsync(ck, previousVersion, cancellationToken)).ConfigureAwait(false);
6063
}
6164

62-
if (!veto)
63-
{
64-
await (persister.UpdateAsync(id, state, dirtyFields, hasDirtyCollection, previousState, previousVersion, instance, null, session, cancellationToken)).ConfigureAwait(false);
65-
}
65+
66+
await (persister.UpdateAsync(id, state, dirtyFields, hasDirtyCollection, previousState, previousVersion, instance, null, session, cancellationToken)).ConfigureAwait(false);
67+
6668

6769
EntityEntry entry = Session.PersistenceContext.GetEntry(instance);
6870
if (entry == null)
@@ -113,7 +115,7 @@ public override async Task ExecuteAsync(CancellationToken cancellationToken)
113115

114116
await (PostUpdateAsync(cancellationToken)).ConfigureAwait(false);
115117

116-
if (statsEnabled && !veto)
118+
if (statsEnabled)
117119
{
118120
stopwatch.Stop();
119121
factory.StatisticsImplementor.UpdateEntity(Persister.EntityName, stopwatch.Elapsed);

0 commit comments

Comments
 (0)