From d0cb27d1c1e1a0fb193b0c96dfcd2bc3ad4fd5a2 Mon Sep 17 00:00:00 2001 From: Shannon Date: Sun, 8 Jun 2014 12:49:20 +0200 Subject: [PATCH] Completes: U4-2633 Bundle all cache refresher transmissions into a single call per request for much better performance --- .../Sync/DefaultServerMessenger.cs | 3 +- src/Umbraco.Core/Sync/RefreshInstruction.cs | 63 ++++ .../Sync/ServerSyncWebServiceClient.cs | 26 +- src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../config/umbracoSettings.config | 3 +- src/Umbraco.Web/BatchedServerMessenger.cs | 271 ++++++++++++++++++ src/Umbraco.Web/Cache/DistributedCache.cs | 31 +- src/Umbraco.Web/Umbraco.Web.csproj | 1 + src/Umbraco.Web/UmbracoModule.cs | 12 +- src/Umbraco.Web/WebBootManager.cs | 3 +- .../webservices/CacheRefresher.asmx.cs | 133 ++++++--- 11 files changed, 492 insertions(+), 55 deletions(-) create mode 100644 src/Umbraco.Core/Sync/RefreshInstruction.cs create mode 100644 src/Umbraco.Web/BatchedServerMessenger.cs diff --git a/src/Umbraco.Core/Sync/DefaultServerMessenger.cs b/src/Umbraco.Core/Sync/DefaultServerMessenger.cs index 66609d5c56..f6704dd413 100644 --- a/src/Umbraco.Core/Sync/DefaultServerMessenger.cs +++ b/src/Umbraco.Core/Sync/DefaultServerMessenger.cs @@ -4,6 +4,7 @@ using System.Globalization; using System.Linq; using System.Net; using System.Threading; +using System.Web; using System.Web.Script.Serialization; using Umbraco.Core.Cache; using Umbraco.Core.Configuration; @@ -520,7 +521,7 @@ namespace Umbraco.Core.Sync } } - private IEnumerable GetAsyncResults(List asyncResultsList, out List waitHandlesList) + internal IEnumerable GetAsyncResults(List asyncResultsList, out List waitHandlesList) { var asyncResults = asyncResultsList.ToArray(); waitHandlesList = new List(); diff --git a/src/Umbraco.Core/Sync/RefreshInstruction.cs b/src/Umbraco.Core/Sync/RefreshInstruction.cs new file mode 100644 index 0000000000..867266085b --- /dev/null +++ b/src/Umbraco.Core/Sync/RefreshInstruction.cs @@ -0,0 +1,63 @@ +using System; + +namespace Umbraco.Core.Sync +{ + [Serializable] + public class RefreshInstruction + { + public RefreshMethodType RefreshType { get; set; } + public Guid RefresherId { get; set; } + public Guid GuidId { get; set; } + public int IntId { get; set; } + public string JsonIds { get; set; } + public string JsonPayload { get; set; } + + [Serializable] + public enum RefreshMethodType + { + RefreshAll, + RefreshByGuid, + RefreshById, + RefreshByIds, + RefreshByJson, + RemoveById + } + + protected bool Equals(RefreshInstruction other) + { + return RefreshType == other.RefreshType && RefresherId.Equals(other.RefresherId) && GuidId.Equals(other.GuidId) && IntId == other.IntId && string.Equals(JsonIds, other.JsonIds) && string.Equals(JsonPayload, other.JsonPayload); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((RefreshInstruction) obj); + } + + public override int GetHashCode() + { + unchecked + { + int hashCode = (int) RefreshType; + hashCode = (hashCode*397) ^ RefresherId.GetHashCode(); + hashCode = (hashCode*397) ^ GuidId.GetHashCode(); + hashCode = (hashCode*397) ^ IntId; + hashCode = (hashCode*397) ^ (JsonIds != null ? JsonIds.GetHashCode() : 0); + hashCode = (hashCode*397) ^ (JsonPayload != null ? JsonPayload.GetHashCode() : 0); + return hashCode; + } + } + + public static bool operator ==(RefreshInstruction left, RefreshInstruction right) + { + return Equals(left, right); + } + + public static bool operator !=(RefreshInstruction left, RefreshInstruction right) + { + return !Equals(left, right); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Sync/ServerSyncWebServiceClient.cs b/src/Umbraco.Core/Sync/ServerSyncWebServiceClient.cs index dd7d0db01d..923ed3c670 100644 --- a/src/Umbraco.Core/Sync/ServerSyncWebServiceClient.cs +++ b/src/Umbraco.Core/Sync/ServerSyncWebServiceClient.cs @@ -4,7 +4,6 @@ using Umbraco.Core.IO; namespace Umbraco.Core.Sync { - /// /// The client Soap service for making distrubuted cache calls between servers /// @@ -21,6 +20,31 @@ namespace Umbraco.Core.Sync } + /// + [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://umbraco.org/webservices/BulkRefresh", 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 BulkRefresh(RefreshInstruction[] instructions, string login, string password) + { + this.Invoke("BulkRefresh", new object[] { + instructions, + login, + password}); + } + + /// + public System.IAsyncResult BeginBulkRefresh(RefreshInstruction[] instructions, string login, string password, System.AsyncCallback callback, object asyncState) + { + return this.BeginInvoke("BulkRefresh", new object[] { + instructions, + login, + password}, callback, asyncState); + } + + /// + public void EndBulkRefresh(System.IAsyncResult asyncResult) + { + this.EndInvoke(asyncResult); + } + /// [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://umbraco.org/webservices/RefreshAll", 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 RefreshAll(System.Guid uniqueIdentifier, string Login, string Password) diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 815fe33ac4..89752d47ed 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -830,6 +830,7 @@ + diff --git a/src/Umbraco.Web.UI/config/umbracoSettings.config b/src/Umbraco.Web.UI/config/umbracoSettings.config index a089b97608..ff888ac14b 100644 --- a/src/Umbraco.Web.UI/config/umbracoSettings.config +++ b/src/Umbraco.Web.UI/config/umbracoSettings.config @@ -224,7 +224,7 @@ - + 0 @@ -233,6 +233,7 @@ + localhost diff --git a/src/Umbraco.Web/BatchedServerMessenger.cs b/src/Umbraco.Web/BatchedServerMessenger.cs new file mode 100644 index 0000000000..669c1de817 --- /dev/null +++ b/src/Umbraco.Web/BatchedServerMessenger.cs @@ -0,0 +1,271 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Web; +using System.Web.Script.Serialization; +using System.Web.UI.WebControls; +using Umbraco.Core.Cache; +using Umbraco.Core.Logging; +using Umbraco.Core.Sync; +using umbraco.interfaces; + +namespace Umbraco.Web +{ + internal class BatchedServerMessenger : DefaultServerMessenger + { + internal BatchedServerMessenger() + { + UmbracoModule.EndRequest += UmbracoModule_EndRequest; + } + + internal BatchedServerMessenger(string login, string password) : base(login, password) + { + UmbracoModule.EndRequest += UmbracoModule_EndRequest; + } + + internal BatchedServerMessenger(string login, string password, bool useDistributedCalls) : base(login, password, useDistributedCalls) + { + UmbracoModule.EndRequest += UmbracoModule_EndRequest; + } + + public BatchedServerMessenger(Func> getUserNamePasswordDelegate) : base(getUserNamePasswordDelegate) + { + UmbracoModule.EndRequest += UmbracoModule_EndRequest; + } + + void UmbracoModule_EndRequest(object sender, EventArgs e) + { + if (HttpContext.Current == null) + { + return; + } + + var items = HttpContext.Current.Items[typeof(BatchedServerMessenger).Name] as List; + if (items != null) + { + var copied = new Message[items.Count]; + items.CopyTo(copied); + //now set to null so it get's cleaned up on this request + HttpContext.Current.Items[typeof (BatchedServerMessenger).Name] = null; + + SendMessages(copied); + } + } + + private class Message + { + public IEnumerable Servers { get; set; } + public ICacheRefresher Refresher { get; set; } + public MessageType DispatchType { get; set; } + public IEnumerable Ids { get; set; } + public Type IdArrayType { get; set; } + public string JsonPayload { get; set; } + } + + protected override void PerformDistributedCall( + IEnumerable servers, + ICacheRefresher refresher, + MessageType dispatchType, + IEnumerable ids = null, + Type idArrayType = null, + string jsonPayload = null) + { + //NOTE: we use UmbracoContext instead of HttpContext.Current because when some web methods run async, the + // HttpContext.Current is null but the UmbracoContext.Current won't be since we manually assign it. + if (UmbracoContext.Current == null || UmbracoContext.Current.HttpContext == null) + { + throw new NotSupportedException("This messenger cannot execute without a valid/current UmbracoContext with an HttpContext assigned"); + } + + if (UmbracoContext.Current.HttpContext.Items[typeof(BatchedServerMessenger).Name] == null) + { + UmbracoContext.Current.HttpContext.Items[typeof(BatchedServerMessenger).Name] = new List(); + } + var list = (List)UmbracoContext.Current.HttpContext.Items[typeof(BatchedServerMessenger).Name]; + + list.Add(new Message + { + DispatchType = dispatchType, + IdArrayType = idArrayType, + Ids = ids, + JsonPayload = jsonPayload, + Refresher = refresher, + Servers = servers + }); + } + + private RefreshInstruction[] ConvertToInstruction(Message msg) + { + switch (msg.DispatchType) + { + case MessageType.RefreshAll: + return new[] + { + new RefreshInstruction + { + RefreshType = RefreshInstruction.RefreshMethodType.RefreshAll, + RefresherId = msg.Refresher.UniqueIdentifier + } + }; + case MessageType.RefreshById: + if (msg.IdArrayType == null) + { + throw new InvalidOperationException("Cannot refresh by id if the idArrayType is null"); + } + + if (msg.IdArrayType == typeof(int)) + { + var serializer = new JavaScriptSerializer(); + var jsonIds = serializer.Serialize(msg.Ids.Cast().ToArray()); + + return new[] + { + new RefreshInstruction + { + JsonIds = jsonIds, + RefreshType = RefreshInstruction.RefreshMethodType.RefreshByIds, + RefresherId = msg.Refresher.UniqueIdentifier + } + }; + } + + return msg.Ids.Select(x => new RefreshInstruction + { + GuidId = (Guid)x, + RefreshType = RefreshInstruction.RefreshMethodType.RefreshById, + RefresherId = msg.Refresher.UniqueIdentifier + }).ToArray(); + + case MessageType.RefreshByJson: + return new[] + { + new RefreshInstruction + { + RefreshType = RefreshInstruction.RefreshMethodType.RefreshByJson, + RefresherId = msg.Refresher.UniqueIdentifier, + JsonPayload = msg.JsonPayload + } + }; + case MessageType.RemoveById: + return msg.Ids.Select(x => new RefreshInstruction + { + IntId = (int)x, + RefreshType = RefreshInstruction.RefreshMethodType.RemoveById, + RefresherId = msg.Refresher.UniqueIdentifier + }).ToArray(); + case MessageType.RefreshByInstance: + case MessageType.RemoveByInstance: + default: + throw new ArgumentOutOfRangeException(); + } + } + + private void SendMessages(IEnumerable messages) + { + var batchedMsg = new List>(); + foreach (var msg in messages) + { + var instructions = ConvertToInstruction(msg); + batchedMsg.Add(new Tuple(msg, instructions)); + } + + var servers = batchedMsg.SelectMany(x => x.Item1.Servers).Distinct(); + + try + { + + //TODO: We should try to figure out the current server's address and if it matches any of the ones + // in the ServerAddress list, then just refresh directly on this server and exclude that server address + // from the list, this will save an internal request. + + using (var cacheRefresher = new ServerSyncWebServiceClient()) + { + var asyncResultsList = new List(); + + LogStartDispatch(); + + // Go through each configured node submitting a request asynchronously + //NOTE: 'asynchronously' in this case does not mean that it will continue while we give the page back to the user! + foreach (var server in servers) + { + //set the server address + cacheRefresher.Url = server.ServerAddress; + + var instructions = batchedMsg + .Where(x => x.Item1.Servers.Contains(server)) + .SelectMany(x => x.Item2) + //only execute distinct instructions - no sense in running the same one. + .Distinct() + .ToArray(); + + asyncResultsList.Add( + cacheRefresher.BeginBulkRefresh( + instructions, Login, Password, null, null)); + } + + List waitHandlesList; + var asyncResults = GetAsyncResults(asyncResultsList, out waitHandlesList); + + var errorCount = 0; + + // Once for each WaitHandle that we have, wait for a response and log it + // We're previously submitted all these requests effectively in parallel and will now retrieve responses on a FIFO basis + foreach (var t in asyncResults) + { + var handleIndex = WaitHandle.WaitAny(waitHandlesList.ToArray(), TimeSpan.FromSeconds(15)); + try + { + cacheRefresher.EndBulkRefresh(t); + } + catch (WebException ex) + { + LogDispatchNodeError(ex); + errorCount++; + } + catch (Exception ex) + { + LogDispatchNodeError(ex); + errorCount++; + } + } + LogDispatchBatchResult(errorCount); + } + } + catch (Exception ee) + { + LogDispatchBatchError(ee); + } + } + + private void LogDispatchBatchError(Exception ee) + { + LogHelper.Error("Error refreshing distributed list", ee); + } + + private void LogDispatchBatchResult(int errorCount) + { + LogHelper.Debug(string.Format("Distributed server push completed with {0} nodes reporting an error", errorCount == 0 ? "no" : errorCount.ToString(CultureInfo.InvariantCulture))); + } + + private void LogDispatchNodeError(Exception ex) + { + LogHelper.Error("Error refreshing a node in the distributed list", ex); + } + + private void LogDispatchNodeError(WebException ex) + { + string url = (ex.Response != null) ? ex.Response.ResponseUri.ToString() : "invalid url (responseUri null)"; + LogHelper.Error("Error refreshing a node in the distributed list, URI attempted: " + url, ex); + } + + private void LogStartDispatch() + { + LogHelper.Info("Submitting calls to distributed servers"); + } + } +} diff --git a/src/Umbraco.Web/Cache/DistributedCache.cs b/src/Umbraco.Web/Cache/DistributedCache.cs index c993129859..23c371d4e8 100644 --- a/src/Umbraco.Web/Cache/DistributedCache.cs +++ b/src/Umbraco.Web/Cache/DistributedCache.cs @@ -60,23 +60,6 @@ namespace Umbraco.Web.Cache private static readonly DistributedCache InstanceObject = new DistributedCache(); - ///// - ///// Fired when any cache refresher method has fired - ///// - ///// - ///// This could be used by developers to know when a cache refresher has executed on the local server. - ///// Similarly to the content.AfterUpdateDocumentCache which fires locally on each machine. - ///// - //public event EventHandler CacheChanged; - - //private void OnCacheChanged(CacheUpdatedEventArgs args) - //{ - // if (CacheChanged != null) - // { - // CacheChanged(this, args); - // } - //} - /// /// Constructor /// @@ -109,6 +92,8 @@ namespace Umbraco.Web.Cache /// public void Refresh(Guid factoryGuid, Func getNumericId, params T[] instances) { + if (factoryGuid == Guid.Empty || instances.Length == 0 || getNumericId == null) return; + ServerMessengerResolver.Current.Messenger.PerformRefresh( ServerRegistrarResolver.Current.Registrar.Registrations, GetRefresherById(factoryGuid), @@ -124,6 +109,8 @@ namespace Umbraco.Web.Cache /// The id of the node. public void Refresh(Guid factoryGuid, int id) { + if (factoryGuid == Guid.Empty || id == default(int)) return; + ServerMessengerResolver.Current.Messenger.PerformRefresh( ServerRegistrarResolver.Current.Registrar.Registrations, GetRefresherById(factoryGuid), @@ -138,6 +125,8 @@ namespace Umbraco.Web.Cache /// The guid of the node. public void Refresh(Guid factoryGuid, Guid id) { + if (factoryGuid == Guid.Empty || id == Guid.Empty) return; + ServerMessengerResolver.Current.Messenger.PerformRefresh( ServerRegistrarResolver.Current.Registrar.Registrations, GetRefresherById(factoryGuid), @@ -152,6 +141,8 @@ namespace Umbraco.Web.Cache /// public void RefreshByJson(Guid factoryGuid, string jsonPayload) { + if (factoryGuid == Guid.Empty || jsonPayload.IsNullOrWhiteSpace()) return; + ServerMessengerResolver.Current.Messenger.PerformRefresh( ServerRegistrarResolver.Current.Registrar.Registrations, GetRefresherById(factoryGuid), @@ -165,6 +156,8 @@ namespace Umbraco.Web.Cache /// The unique identifier. public void RefreshAll(Guid factoryGuid) { + if (factoryGuid == Guid.Empty) return; + RefreshAll(factoryGuid, true); } @@ -178,6 +171,8 @@ namespace Umbraco.Web.Cache /// public void RefreshAll(Guid factoryGuid, bool allServers) { + if (factoryGuid == Guid.Empty) return; + ServerMessengerResolver.Current.Messenger.PerformRefreshAll( allServers ? ServerRegistrarResolver.Current.Registrar.Registrations @@ -193,6 +188,8 @@ namespace Umbraco.Web.Cache /// The id. public void Remove(Guid factoryGuid, int id) { + if (factoryGuid == Guid.Empty || id == default(int)) return; + ServerMessengerResolver.Current.Messenger.PerformRemove( ServerRegistrarResolver.Current.Registrar.Registrations, GetRefresherById(factoryGuid), diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 581893f2ea..b742f0ad63 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -269,6 +269,7 @@ Properties\SolutionInfo.cs + diff --git a/src/Umbraco.Web/UmbracoModule.cs b/src/Umbraco.Web/UmbracoModule.cs index 48cb9c82d6..9c34af7ab8 100644 --- a/src/Umbraco.Web/UmbracoModule.cs +++ b/src/Umbraco.Web/UmbracoModule.cs @@ -26,7 +26,8 @@ namespace Umbraco.Web // Request.RawUrl is still there // response.Redirect does?! always remap to /vdir?! - public class UmbracoModule : IHttpModule + public class + UmbracoModule : IHttpModule { #region HttpModule event handlers @@ -527,6 +528,8 @@ namespace Umbraco.Web LogHelper.Debug("Total milliseconds for umbraco request to process: " + DateTime.Now.Subtract(UmbracoContext.Current.ObjectCreated).TotalMilliseconds); } + OnEndRequest(new EventArgs()); + DisposeHttpContextItems(httpContext); }; @@ -560,6 +563,13 @@ namespace Umbraco.Web { if (RouteAttempt != null) RouteAttempt(this, args); + } + + internal static event EventHandler EndRequest; + private void OnEndRequest(EventArgs args) + { + if (EndRequest != null) + EndRequest(this, args); } #endregion } diff --git a/src/Umbraco.Web/WebBootManager.cs b/src/Umbraco.Web/WebBootManager.cs index 536eb23b84..05398a708c 100644 --- a/src/Umbraco.Web/WebBootManager.cs +++ b/src/Umbraco.Web/WebBootManager.cs @@ -275,7 +275,8 @@ namespace Umbraco.Web DefaultRenderMvcControllerResolver.Current = new DefaultRenderMvcControllerResolver(typeof(RenderMvcController)); //Override the ServerMessengerResolver to set a username/password for the distributed calls - ServerMessengerResolver.Current.SetServerMessenger(new DefaultServerMessenger(() => + //ServerMessengerResolver.Current.SetServerMessenger(new DefaultServerMessenger(() => + ServerMessengerResolver.Current.SetServerMessenger(new BatchedServerMessenger(() => { //we should not proceed to change this if the app/database is not configured since there will // be no user, plus we don't need to have server messages sent if this is the case. diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/CacheRefresher.asmx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/CacheRefresher.asmx.cs index 4c2f721bd2..cd992fc71f 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/CacheRefresher.asmx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/CacheRefresher.asmx.cs @@ -4,55 +4,106 @@ using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Diagnostics; +using System.Linq; using System.Web; using System.Web.Script.Serialization; using System.Web.Services; using System.Xml; using Umbraco.Core; using Umbraco.Core.Cache; +using Umbraco.Core.Sync; namespace umbraco.presentation.webservices { + /// /// Summary description for CacheRefresher. /// [WebService(Namespace="http://umbraco.org/webservices/")] public class CacheRefresher : WebService - { + { + + [WebMethod] + public void BulkRefresh(RefreshInstruction[] instructions, string login, string password) + { + if (BusinessLogic.User.validateCredentials(login, password) == false) + { + return; + } + + //only execute distinct instructions - no sense in running the same one. + foreach (var instruction in instructions.Distinct()) + { + switch (instruction.RefreshType) + { + case RefreshInstruction.RefreshMethodType.RefreshAll: + RefreshAll(instruction.RefresherId); + break; + case RefreshInstruction.RefreshMethodType.RefreshByGuid: + RefreshByGuid(instruction.RefresherId, instruction.GuidId); + break; + case RefreshInstruction.RefreshMethodType.RefreshById: + RefreshById(instruction.RefresherId, instruction.IntId); + break; + case RefreshInstruction.RefreshMethodType.RefreshByIds: + RefreshByIds(instruction.RefresherId, instruction.JsonIds); + break; + case RefreshInstruction.RefreshMethodType.RefreshByJson: + RefreshByJson(instruction.RefresherId, instruction.JsonPayload); + break; + case RefreshInstruction.RefreshMethodType.RemoveById: + RemoveById(instruction.RefresherId, instruction.IntId); + break; + } + } + } + [WebMethod] public void RefreshAll(Guid uniqueIdentifier, string Login, string Password) { if (BusinessLogic.User.validateCredentials(Login, Password)) { - var cr = CacheRefreshersResolver.Current.GetById(uniqueIdentifier); - cr.RefreshAll(); - + RefreshAll(uniqueIdentifier); } } - [WebMethod] + private void RefreshAll(Guid uniqueIdentifier) + { + var cr = CacheRefreshersResolver.Current.GetById(uniqueIdentifier); + cr.RefreshAll(); + } + + [WebMethod] public void RefreshByGuid(Guid uniqueIdentifier, Guid Id, string Login, string Password) { if (BusinessLogic.User.validateCredentials(Login, Password)) { - var cr = CacheRefreshersResolver.Current.GetById(uniqueIdentifier); - cr.Refresh(Id); - + RefreshByGuid(uniqueIdentifier, Id); } } + private void RefreshByGuid(Guid uniqueIdentifier, Guid Id) + { + var cr = CacheRefreshersResolver.Current.GetById(uniqueIdentifier); + cr.Refresh(Id); + } + [WebMethod] public void RefreshById(Guid uniqueIdentifier, int Id, string Login, string Password) { if (BusinessLogic.User.validateCredentials(Login, Password)) { - var cr = CacheRefreshersResolver.Current.GetById(uniqueIdentifier); - cr.Refresh(Id); - + RefreshById(uniqueIdentifier, Id); } } - /// + private void RefreshById(Guid uniqueIdentifier, int Id) + { + var cr = CacheRefreshersResolver.Current.GetById(uniqueIdentifier); + cr.Refresh(Id); + } + + /// /// Refreshes objects for all Ids matched in the json string /// /// @@ -62,20 +113,25 @@ namespace umbraco.presentation.webservices [WebMethod] public void RefreshByIds(Guid uniqueIdentifier, string jsonIds, string Login, string Password) { - var serializer = new JavaScriptSerializer(); - var ids = serializer.Deserialize(jsonIds); - if (BusinessLogic.User.validateCredentials(Login, Password)) { - var cr = CacheRefreshersResolver.Current.GetById(uniqueIdentifier); - foreach (var i in ids) - { - cr.Refresh(i); - } + RefreshByIds(uniqueIdentifier, jsonIds); } } - /// + private void RefreshByIds(Guid uniqueIdentifier, string jsonIds) + { + var serializer = new JavaScriptSerializer(); + var ids = serializer.Deserialize(jsonIds); + + var cr = CacheRefreshersResolver.Current.GetById(uniqueIdentifier); + foreach (var i in ids) + { + cr.Refresh(i); + } + } + + /// /// Refreshes objects using the passed in Json payload, it will be up to the cache refreshers to deserialize /// /// @@ -90,25 +146,36 @@ namespace umbraco.presentation.webservices { 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.Refresh(jsonPayload); + RefreshByJson(uniqueIdentifier, jsonPayload); } } - [WebMethod] - public void RemoveById(Guid uniqueIdentifier, int Id, string Login, string Password) { + private void RefreshByJson(Guid uniqueIdentifier, string jsonPayload) + { + 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.Refresh(jsonPayload); + } - if (BusinessLogic.User.validateCredentials(Login, Password)) { - var cr = CacheRefreshersResolver.Current.GetById(uniqueIdentifier); - cr.Remove(Id); + [WebMethod] + public void RemoveById(Guid uniqueIdentifier, int Id, string Login, string Password) + { + if (BusinessLogic.User.validateCredentials(Login, Password)) + { + RemoveById(uniqueIdentifier, Id); } } - [WebMethod] + private void RemoveById(Guid uniqueIdentifier, int Id) + { + var cr = CacheRefreshersResolver.Current.GetById(uniqueIdentifier); + cr.Remove(Id); + } + + [WebMethod] public XmlDocument GetRefreshers(string Login, string Password) { if (BusinessLogic.User.validateCredentials(Login, Password))