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

This commit is contained in:
Sebastiaan Janssen
2013-09-30 10:31:45 +02:00
parent 010d47c68b
commit 767252cdf1
6 changed files with 64 additions and 20 deletions

View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Umbraco.Core
{
/// <summary>
/// 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.
/// </summary>
public interface IDisposeOnRequestEnd : IDisposable
{
}
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using Umbraco.Core.CodeAnnotations;
@@ -9,7 +10,8 @@ namespace Umbraco.Core.Models
/// </summary>
public static class UmbracoObjectTypesExtensions
{
private static readonly Dictionary<UmbracoObjectTypes, Guid> UmbracoObjectTypeCache = new Dictionary<UmbracoObjectTypes,Guid>();
//MUST be concurrent to avoid thread collisions!
private static readonly ConcurrentDictionary<UmbracoObjectTypes, Guid> UmbracoObjectTypeCache = new ConcurrentDictionary<UmbracoObjectTypes, Guid>();
/// <summary>
/// Get an UmbracoObjectTypes value from it's name
@@ -48,24 +50,22 @@ namespace Umbraco.Core.Models
/// <returns>a GUID value of the UmbracoObjectTypes</returns>
public static Guid GetGuid(this UmbracoObjectTypes umbracoObjectType)
{
if (UmbracoObjectTypeCache.ContainsKey(umbracoObjectType))
return UmbracoObjectTypeCache[umbracoObjectType];
return UmbracoObjectTypeCache.GetOrAdd(umbracoObjectType, types =>
{
var type = typeof(UmbracoObjectTypes);
var memInfo = type.GetMember(umbracoObjectType.ToString());
var attributes = memInfo[0].GetCustomAttributes(typeof(UmbracoObjectTypeAttribute),
false);
var type = typeof(UmbracoObjectTypes);
var memInfo = type.GetMember(umbracoObjectType.ToString());
var attributes = memInfo[0].GetCustomAttributes(typeof(UmbracoObjectTypeAttribute),
false);
if (attributes.Length == 0)
return Guid.Empty;
if (attributes.Length == 0)
return Guid.Empty;
var attribute = ((UmbracoObjectTypeAttribute)attributes[0]);
if (attribute == null)
return Guid.Empty;
var attribute = ((UmbracoObjectTypeAttribute)attributes[0]);
if (attribute == null)
return Guid.Empty;
UmbracoObjectTypeCache.Add(umbracoObjectType, attribute.ObjectId);
return attribute.ObjectId;
return attribute.ObjectId;
});
}
/// <summary>

View File

@@ -16,7 +16,7 @@ namespace Umbraco.Core.Persistence
/// 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
/// this object instead of the base PetaPoco database object.
/// </remarks>
public class UmbracoDatabase : Database
public class UmbracoDatabase : Database, IDisposeOnRequestEnd
{
private readonly Guid _instanceId = Guid.NewGuid();
/// <summary>

View File

@@ -165,6 +165,7 @@
<Compile Include="Events\SaveEventArgs.cs" />
<Compile Include="Events\SendToPublishEventArgs.cs" />
<Compile Include="IApplicationEventHandler.cs" />
<Compile Include="IDisposeOnRequestEnd.cs" />
<Compile Include="IO\ResizedImage.cs" />
<Compile Include="IO\UmbracoMediaFile.cs" />
<Compile Include="Media\MediaSubfolderCounter.cs" />

View File

@@ -24,7 +24,7 @@ namespace Umbraco.Web
/// <summary>
/// Class that encapsulates Umbraco information of a specific HTTP request
/// </summary>
public class UmbracoContext : DisposableObject
public class UmbracoContext : DisposableObject, IDisposeOnRequestEnd
{
private const string HttpContextItemName = "Umbraco.Web.UmbracoContext";
private static readonly object Locker = new object();

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
@@ -465,10 +466,38 @@ namespace Umbraco.Web
/// <param name="http"></param>
private static void DisposeHttpContextItems(HttpContext http)
{
// do not process if client-side request
if (http.Request.Url.IsClientSideRequest())
return;
//get a list of keys to dispose
var keys = new HashSet<object>();
foreach (DictionaryEntry i in http.Items)
{
i.Value.DisposeIfDisposable();
i.Key.DisposeIfDisposable();
if (i.Value is IDisposeOnRequestEnd || i.Key is IDisposeOnRequestEnd)
{
keys.Add(i.Key);
}
}
//dispose each item and key that was found as disposable.
foreach (var k in keys)
{
try
{
http.Items[k].DisposeIfDisposable();
}
catch (Exception ex)
{
LogHelper.Error<UmbracoModule>("Could not dispose item with key " + k, ex);
}
try
{
k.DisposeIfDisposable();
}
catch (Exception ex)
{
LogHelper.Error<UmbracoModule>("Could not dispose item key " + k, ex);
}
}
}