Skip to content

Commit 036ef2c

Browse files
committed
(Perf) Hooking: prevent repetitive hooking of the same entity/state/[pre|post] combination within a single request
1 parent c0e5612 commit 036ef2c

1 file changed

Lines changed: 42 additions & 4 deletions

File tree

src/Libraries/SmartStore.Core/Data/Hooks/DefaultHookHandler.cs

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ public class DefaultHookHandler : IHookHandler
1515
private readonly Multimap<Type, IPreActionHook> _preHooksRequestCache = new Multimap<Type, IPreActionHook>();
1616
private readonly Multimap<Type, IPostActionHook> _postHooksRequestCache = new Multimap<Type, IPostActionHook>();
1717

18+
// Prevents repetitive hooking of the same entity/state/[pre|post] combination within a single request
19+
private readonly HashSet<HookedEntityKey> _hookedEntities = new HashSet<HookedEntityKey>();
20+
1821
private static HashSet<Type> _importantPreHookTypes;
1922
private static HashSet<Type> _importantPostHookTypes;
2023
private readonly static object _lock = new object();
@@ -79,7 +82,13 @@ public bool TriggerPreActionHooks(IEnumerable<HookedEntityEntry> entries, bool r
7982
foreach (var entry in entries)
8083
{
8184
var e = entry; // Prevents access to modified closure
82-
var preHooks = GetPreHookInstancesFor(e.Entry.Entity as BaseEntity, importantHooksOnly);
85+
var entity = e.Entry.Entity as BaseEntity;
86+
if (HookedAlready(entity, e.PreSaveState, false))
87+
{
88+
// Prevent repetitive hooking of the same entity/state/pre combination within a single request
89+
continue;
90+
}
91+
var preHooks = GetPreHookInstancesFor(entity, importantHooksOnly);
8392
foreach (var hook in preHooks)
8493
{
8594
if (hook.CanProcess(e.PreSaveState) && hook.RequiresValidation == requiresValidation)
@@ -89,7 +98,7 @@ public bool TriggerPreActionHooks(IEnumerable<HookedEntityEntry> entries, bool r
8998
// call hook
9099
try
91100
{
92-
hook.HookObject(e.Entry.Entity, metadata);
101+
hook.HookObject(entity, metadata);
93102
}
94103
catch (Exception ex)
95104
{
@@ -153,7 +162,13 @@ public void TriggerPostActionHooks(IEnumerable<HookedEntityEntry> entries, bool
153162
foreach (var entry in entries)
154163
{
155164
var e = entry; // Prevents access to modified closure
156-
var postHooks = GetPostHookInstancesFor(e.Entry.Entity as BaseEntity, importantHooksOnly);
165+
var entity = e.Entry.Entity as BaseEntity;
166+
if (HookedAlready(entity, e.PreSaveState, true))
167+
{
168+
// Prevent repetitive hooking of the same entity/state/post combination within a single request
169+
continue;
170+
}
171+
var postHooks = GetPostHookInstancesFor(entity, importantHooksOnly);
157172
foreach (var hook in postHooks)
158173
{
159174
if (hook.CanProcess(e.PreSaveState))
@@ -163,7 +178,7 @@ public void TriggerPostActionHooks(IEnumerable<HookedEntityEntry> entries, bool
163178
// call hook
164179
try
165180
{
166-
hook.HookObject(e.Entry.Entity, metadata);
181+
hook.HookObject(entity, metadata);
167182
}
168183
catch (Exception ex)
169184
{
@@ -205,5 +220,28 @@ private IEnumerable<IPostActionHook> GetPostHookInstancesFor(BaseEntity entity,
205220

206221
return hooks;
207222
}
223+
224+
private bool HookedAlready(BaseEntity entity, EntityState preSaveState, bool isPostActionHook)
225+
{
226+
if (entity.IsTransientRecord())
227+
return false;
228+
229+
var key = new HookedEntityKey(entity.GetUnproxiedType(), entity.Id, preSaveState, isPostActionHook);
230+
if (_hookedEntities.Contains(key))
231+
{
232+
return true;
233+
}
234+
235+
_hookedEntities.Add(key);
236+
return false;
237+
}
238+
239+
class HookedEntityKey : Tuple<Type, int, EntityState, bool>
240+
{
241+
public HookedEntityKey(Type entityType, int entityId, EntityState preSaveState, bool isPostActionHook)
242+
: base(entityType, entityId, preSaveState, isPostActionHook)
243+
{
244+
}
245+
}
208246
}
209247
}

0 commit comments

Comments
 (0)