Converts dictionary cache in ContentType to use concurrent dictionary. Changes ContentTypeControlNew to

not call the propertytype.delete() method if it is a DocumentType or MediaType (should only be called for
legacy code like members). Updates ContentTypeCacheRefresher to ensure that all of the content type cache is
cleared properly in the ICacheRefresher.
This commit is contained in:
Shannon Deminick
2013-03-19 02:02:11 +06:00
parent e3a8b4ac78
commit 3052c7311f
4 changed files with 120 additions and 71 deletions

View File

@@ -5,10 +5,18 @@ using System.Text;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence.Caching;
namespace Umbraco.Web.Cache
{
/// <summary>
/// A cache refresher to ensure content type cache is updated when members change
/// </summary>
/// <remarks>
/// This is not intended to be used directly in your code
/// </remarks>
public sealed class ContentTypeCacheRefresher : ICacheRefresher<IContentType>, ICacheRefresher<IMediaType>
{
public Guid UniqueIdentifier
@@ -25,7 +33,11 @@ namespace Umbraco.Web.Cache
//all property type cache
ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(CacheKeys.PropertyTypeCacheKey);
//all content type property cache
ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(CacheKeys.ContentTypePropertiesCacheKey);
ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(CacheKeys.ContentTypePropertiesCacheKey);
//all content type cache
ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(CacheKeys.ContentTypeCacheKey);
//clear static object cache
global::umbraco.cms.businesslogic.ContentType.RemoveAllDataTypeCache();
}
public void Refresh(int id)
@@ -62,27 +74,70 @@ namespace Umbraco.Web.Cache
ClearContentTypeCache(instance);
}
/// <summary>
/// This clears out all cache associated with a content type
/// </summary>
/// <param name="contentTypes"></param>
/// <remarks>
/// The cache that is required to be cleared when a content type is updated is as follows:
/// - ApplicationCache (keys to clear):
/// -- CacheKeys.PropertyTypeCacheKey + propertyType.Id (each property type assigned)
/// -- CacheKeys.ContentTypePropertiesCacheKey + contentType.Id
/// - ContentType.RemoveFromDataTypeCache (clears static object/dictionary cache)
/// - InMemoryCacheProvider.Current.Clear();
/// - RuntimeCacheProvider.Current.Clear();
///
/// TODO: Needs to update any content items that this effects for the xml cache... currently it would seem that this is not handled!
/// it is only handled in the ContentTypeControlNew.ascx, not by business logic/events. - The xml cache needs to be updated when the doc type alias changes or when a property type is removed, the ContentService.RePublishAll should be executed anytime either of these happens.
/// </remarks>
private static void ClearContentTypeCache(params IContentTypeBase[] contentTypes)
{
contentTypes.ForEach(contentType =>
{
//clears the cache for each property type associated with the content type
foreach (var p in contentType.PropertyTypes)
{
ApplicationContext.Current.ApplicationCache.ClearCacheItem(CacheKeys.PropertyTypeCacheKey + p.Id);
}
//clears the cache associated with the content type properties collection
ApplicationContext.Current.ApplicationCache.ClearCacheItem(CacheKeys.ContentTypePropertiesCacheKey + contentType.Id);
//clears the dictionary object cache of the legacy ContentType
global::umbraco.cms.businesslogic.ContentType.RemoveFromDataTypeCache(contentType.Alias);
});
contentTypes.ForEach(ClearContentTypeCache);
//clear the cache providers if there were any content types to clear
if (contentTypes.Any())
{
InMemoryCacheProvider.Current.Clear();
RuntimeCacheProvider.Current.Clear();
}
}
}
/// <summary>
/// Clears cache for an individual IContentTypeBase object
/// </summary>
/// <param name="contentType"></param>
/// <remarks>
/// See notes for the other overloaded ClearContentTypeCache for
/// full details on clearing cache.
/// </remarks>
private static void ClearContentTypeCache(IContentTypeBase contentType)
{
//clears the cache for each property type associated with the content type
foreach (var p in contentType.PropertyTypes)
{
ApplicationContext.Current.ApplicationCache.ClearCacheItem(CacheKeys.PropertyTypeCacheKey + p.Id);
}
//clears the cache associated with the Content type itself
ApplicationContext.Current.ApplicationCache.ClearCacheItem(string.Format("{0}{1}", CacheKeys.ContentTypeCacheKey, contentType.Id));
//clears the cache associated with the content type properties collection
ApplicationContext.Current.ApplicationCache.ClearCacheItem(CacheKeys.ContentTypePropertiesCacheKey + contentType.Id);
//clears the dictionary object cache of the legacy ContentType
global::umbraco.cms.businesslogic.ContentType.RemoveFromDataTypeCache(contentType.Alias);
//need to recursively clear the cache for each child content type
// performance related to http://issues.umbraco.org/issue/U4-1714
var dtos = ApplicationContext.Current.DatabaseContext.Database.Fetch<ContentType2ContentTypeDto>("WHERE parentContentTypeId = @Id", new { Id = contentType.Id });
foreach (var dto in dtos)
{
ClearContentTypeCache(dto.ChildId);
}
}
/// <summary>
/// Clears the cache for any content type with the specified Ids
/// </summary>
/// <param name="ids"></param>
private static void ClearContentTypeCache(params int[] ids)
{
ClearContentTypeCache(

View File

@@ -866,8 +866,14 @@ jQuery(document).ready(function() {{ refreshDropDowns(); }});
_contentType.ContentTypeItem.RemovePropertyType(gpw.PropertyType.Alias);
_contentType.Save();
}
gpw.GenricPropertyControl.PropertyType.delete();
else
{
//if it is not a document type or a media type, then continue to call the legacy delete() method.
//the new API for document type and media type's will ensure that the data is removed correctly and that
//the cache is flushed correctly (using events). If it is not one of these types, we'll rever to the
//legacy operation (... like for members i suppose ?)
gpw.GenricPropertyControl.PropertyType.delete();
}
LoadContentType(_contentType.Id);
BindDataGenericProperties(true);

View File

@@ -99,8 +99,7 @@ namespace umbraco.cms.businesslogic
/// Used for cache so we don't have to lookup column names all the time, this is actually only used for the ChildrenAsTable methods
/// </summary>
private static readonly ConcurrentDictionary<string, IDictionary<string, string>> AliasToNames = new ConcurrentDictionary<string, IDictionary<string, string>>();
private static Dictionary<Tuple<string, string>, Guid> _propertyTypeCache = new Dictionary<Tuple<string, string>, Guid>();
private static readonly ConcurrentDictionary<Tuple<string, string>, Guid> PropertyTypeCache = new ConcurrentDictionary<Tuple<string, string>, Guid>();
/// <summary>
/// Returns a content type's columns alias -> name mapping
@@ -112,68 +111,58 @@ namespace umbraco.cms.businesslogic
/// </remarks>
internal static IDictionary<string, string> GetAliasesAndNames(string contentTypeAlias)
{
IDictionary<string, string> cached;
if (AliasToNames.TryGetValue(contentTypeAlias, out cached))
{
return cached;
}
var ct = ContentType.GetByAlias(contentTypeAlias);
var userFields = ct.PropertyTypes.ToDictionary(x => x.Alias, x => x.Name);
AliasToNames.TryAdd(contentTypeAlias, userFields);
return userFields;
return AliasToNames.GetOrAdd(contentTypeAlias, s =>
{
var ct = ContentType.GetByAlias(contentTypeAlias);
var userFields = ct.PropertyTypes.ToDictionary(x => x.Alias, x => x.Name);
return userFields;
});
}
/// <summary>
/// Removes the static object cache
/// </summary>
/// <param name="contentTypeAlias"></param>
public static void RemoveFromDataTypeCache(string contentTypeAlias)
{
lock (_propertyTypeCache)
var toDelete = PropertyTypeCache.Keys.Where(key => string.Equals(key.first, contentTypeAlias)).ToList();
foreach (var key in toDelete)
{
List<Tuple<string, string>> toDelete = new List<Tuple<string, string>>();
foreach (Tuple<string, string> key in _propertyTypeCache.Keys)
{
if (string.Equals(key.first, contentTypeAlias))
{
toDelete.Add(key);
}
}
foreach (Tuple<string, string> key in toDelete)
{
_propertyTypeCache.Remove(key);
}
Guid id;
PropertyTypeCache.TryRemove(key, out id);
}
//don't put lock around this as it is ConcurrentDictionary.
AliasToNames.Clear();
}
/// <summary>
/// Removes the static object cache
/// </summary>
internal static void RemoveAllDataTypeCache()
{
AliasToNames.Clear();
PropertyTypeCache.Clear();
}
public static Guid GetDataType(string contentTypeAlias, string propertyTypeAlias)
{
Tuple<string, string> key = new Tuple<string, string>()
var key = new Tuple<string, string>()
{
first = contentTypeAlias,
second = propertyTypeAlias
};
//The property type is not on IProperty (it's not stored in NodeFactory)
//first check the cache
if (_propertyTypeCache != null && _propertyTypeCache.ContainsKey(key))
{
return _propertyTypeCache[key];
}
// With 4.10 we can't do this via direct SQL as we have content type mixins
Guid controlId = Guid.Empty;
ContentType ct = GetByAlias(contentTypeAlias);
PropertyType pt = ct.getPropertyType(propertyTypeAlias);
if (pt != null)
{
controlId = pt.DataTypeDefinition.DataType.Id;
}
//add to cache (even if empty!)
if (!_propertyTypeCache.ContainsKey(key))
{
_propertyTypeCache.Add(key, controlId);
}
return controlId;
return PropertyTypeCache.GetOrAdd(key, tuple =>
{
// With 4.10 we can't do this via direct SQL as we have content type mixins
var controlId = Guid.Empty;
var ct = GetByAlias(contentTypeAlias);
var pt = ct.getPropertyType(propertyTypeAlias);
if (pt != null)
{
controlId = pt.DataTypeDefinition.DataType.Id;
}
return controlId;
});
}
@@ -196,7 +185,7 @@ namespace umbraco.cms.businesslogic
/// Flushes the tab cache.
/// </summary>
/// <param name="TabId">The tab id.</param>
[Obsolete("Tab cache is flushed automatically by Umbraco when a content type changes")]
[Obsolete("There is no cache to flush for tabs")]
public static void FlushTabCache(int TabId, int ContentTypeId)
{
Tab.FlushCache(TabId, ContentTypeId);
@@ -731,7 +720,7 @@ namespace umbraco.cms.businesslogic
[Obsolete("Use PropertyTypeGroup methods instead", false)]
public void ClearVirtualTabs()
{
// zb-00040 #29889 : clear the right cache! t.contentType is the ctype which _defines_ the tab, not the current one.
//NOTE: SD: There is no cache to clear so this doesn't actually do anything
foreach (TabI t in getVirtualTabs)
Tab.FlushCache(t.Id, Id);
@@ -1132,14 +1121,13 @@ namespace umbraco.cms.businesslogic
}
}
[Obsolete("The content type cache is automatically cleared by Umbraco when a content type is saved")]
[Obsolete("The content type cache is automatically cleared by Umbraco when a content type is saved, this method is no longer used")]
protected internal void FlushAllFromCache()
{
ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(CacheKeys.ContentTypeCacheKey);
ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(CacheKeys.ContentTypePropertiesCacheKey);
//clear the property datatype cache used by razor
_propertyTypeCache = new Dictionary<Tuple<string, string>, Guid>();
RemoveAllDataTypeCache();
ClearVirtualTabs();
}

View File

@@ -867,7 +867,7 @@ namespace umbraco.cms.businesslogic.packager
}
}
// clear caching
// clear caching (NOTE: SD: there is no tab caching so this really doesn't do anything)
foreach (DocumentType.TabI t in dt.getVirtualTabs.ToList())
DocumentType.FlushTabCache(t.Id, dt.Id);