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:
Shannon Deminick
2013-03-23 01:59:25 +06:00
parent af2693af76
commit e97a01c75c
22 changed files with 219 additions and 247 deletions

View File

@@ -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);
}
}

View File

@@ -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));
}
}
}

View File

@@ -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;

View File

@@ -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>

View File

@@ -9,7 +9,6 @@
RefreshById,
RefreshByJson,
RemoveById,
RemoveByJson,
RefreshByInstance,
RemoveByInstance
}

View File

@@ -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)

View File

@@ -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);

View File

@@ -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)

View File

@@ -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>

View 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);
}
}
}

View File

@@ -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)
{

View File

@@ -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));
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}

View File

@@ -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)));

View File

@@ -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" />

View File

@@ -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) {

View File

@@ -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

View File

@@ -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
}
}

View File

@@ -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);
}
}
}
}
}
}

View File

@@ -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());
}
}
}
}

View File

@@ -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">