Skip to content

Commit 767252c

Browse files
Creates IDisposeOnRequestEnd and ensures UmbracoContext and UmbracoDatabase implement it, then we only dispose of these types of objects at the end of the request if they are part of the httpcontext items (U4-2738). Fixes: U4-2988 UmbracoObjectTypesExtensions is not thread safe
1 parent 010d47c commit 767252c

File tree

6 files changed

+64
-20
lines changed

6 files changed

+64
-20
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
6+
namespace Umbraco.Core
7+
{
8+
/// <summary>
9+
/// Any class implementing this interface that is added to the httpcontext.items keys or values will be disposed of at the end of the request.
10+
/// </summary>
11+
public interface IDisposeOnRequestEnd : IDisposable
12+
{
13+
}
14+
}

src/Umbraco.Core/Models/UmbracoObjectTypesExtensions.cs

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Concurrent;
23
using System.Collections.Generic;
34
using Umbraco.Core.CodeAnnotations;
45

@@ -9,7 +10,8 @@ namespace Umbraco.Core.Models
910
/// </summary>
1011
public static class UmbracoObjectTypesExtensions
1112
{
12-
private static readonly Dictionary<UmbracoObjectTypes, Guid> UmbracoObjectTypeCache = new Dictionary<UmbracoObjectTypes,Guid>();
13+
//MUST be concurrent to avoid thread collisions!
14+
private static readonly ConcurrentDictionary<UmbracoObjectTypes, Guid> UmbracoObjectTypeCache = new ConcurrentDictionary<UmbracoObjectTypes, Guid>();
1315

1416
/// <summary>
1517
/// Get an UmbracoObjectTypes value from it's name
@@ -48,24 +50,22 @@ public static UmbracoObjectTypes GetUmbracoObjectType(Guid guid)
4850
/// <returns>a GUID value of the UmbracoObjectTypes</returns>
4951
public static Guid GetGuid(this UmbracoObjectTypes umbracoObjectType)
5052
{
51-
if (UmbracoObjectTypeCache.ContainsKey(umbracoObjectType))
52-
return UmbracoObjectTypeCache[umbracoObjectType];
53-
54-
var type = typeof(UmbracoObjectTypes);
55-
var memInfo = type.GetMember(umbracoObjectType.ToString());
56-
var attributes = memInfo[0].GetCustomAttributes(typeof(UmbracoObjectTypeAttribute),
57-
false);
58-
59-
if (attributes.Length == 0)
60-
return Guid.Empty;
53+
return UmbracoObjectTypeCache.GetOrAdd(umbracoObjectType, types =>
54+
{
55+
var type = typeof(UmbracoObjectTypes);
56+
var memInfo = type.GetMember(umbracoObjectType.ToString());
57+
var attributes = memInfo[0].GetCustomAttributes(typeof(UmbracoObjectTypeAttribute),
58+
false);
6159

62-
var attribute = ((UmbracoObjectTypeAttribute)attributes[0]);
63-
if (attribute == null)
64-
return Guid.Empty;
60+
if (attributes.Length == 0)
61+
return Guid.Empty;
6562

66-
UmbracoObjectTypeCache.Add(umbracoObjectType, attribute.ObjectId);
63+
var attribute = ((UmbracoObjectTypeAttribute)attributes[0]);
64+
if (attribute == null)
65+
return Guid.Empty;
6766

68-
return attribute.ObjectId;
67+
return attribute.ObjectId;
68+
});
6969
}
7070

7171
/// <summary>

src/Umbraco.Core/Persistence/UmbracoDatabase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ namespace Umbraco.Core.Persistence
1616
/// can then override any additional execution (such as additional loggging, functionality, etc...) that we need to without breaking compatibility since we'll always be exposing
1717
/// this object instead of the base PetaPoco database object.
1818
/// </remarks>
19-
public class UmbracoDatabase : Database
19+
public class UmbracoDatabase : Database, IDisposeOnRequestEnd
2020
{
2121
private readonly Guid _instanceId = Guid.NewGuid();
2222
/// <summary>

src/Umbraco.Core/Umbraco.Core.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@
165165
<Compile Include="Events\SaveEventArgs.cs" />
166166
<Compile Include="Events\SendToPublishEventArgs.cs" />
167167
<Compile Include="IApplicationEventHandler.cs" />
168+
<Compile Include="IDisposeOnRequestEnd.cs" />
168169
<Compile Include="IO\ResizedImage.cs" />
169170
<Compile Include="IO\UmbracoMediaFile.cs" />
170171
<Compile Include="Media\MediaSubfolderCounter.cs" />

src/Umbraco.Web/UmbracoContext.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ namespace Umbraco.Web
2424
/// <summary>
2525
/// Class that encapsulates Umbraco information of a specific HTTP request
2626
/// </summary>
27-
public class UmbracoContext : DisposableObject
27+
public class UmbracoContext : DisposableObject, IDisposeOnRequestEnd
2828
{
2929
private const string HttpContextItemName = "Umbraco.Web.UmbracoContext";
3030
private static readonly object Locker = new object();

src/Umbraco.Web/UmbracoModule.cs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections;
3+
using System.Collections.Generic;
34
using System.IO;
45
using System.Linq;
56
using System.Threading;
@@ -465,10 +466,38 @@ static void PersistXmlCache(HttpContextBase httpContext)
465466
/// <param name="http"></param>
466467
private static void DisposeHttpContextItems(HttpContext http)
467468
{
469+
// do not process if client-side request
470+
if (http.Request.Url.IsClientSideRequest())
471+
return;
472+
473+
//get a list of keys to dispose
474+
var keys = new HashSet<object>();
468475
foreach (DictionaryEntry i in http.Items)
469476
{
470-
i.Value.DisposeIfDisposable();
471-
i.Key.DisposeIfDisposable();
477+
if (i.Value is IDisposeOnRequestEnd || i.Key is IDisposeOnRequestEnd)
478+
{
479+
keys.Add(i.Key);
480+
}
481+
}
482+
//dispose each item and key that was found as disposable.
483+
foreach (var k in keys)
484+
{
485+
try
486+
{
487+
http.Items[k].DisposeIfDisposable();
488+
}
489+
catch (Exception ex)
490+
{
491+
LogHelper.Error<UmbracoModule>("Could not dispose item with key " + k, ex);
492+
}
493+
try
494+
{
495+
k.DisposeIfDisposable();
496+
}
497+
catch (Exception ex)
498+
{
499+
LogHelper.Error<UmbracoModule>("Could not dispose item key " + k, ex);
500+
}
472501
}
473502
}
474503

0 commit comments

Comments
 (0)