Fixes: #U4-1992 - Creates DictionaryCacheRefresher to ensure that all cache associated with the dictionary is updated amongst all
servers when it is changed/removed. Removes RemoveByJson as we only actually require RefreshByJson since we can put any sort of parameters in a custom json string including whether it is a remove operation (if required)
This commit is contained in:
@@ -7,7 +7,10 @@ namespace Umbraco.Core.Cache
|
||||
/// </summary>
|
||||
interface IJsonCacheRefresher : ICacheRefresher
|
||||
{
|
||||
/// <summary>
|
||||
/// Refreshes, clears, etc... any cache based on the information provided in the json
|
||||
/// </summary>
|
||||
/// <param name="jsonPayload"></param>
|
||||
void Refresh(string jsonPayload);
|
||||
void Remove(string jsonPayload);
|
||||
}
|
||||
}
|
||||
@@ -16,10 +16,5 @@ namespace Umbraco.Core.Cache
|
||||
{
|
||||
OnCacheUpdated(Instance, new CacheRefresherEventArgs(jsonPayload, MessageType.RefreshByJson));
|
||||
}
|
||||
|
||||
public virtual void Remove(string jsonPayload)
|
||||
{
|
||||
OnCacheUpdated(Instance, new CacheRefresherEventArgs(jsonPayload, MessageType.RemoveByJson));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -91,15 +91,6 @@ namespace Umbraco.Core.Sync
|
||||
instances);
|
||||
}
|
||||
|
||||
public void PerformRemove(IEnumerable<IServerAddress> servers, ICacheRefresher refresher, string jsonPayload)
|
||||
{
|
||||
if (servers == null) throw new ArgumentNullException("servers");
|
||||
if (refresher == null) throw new ArgumentNullException("refresher");
|
||||
if (jsonPayload == null) throw new ArgumentNullException("jsonPayload");
|
||||
|
||||
MessageSeversForIdsOrJson(servers, refresher, MessageType.RemoveByJson, jsonPayload: jsonPayload);
|
||||
}
|
||||
|
||||
public void PerformRemove<T>(IEnumerable<IServerAddress> servers, ICacheRefresher refresher, Func<T, int> getNumericId, params T[] instances)
|
||||
{
|
||||
if (servers == null) throw new ArgumentNullException("servers");
|
||||
@@ -283,15 +274,7 @@ namespace Umbraco.Core.Sync
|
||||
}
|
||||
|
||||
//if we are not, then just invoke the call on the cache refresher
|
||||
switch (dispatchType)
|
||||
{
|
||||
case MessageType.RefreshByJson:
|
||||
jsonRefresher.Refresh(jsonPayload);
|
||||
break;
|
||||
case MessageType.RemoveByJson:
|
||||
jsonRefresher.Remove(jsonPayload);
|
||||
break;
|
||||
}
|
||||
jsonRefresher.Refresh(jsonPayload);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -380,12 +363,7 @@ namespace Umbraco.Core.Sync
|
||||
asyncResultsList.Add(
|
||||
cacheRefresher.BeginRefreshByJson(
|
||||
refresher.UniqueIdentifier, jsonPayload, _login, _password, null, null));
|
||||
break;
|
||||
case MessageType.RemoveByJson:
|
||||
asyncResultsList.Add(
|
||||
cacheRefresher.BeginRemoveByJson(
|
||||
refresher.UniqueIdentifier, jsonPayload, _login, _password, null, null));
|
||||
break;
|
||||
break;
|
||||
case MessageType.RefreshAll:
|
||||
asyncResultsList.Add(
|
||||
cacheRefresher.BeginRefreshAll(
|
||||
@@ -438,9 +416,6 @@ namespace Umbraco.Core.Sync
|
||||
case MessageType.RefreshByJson:
|
||||
cacheRefresher.EndRefreshByJson(t);
|
||||
break;
|
||||
case MessageType.RemoveByJson:
|
||||
cacheRefresher.EndRemoveByJson(t);
|
||||
break;
|
||||
case MessageType.RefreshAll:
|
||||
cacheRefresher.EndRefreshAll(t);
|
||||
break;
|
||||
|
||||
@@ -40,16 +40,6 @@ namespace Umbraco.Core.Sync
|
||||
/// <param name="instances"></param>
|
||||
void PerformRefresh<T>(IEnumerable<IServerAddress> servers, ICacheRefresher refresher, Func<T, Guid> getGuidId, params T[] instances);
|
||||
|
||||
/// <summary>
|
||||
/// Performs a remove and sends along the JSON payload to each server
|
||||
/// </summary>
|
||||
/// <param name="servers"></param>
|
||||
/// <param name="refresher"></param>
|
||||
/// <param name="jsonPayload">
|
||||
/// A pre-formatted custom json payload to be sent to the servers, the cache refresher will deserialize and use to remove cache
|
||||
/// </param>
|
||||
void PerformRemove(IEnumerable<IServerAddress> servers, ICacheRefresher refresher, string jsonPayload);
|
||||
|
||||
/// <summary>
|
||||
/// Removes the cache for the specified items
|
||||
/// </summary>
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
RefreshById,
|
||||
RefreshByJson,
|
||||
RemoveById,
|
||||
RemoveByJson,
|
||||
RefreshByInstance,
|
||||
RemoveByInstance
|
||||
}
|
||||
|
||||
@@ -45,34 +45,7 @@ namespace Umbraco.Core.Sync
|
||||
{
|
||||
this.EndInvoke(asyncResult);
|
||||
}
|
||||
|
||||
/// <remarks/>
|
||||
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://umbraco.org/webservices/RemoveByJson", RequestNamespace = "http://umbraco.org/webservices/", ResponseNamespace = "http://umbraco.org/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
|
||||
public void RemoveByJson(System.Guid uniqueIdentifier, string jsonPayload, string Login, string Password)
|
||||
{
|
||||
this.Invoke("RemoveByJson", new object[] {
|
||||
uniqueIdentifier,
|
||||
jsonPayload,
|
||||
Login,
|
||||
Password});
|
||||
}
|
||||
|
||||
/// <remarks/>
|
||||
public System.IAsyncResult BeginRemoveByJson(System.Guid uniqueIdentifier, string jsonPayload, string Login, string Password, System.AsyncCallback callback, object asyncState)
|
||||
{
|
||||
return this.BeginInvoke("RemoveByJson", new object[] {
|
||||
uniqueIdentifier,
|
||||
jsonPayload,
|
||||
Login,
|
||||
Password}, callback, asyncState);
|
||||
}
|
||||
|
||||
/// <remarks/>
|
||||
public void EndRemoveByJson(System.IAsyncResult asyncResult)
|
||||
{
|
||||
this.EndInvoke(asyncResult);
|
||||
}
|
||||
|
||||
|
||||
/// <remarks/>
|
||||
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://umbraco.org/webservices/RefreshByJson", RequestNamespace = "http://umbraco.org/webservices/", ResponseNamespace = "http://umbraco.org/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
|
||||
public void RefreshByJson(System.Guid uniqueIdentifier, string jsonPayload, string Login, string Password)
|
||||
|
||||
@@ -238,6 +238,7 @@ namespace Umbraco.Tests.BusinessLogic
|
||||
var newKey = "NEWKEY" + Guid.NewGuid().ToString("N");
|
||||
|
||||
d.key = newKey;
|
||||
d.Save();
|
||||
Assert.AreNotEqual(oldKey, d.key);
|
||||
Assert.AreEqual(newKey, d.key);
|
||||
|
||||
|
||||
@@ -23,6 +23,15 @@ namespace Umbraco.Web.Cache
|
||||
protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
|
||||
{
|
||||
|
||||
//Bind to dictionary events
|
||||
//NOTE: we need to bind to legacy and new API events currently: http://issues.umbraco.org/issue/U4-1979
|
||||
|
||||
global::umbraco.cms.businesslogic.Dictionary.DictionaryItem.New += DictionaryItemNew;
|
||||
global::umbraco.cms.businesslogic.Dictionary.DictionaryItem.Saving +=DictionaryItemSaving;
|
||||
global::umbraco.cms.businesslogic.Dictionary.DictionaryItem.Deleted +=DictionaryItemDeleted;
|
||||
LocalizationService.DeletedDictionaryItem += LocalizationServiceDeletedDictionaryItem;
|
||||
LocalizationService.SavedDictionaryItem += LocalizationServiceSavedDictionaryItem;
|
||||
|
||||
//Bind to data type events
|
||||
//NOTE: we need to bind to legacy and new API events currently: http://issues.umbraco.org/issue/U4-1979
|
||||
|
||||
@@ -97,6 +106,35 @@ namespace Umbraco.Web.Cache
|
||||
MediaService.Trashing += MediaServiceTrashing;
|
||||
}
|
||||
|
||||
#region Dictionary event handlers
|
||||
|
||||
static void LocalizationServiceSavedDictionaryItem(ILocalizationService sender, Core.Events.SaveEventArgs<IDictionaryItem> e)
|
||||
{
|
||||
e.SavedEntities.ForEach(x => DistributedCache.Instance.RefreshDictionaryCache(x.Id));
|
||||
}
|
||||
|
||||
static void LocalizationServiceDeletedDictionaryItem(ILocalizationService sender, Core.Events.DeleteEventArgs<IDictionaryItem> e)
|
||||
{
|
||||
e.DeletedEntities.ForEach(x => DistributedCache.Instance.RemoveDictionaryCache(x.Id));
|
||||
}
|
||||
|
||||
static void DictionaryItemDeleted(global::umbraco.cms.businesslogic.Dictionary.DictionaryItem sender, System.EventArgs e)
|
||||
{
|
||||
DistributedCache.Instance.RemoveDictionaryCache(sender.id);
|
||||
}
|
||||
|
||||
static void DictionaryItemSaving(global::umbraco.cms.businesslogic.Dictionary.DictionaryItem sender, System.EventArgs e)
|
||||
{
|
||||
DistributedCache.Instance.RefreshDictionaryCache(sender.id);
|
||||
}
|
||||
|
||||
static void DictionaryItemNew(global::umbraco.cms.businesslogic.Dictionary.DictionaryItem sender, System.EventArgs e)
|
||||
{
|
||||
DistributedCache.Instance.RefreshDictionaryCache(sender.id);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region DataType event handlers
|
||||
static void DataTypeServiceSaved(IDataTypeService sender, Core.Events.SaveEventArgs<IDataTypeDefinition> e)
|
||||
{
|
||||
@@ -132,12 +170,12 @@ namespace Umbraco.Web.Cache
|
||||
|
||||
static void FileServiceDeletedStylesheet(IFileService sender, Core.Events.DeleteEventArgs<Stylesheet> e)
|
||||
{
|
||||
e.DeletedEntities.ForEach(DistributedCache.Instance.RemoveStylesheetCache);
|
||||
e.DeletedEntities.ForEach(x => DistributedCache.Instance.RemoveStylesheetCache(x));
|
||||
}
|
||||
|
||||
static void FileServiceSavedStylesheet(IFileService sender, Core.Events.SaveEventArgs<Stylesheet> e)
|
||||
{
|
||||
e.SavedEntities.ForEach(DistributedCache.Instance.RefreshStylesheetCache);
|
||||
e.SavedEntities.ForEach(x => DistributedCache.Instance.RefreshStylesheetCache(x));
|
||||
}
|
||||
|
||||
static void StyleSheetAfterSave(StyleSheet sender, SaveEventArgs e)
|
||||
|
||||
@@ -156,17 +156,6 @@ namespace Umbraco.Web.Cache
|
||||
base.Refresh(jsonPayload);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the cache using the custom jsonPayload provided
|
||||
/// </summary>
|
||||
/// <param name="jsonPayload"></param>
|
||||
public override void Remove(string jsonPayload)
|
||||
{
|
||||
var payload = DeserializeFromJsonPayload(jsonPayload);
|
||||
ClearContentTypeCache(payload);
|
||||
base.Remove(jsonPayload);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This clears out all cache associated with a content type
|
||||
/// </summary>
|
||||
|
||||
42
src/Umbraco.Web/Cache/DictionaryCacheRefresher.cs
Normal file
42
src/Umbraco.Web/Cache/DictionaryCacheRefresher.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using Umbraco.Core.Cache;
|
||||
|
||||
namespace Umbraco.Web.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// A cache refresher to ensure the dictionary cache is refreshed when dictionary change
|
||||
/// </summary>
|
||||
public sealed class DictionaryCacheRefresher : CacheRefresherBase<DictionaryCacheRefresher>
|
||||
{
|
||||
protected override DictionaryCacheRefresher Instance
|
||||
{
|
||||
get { return this; }
|
||||
}
|
||||
|
||||
public override Guid UniqueIdentifier
|
||||
{
|
||||
get { return new Guid(DistributedCache.DictionaryCacheRefresherId); }
|
||||
}
|
||||
|
||||
public override string Name
|
||||
{
|
||||
get { return "Dictionary cache refresher"; }
|
||||
}
|
||||
|
||||
public override void Refresh(int id)
|
||||
{
|
||||
global::umbraco.cms.businesslogic.Dictionary.ClearCache();
|
||||
//when a dictionary item is updated we must also clear the text cache!
|
||||
global::umbraco.cms.businesslogic.language.Item.ClearCache();
|
||||
base.Refresh(id);
|
||||
}
|
||||
|
||||
public override void Remove(int id)
|
||||
{
|
||||
global::umbraco.cms.businesslogic.Dictionary.ClearCache();
|
||||
//when a dictionary item is removed we must also clear the text cache!
|
||||
global::umbraco.cms.businesslogic.language.Item.ClearCache();
|
||||
base.Remove(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,7 @@ namespace Umbraco.Web.Cache
|
||||
public const string StylesheetCacheRefresherId = "E0633648-0DEB-44AE-9A48-75C3A55CB670";
|
||||
public const string StylesheetPropertyCacheRefresherId = "2BC7A3A4-6EB1-4FBC-BAA3-C9E7B6D36D38";
|
||||
public const string DataTypeCacheRefresherId = "35B16C25-A17E-45D7-BC8F-EDAB1DCC28D2";
|
||||
public const string DictionaryCacheRefresherId = "D1D7E227-F817-4816-BFE9-6C39B6152884";
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -206,21 +207,7 @@ namespace Umbraco.Web.Cache
|
||||
GetRefresherById(factoryGuid),
|
||||
getNumericId,
|
||||
instances);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a request to all registered load-balanced servers to remove data based on the custom json payload
|
||||
/// using the specified ICacheRefresher with the guid factoryGuid.
|
||||
/// </summary>
|
||||
/// <param name="factoryGuid"></param>
|
||||
/// <param name="jsonPayload"></param>
|
||||
public void RemoveByJson(Guid factoryGuid, string jsonPayload)
|
||||
{
|
||||
ServerMessengerResolver.Current.Messenger.PerformRemove(
|
||||
ServerRegistrarResolver.Current.Registrar.Registrations,
|
||||
GetRefresherById(factoryGuid),
|
||||
jsonPayload);
|
||||
}
|
||||
}
|
||||
|
||||
private static ICacheRefresher GetRefresherById(Guid uniqueIdentifier)
|
||||
{
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Umbraco.Web.Cache
|
||||
/// <summary>
|
||||
/// Extension methods for DistrubutedCache
|
||||
/// </summary>
|
||||
public static class DistributedCacheExtensions
|
||||
internal static class DistributedCacheExtensions
|
||||
{
|
||||
#region User cache
|
||||
public static void RemoveUserCache(this DistributedCache dc, int userId)
|
||||
@@ -48,6 +48,30 @@ namespace Umbraco.Web.Cache
|
||||
|
||||
#endregion
|
||||
|
||||
#region Dictionary cache
|
||||
/// <summary>
|
||||
/// Refreshes the cache amongst servers for a dictionary item
|
||||
/// </summary>
|
||||
/// <param name="dc"></param>
|
||||
/// <param name="dictionaryItemId"></param>
|
||||
public static void RefreshDictionaryCache(this DistributedCache dc, int dictionaryItemId)
|
||||
{
|
||||
dc.Refresh(new Guid(DistributedCache.DictionaryCacheRefresherId), dictionaryItemId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes the cache amongst servers for a dictionary item
|
||||
/// </summary>
|
||||
/// <param name="dc"></param>
|
||||
/// <param name="dictionaryItemId"></param>
|
||||
public static void RemoveDictionaryCache(this DistributedCache dc, int dictionaryItemId)
|
||||
{
|
||||
dc.Remove(new Guid(DistributedCache.DictionaryCacheRefresherId), dictionaryItemId);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Data type cache
|
||||
/// <summary>
|
||||
/// Refreshes the cache amongst servers for a template
|
||||
@@ -179,7 +203,7 @@ namespace Umbraco.Web.Cache
|
||||
/// <param name="media"></param>
|
||||
public static void RemoveMediaCache(this DistributedCache dc, params IMedia[] media)
|
||||
{
|
||||
dc.RemoveByJson(new Guid(DistributedCache.MediaCacheRefresherId),
|
||||
dc.RefreshByJson(new Guid(DistributedCache.MediaCacheRefresherId),
|
||||
MediaCacheRefresher.SerializeToJsonPayload(media));
|
||||
}
|
||||
|
||||
@@ -220,7 +244,7 @@ namespace Umbraco.Web.Cache
|
||||
{
|
||||
if (macro != null)
|
||||
{
|
||||
dc.RemoveByJson(new Guid(DistributedCache.MacroCacheRefresherId),
|
||||
dc.RefreshByJson(new Guid(DistributedCache.MacroCacheRefresherId),
|
||||
MacroCacheRefresher.SerializeToJsonPayload(macro));
|
||||
}
|
||||
}
|
||||
@@ -234,7 +258,7 @@ namespace Umbraco.Web.Cache
|
||||
{
|
||||
if (macro != null && macro.Model != null)
|
||||
{
|
||||
dc.RemoveByJson(new Guid(DistributedCache.MacroCacheRefresherId),
|
||||
dc.RefreshByJson(new Guid(DistributedCache.MacroCacheRefresherId),
|
||||
MacroCacheRefresher.SerializeToJsonPayload(macro));
|
||||
}
|
||||
}
|
||||
@@ -282,7 +306,7 @@ namespace Umbraco.Web.Cache
|
||||
if (contentType != null)
|
||||
{
|
||||
//dc.Remove(new Guid(DistributedCache.ContentTypeCacheRefresherId), x => x.Id, contentType);
|
||||
dc.RemoveByJson(new Guid(DistributedCache.ContentTypeCacheRefresherId),
|
||||
dc.RefreshByJson(new Guid(DistributedCache.ContentTypeCacheRefresherId),
|
||||
ContentTypeCacheRefresher.SerializeToJsonPayload(true, contentType));
|
||||
}
|
||||
}
|
||||
@@ -297,7 +321,7 @@ namespace Umbraco.Web.Cache
|
||||
if (mediaType != null)
|
||||
{
|
||||
//dc.Remove(new Guid(DistributedCache.ContentTypeCacheRefresherId), x => x.Id, mediaType);
|
||||
dc.RemoveByJson(new Guid(DistributedCache.ContentTypeCacheRefresherId),
|
||||
dc.RefreshByJson(new Guid(DistributedCache.ContentTypeCacheRefresherId),
|
||||
ContentTypeCacheRefresher.SerializeToJsonPayload(true, mediaType));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,8 +31,10 @@ namespace Umbraco.Web.Cache
|
||||
}
|
||||
|
||||
public override void Remove(int id)
|
||||
{
|
||||
{
|
||||
ApplicationContext.Current.ApplicationCache.ClearCacheItem(CacheKeys.LanguageCacheKey);
|
||||
//when a language is removed we must also clear the text cache!
|
||||
global::umbraco.cms.businesslogic.language.Item.ClearCache();
|
||||
base.Remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,23 +148,18 @@ namespace Umbraco.Web.Cache
|
||||
}
|
||||
|
||||
public override void Refresh(string jsonPayload)
|
||||
{
|
||||
Remove(jsonPayload);
|
||||
base.Refresh(jsonPayload);
|
||||
}
|
||||
|
||||
public override void Remove(string jsonPayload)
|
||||
{
|
||||
var payloads = DeserializeFromJsonPayload(jsonPayload);
|
||||
|
||||
payloads.ForEach(payload =>
|
||||
{
|
||||
GetCacheKeysForAlias(payload.Alias).ForEach(
|
||||
alias =>
|
||||
ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(alias));
|
||||
});
|
||||
{
|
||||
GetCacheKeysForAlias(payload.Alias).ForEach(
|
||||
alias =>
|
||||
ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(alias));
|
||||
});
|
||||
|
||||
base.Remove(jsonPayload);
|
||||
base.Refresh(jsonPayload);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -95,12 +95,6 @@ namespace Umbraco.Web.Cache
|
||||
base.Refresh(jsonPayload);
|
||||
}
|
||||
|
||||
public override void Remove(string jsonPayload)
|
||||
{
|
||||
ClearCache(DeserializeFromJsonPayload(jsonPayload));
|
||||
base.Remove(jsonPayload);
|
||||
}
|
||||
|
||||
public override void Refresh(int id)
|
||||
{
|
||||
ClearCache(FromMedia(ApplicationContext.Current.Services.MediaService.GetById(id)));
|
||||
|
||||
@@ -265,6 +265,7 @@
|
||||
<Compile Include="CacheHelperExtensions.cs" />
|
||||
<Compile Include="Cache\ContentTypeCacheRefresher.cs" />
|
||||
<Compile Include="Cache\DataTypeCacheRefresher.cs" />
|
||||
<Compile Include="Cache\DictionaryCacheRefresher.cs" />
|
||||
<Compile Include="Cache\DistributedCache.cs" />
|
||||
<Compile Include="Cache\DistributedCacheExtensions.cs" />
|
||||
<Compile Include="Cache\CacheRefresherEventHandler.cs" />
|
||||
|
||||
@@ -132,30 +132,6 @@ namespace umbraco.presentation.webservices
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes objects using the passed in Json payload, it will be up to the cache refreshers to deserialize
|
||||
/// </summary>
|
||||
/// <param name="uniqueIdentifier"></param>
|
||||
/// <param name="jsonPayload">A custom JSON payload used by the cache refresher</param>
|
||||
/// <param name="Login"></param>
|
||||
/// <param name="Password"></param>
|
||||
/// <remarks>
|
||||
/// NOTE: the cache refresher defined by the ID MUST be of type IJsonCacheRefresher or an exception will be thrown
|
||||
/// </remarks>
|
||||
[WebMethod]
|
||||
public void RemoveByJson(Guid uniqueIdentifier, string jsonPayload, string Login, string Password)
|
||||
{
|
||||
if (BusinessLogic.User.validateCredentials(Login, Password))
|
||||
{
|
||||
var cr = CacheRefreshersResolver.Current.GetById(uniqueIdentifier) as IJsonCacheRefresher;
|
||||
if (cr == null)
|
||||
{
|
||||
throw new InvalidOperationException("The cache refresher: " + uniqueIdentifier + " is not of type " + typeof(IJsonCacheRefresher));
|
||||
}
|
||||
cr.Remove(jsonPayload);
|
||||
}
|
||||
}
|
||||
|
||||
[WebMethod]
|
||||
public void RemoveById(Guid uniqueIdentifier, int Id, string Login, string Password) {
|
||||
|
||||
|
||||
@@ -245,9 +245,7 @@ namespace umbraco.cms.businesslogic
|
||||
|
||||
private List<int> m_AllowedChildContentTypeIDs = null;
|
||||
private List<TabI> m_VirtualTabs = null;
|
||||
|
||||
private static readonly object propertyTypesCacheSyncLock = new object();
|
||||
|
||||
|
||||
protected internal IContentTypeComposition ContentTypeItem;
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -59,6 +59,16 @@ namespace umbraco.cms.businesslogic
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used by the cache refreshers to clear the cache on distributed servers
|
||||
/// </summary>
|
||||
internal static void ClearCache()
|
||||
{
|
||||
DictionaryItems.Clear();
|
||||
//ensure the flag is reset so that EnsureCache will re-cache everything
|
||||
_cacheIsEnsured = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve a list of toplevel DictionaryItems
|
||||
/// </summary>
|
||||
@@ -159,7 +169,8 @@ namespace umbraco.cms.businesslogic
|
||||
/// </summary>
|
||||
public bool IsTopMostItem()
|
||||
{
|
||||
return DictionaryItems.Values.Cast<DictionaryItem>()
|
||||
EnsureCache();
|
||||
return DictionaryItems.Values
|
||||
.Where(x => x.id == id)
|
||||
.Select(x => x.ParentId)
|
||||
.SingleOrDefault() == TopLevelParent;
|
||||
@@ -172,9 +183,10 @@ namespace umbraco.cms.businesslogic
|
||||
{
|
||||
get
|
||||
{
|
||||
EnsureCache();
|
||||
if (_parent == null)
|
||||
{
|
||||
var p = DictionaryItems.Values.Cast<DictionaryItem>().SingleOrDefault(x => x.UniqueId == this.ParentId);
|
||||
var p = DictionaryItems.Values.SingleOrDefault(x => x.UniqueId == this.ParentId);
|
||||
|
||||
if (p == null)
|
||||
{
|
||||
@@ -203,7 +215,8 @@ namespace umbraco.cms.businesslogic
|
||||
{
|
||||
get
|
||||
{
|
||||
return DictionaryItems.Values.Cast<DictionaryItem>()
|
||||
EnsureCache();
|
||||
return DictionaryItems.Values
|
||||
.Where(x => x.ParentId == this.UniqueId).OrderBy(item => item.key)
|
||||
.ToArray();
|
||||
}
|
||||
@@ -219,7 +232,8 @@ namespace umbraco.cms.businesslogic
|
||||
{
|
||||
get
|
||||
{
|
||||
return (SqlHelper.ExecuteScalar<int>("select count([key]) as tmp from cmsDictionary where parent=@uniqueId", SqlHelper.CreateParameter("@uniqueId", UniqueId)) > 0);
|
||||
EnsureCache();
|
||||
return DictionaryItems.Values.Any(x => x.ParentId == UniqueId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,11 +251,7 @@ namespace umbraco.cms.businesslogic
|
||||
{
|
||||
SqlHelper.ExecuteNonQuery("Update cmsDictionary set [key] = @key WHERE pk = @Id", SqlHelper.CreateParameter("@key", value),
|
||||
SqlHelper.CreateParameter("@Id", id));
|
||||
|
||||
//remove the cached item since the key is different
|
||||
DictionaryItem val;
|
||||
DictionaryItems.TryRemove(key, out val);
|
||||
|
||||
|
||||
using (IRecordsReader dr =
|
||||
SqlHelper.ExecuteReader("Select pk, id, [key], parent from cmsDictionary where id=@id",
|
||||
SqlHelper.CreateParameter("@id", this.UniqueId)))
|
||||
@@ -253,8 +263,6 @@ namespace umbraco.cms.businesslogic
|
||||
dr.GetString("key"),
|
||||
dr.GetGuid("id"),
|
||||
dr.GetGuid("parent"));
|
||||
|
||||
DictionaryItems.AddOrUpdate(item.key, s => item, (s, dictionaryItem) => item);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -284,13 +292,13 @@ namespace umbraco.cms.businesslogic
|
||||
|
||||
public void setValue(int languageId, string value)
|
||||
{
|
||||
// Calling Save method triggers the Saving event
|
||||
Save();
|
||||
|
||||
if (Item.hasText(UniqueId, languageId))
|
||||
Item.setText(languageId, UniqueId, value);
|
||||
else
|
||||
Item.addText(languageId, UniqueId, value);
|
||||
|
||||
// Calling Save method triggers the Saving event
|
||||
Save();
|
||||
}
|
||||
|
||||
public string Value()
|
||||
@@ -309,13 +317,13 @@ namespace umbraco.cms.businesslogic
|
||||
/// <param name="value"></param>
|
||||
public void setValue(string value)
|
||||
{
|
||||
// Calling Save method triggers the Saving event
|
||||
Save();
|
||||
|
||||
if (Item.hasText(UniqueId, 0))
|
||||
Item.setText(0, UniqueId, value);
|
||||
else
|
||||
Item.addText(0, UniqueId, value);
|
||||
|
||||
// Calling Save method triggers the Saving event
|
||||
Save();
|
||||
}
|
||||
|
||||
public static int addKey(string key, string defaultValue, string parentKey)
|
||||
@@ -352,12 +360,12 @@ namespace umbraco.cms.businesslogic
|
||||
// remove key from database
|
||||
SqlHelper.ExecuteNonQuery("delete from cmsDictionary where [key] = @key", SqlHelper.CreateParameter("@key", key));
|
||||
|
||||
// Remove key from cache
|
||||
DictionaryItem val;
|
||||
DictionaryItems.TryRemove(key, out val);
|
||||
OnDeleted(EventArgs.Empty);
|
||||
}
|
||||
|
||||
[Obsolete("Does not save the dictionary item, use setValue() instead.")]
|
||||
/// <summary>
|
||||
/// ensures events fire after setting proeprties
|
||||
/// </summary>
|
||||
public void Save()
|
||||
{
|
||||
OnSaving(EventArgs.Empty);
|
||||
@@ -467,9 +475,7 @@ namespace umbraco.cms.businesslogic
|
||||
dr.GetString("key"),
|
||||
dr.GetGuid("id"),
|
||||
dr.GetGuid("parent"));
|
||||
|
||||
DictionaryItems.AddOrUpdate(item.key, s => item, (s, dictionaryItem) => item);
|
||||
|
||||
|
||||
item.setValue(defaultValue);
|
||||
|
||||
item.OnNew(EventArgs.Empty);
|
||||
@@ -514,6 +520,13 @@ namespace umbraco.cms.businesslogic
|
||||
if (Deleting != null)
|
||||
Deleting(this, e);
|
||||
}
|
||||
|
||||
public static event DeleteEventHandler Deleted;
|
||||
protected virtual void OnDeleted(EventArgs e)
|
||||
{
|
||||
if (Deleted != null)
|
||||
Deleted(this, e);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,14 +10,13 @@ using System.Collections.Generic;
|
||||
namespace umbraco.cms.businesslogic.language
|
||||
{
|
||||
/// <summary>
|
||||
/// Item class contains method for accessing language translated text, its a generic component which
|
||||
/// can be used for storing language translated content, all items are associated to an unique identifier (Guid)
|
||||
///
|
||||
/// The data is cached and are usable in the public website.
|
||||
///
|
||||
/// Primarily used by the built-in dictionary
|
||||
///
|
||||
/// THIS CLASS IS NOT INTENDED TO BE USED DIRECTLY IN YOUR CODE, USE THE umbraco.cms.businesslogic.Dictionary class instead
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class is used by the DictionaryItem, all caching is handled in the DictionaryItem.Save() method which will ensure that
|
||||
/// cache is invalidated if anything is changed.
|
||||
/// </remarks>
|
||||
[Obsolete("THIS CLASS IS NOT INTENDED TO BE USED DIRECTLY IN YOUR CODE, USE THE umbraco.cms.businesslogic.Dictionary class instead")]
|
||||
public class Item
|
||||
{
|
||||
private static readonly ConcurrentDictionary<Guid, Dictionary<int, string>> Items = new ConcurrentDictionary<Guid, Dictionary<int, string>>();
|
||||
@@ -36,7 +35,7 @@ namespace umbraco.cms.businesslogic.language
|
||||
/// <summary>
|
||||
/// Populates the global hash table with the data from the database.
|
||||
/// </summary>
|
||||
private static void EnsureData()
|
||||
private static void EnsureCache()
|
||||
{
|
||||
if (!_isInitialize)
|
||||
{
|
||||
@@ -54,7 +53,16 @@ namespace umbraco.cms.businesslogic.language
|
||||
var uniqueId = dr.GetGuid("UniqueId");
|
||||
var text = dr.GetString("value");
|
||||
|
||||
UpdateCache(languageId, uniqueId, text);
|
||||
Items.AddOrUpdate(uniqueId, guid =>
|
||||
{
|
||||
var languagevalues = new Dictionary<int, string> { { languageId, text } };
|
||||
return languagevalues;
|
||||
}, (guid, dictionary) =>
|
||||
{
|
||||
// add/update the text for the id
|
||||
dictionary[languageId] = text;
|
||||
return dictionary;
|
||||
});
|
||||
}
|
||||
}
|
||||
_isInitialize = true;
|
||||
@@ -64,20 +72,16 @@ namespace umbraco.cms.businesslogic.language
|
||||
}
|
||||
}
|
||||
|
||||
private static void UpdateCache(int languageId, Guid key, string text)
|
||||
/// <summary>
|
||||
/// Clears the cache, this is used for cache refreshers to ensure that the cache is up to date across all servers
|
||||
/// </summary>
|
||||
internal static void ClearCache()
|
||||
{
|
||||
Items.AddOrUpdate(key, guid =>
|
||||
{
|
||||
var languagevalues = new Dictionary<int, string> {{languageId, text}};
|
||||
return languagevalues;
|
||||
}, (guid, dictionary) =>
|
||||
{
|
||||
// add/update the text for the id
|
||||
dictionary[languageId] = text;
|
||||
return dictionary;
|
||||
});
|
||||
Items.Clear();
|
||||
//reset the flag so that we re-lookup the cache
|
||||
_isInitialize = false;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the value of a languagetranslated item given the key
|
||||
/// </summary>
|
||||
@@ -86,7 +90,7 @@ namespace umbraco.cms.businesslogic.language
|
||||
/// <returns>The language translated text</returns>
|
||||
public static string Text(Guid key, int languageId)
|
||||
{
|
||||
EnsureData();
|
||||
EnsureCache();
|
||||
|
||||
Dictionary<int, string> val;
|
||||
if (Items.TryGetValue(key, out val))
|
||||
@@ -104,7 +108,7 @@ namespace umbraco.cms.businesslogic.language
|
||||
/// <returns>returns True if there is a value associated to the unique identifier with the specified language</returns>
|
||||
public static bool hasText(Guid key, int languageId)
|
||||
{
|
||||
EnsureData();
|
||||
EnsureCache();
|
||||
|
||||
Dictionary<int, string> val;
|
||||
if (Items.TryGetValue(key, out val))
|
||||
@@ -124,16 +128,12 @@ namespace umbraco.cms.businesslogic.language
|
||||
|
||||
public static void setText(int languageId, Guid key, string value)
|
||||
{
|
||||
EnsureData();
|
||||
|
||||
if (!hasText(key, languageId)) throw new ArgumentException("Key does not exist");
|
||||
|
||||
SqlHelper.ExecuteNonQuery("Update cmsLanguageText set [value] = @value where LanguageId = @languageId And UniqueId = @key",
|
||||
SqlHelper.CreateParameter("@value", value),
|
||||
SqlHelper.CreateParameter("@languageId", languageId),
|
||||
SqlHelper.CreateParameter("@key", key));
|
||||
|
||||
UpdateCache(languageId, key, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -145,16 +145,12 @@ namespace umbraco.cms.businesslogic.language
|
||||
/// <param name="value"></param>
|
||||
public static void addText(int languageId, Guid key, string value)
|
||||
{
|
||||
EnsureData();
|
||||
|
||||
if (hasText(key, languageId)) throw new ArgumentException("Key being add'ed already exists");
|
||||
|
||||
SqlHelper.ExecuteNonQuery("Insert Into cmsLanguageText (languageId,UniqueId,[value]) values (@languageId, @key, @value)",
|
||||
SqlHelper.CreateParameter("@languageId", languageId),
|
||||
SqlHelper.CreateParameter("@key", key),
|
||||
SqlHelper.CreateParameter("@value", value));
|
||||
|
||||
UpdateCache(languageId, key, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -163,15 +159,9 @@ namespace umbraco.cms.businesslogic.language
|
||||
/// <param name="key">Unique identifier</param>
|
||||
public static void removeText(Guid key)
|
||||
{
|
||||
EnsureData();
|
||||
|
||||
// remove from database
|
||||
SqlHelper.ExecuteNonQuery("Delete from cmsLanguageText where UniqueId = @key",
|
||||
SqlHelper.CreateParameter("@key", key));
|
||||
|
||||
// remove from cache
|
||||
Dictionary<int, string> val;
|
||||
Items.TryRemove(key, out val);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -181,25 +171,10 @@ namespace umbraco.cms.businesslogic.language
|
||||
/// <param name="languageId"></param>
|
||||
public static void RemoveByLanguage(int languageId)
|
||||
{
|
||||
EnsureData();
|
||||
|
||||
// remove from database
|
||||
SqlHelper.ExecuteNonQuery("Delete from cmsLanguageText where languageId = @languageId",
|
||||
SqlHelper.CreateParameter("@languageId", languageId));
|
||||
|
||||
//we need to lock here because the inner dictionary is not concurrent, seems overkill to have a nested concurrent dictionary
|
||||
lock (Locker)
|
||||
{
|
||||
//delete all of the items belonging to the language
|
||||
foreach (var entry in Items.Values)
|
||||
{
|
||||
if (entry.ContainsKey(languageId))
|
||||
{
|
||||
entry.Remove(languageId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,29 +82,31 @@ namespace umbraco.cms.businesslogic.language
|
||||
/// Creates a new language given the culture code - ie. da-dk (denmark)
|
||||
/// </summary>
|
||||
/// <param name="cultureCode">Culturecode of the language</param>
|
||||
[MethodImpl(MethodImplOptions.Synchronized)]
|
||||
public static void MakeNew(string cultureCode)
|
||||
{
|
||||
var culture = GetCulture(cultureCode);
|
||||
if (culture != null)
|
||||
lock (Locker)
|
||||
{
|
||||
//insert it
|
||||
SqlHelper.ExecuteNonQuery(
|
||||
"insert into umbracoLanguage (languageISOCode) values (@CultureCode)",
|
||||
SqlHelper.CreateParameter("@CultureCode", cultureCode));
|
||||
|
||||
//get it's id
|
||||
var newId = SqlHelper.ExecuteScalar<int>("SELECT MAX(id) FROM umbracoLanguage WHERE languageISOCode=@cultureCode", SqlHelper.CreateParameter("@cultureCode", cultureCode));
|
||||
|
||||
//load it and raise events
|
||||
using (var dr = SqlHelper.ExecuteReader(string.Format("{0} where id = {1}", m_SQLOptimizedGetAll, newId)))
|
||||
var culture = GetCulture(cultureCode);
|
||||
if (culture != null)
|
||||
{
|
||||
while (dr.Read())
|
||||
//insert it
|
||||
SqlHelper.ExecuteNonQuery(
|
||||
"insert into umbracoLanguage (languageISOCode) values (@CultureCode)",
|
||||
SqlHelper.CreateParameter("@CultureCode", cultureCode));
|
||||
|
||||
//get it's id
|
||||
var newId = SqlHelper.ExecuteScalar<int>("SELECT MAX(id) FROM umbracoLanguage WHERE languageISOCode=@cultureCode", SqlHelper.CreateParameter("@cultureCode", cultureCode));
|
||||
|
||||
//load it and raise events
|
||||
using (var dr = SqlHelper.ExecuteReader(string.Format("{0} where id = {1}", m_SQLOptimizedGetAll, newId)))
|
||||
{
|
||||
|
||||
var ct = new Language();
|
||||
ct.PopulateFromReader(dr);
|
||||
ct.OnNew(new NewEventArgs());
|
||||
while (dr.Read())
|
||||
{
|
||||
|
||||
var ct = new Language();
|
||||
ct.PopulateFromReader(dr);
|
||||
ct.OnNew(new NewEventArgs());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,7 +330,7 @@
|
||||
<Compile Include="businesslogic\language\Language.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="businesslogic\language\Text.cs">
|
||||
<Compile Include="businesslogic\language\Item.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="businesslogic\macro\Macro.cs">
|
||||
|
||||
Reference in New Issue
Block a user