diff --git a/src/Umbraco.Core/Cache/IJsonCacheRefresher.cs b/src/Umbraco.Core/Cache/IJsonCacheRefresher.cs new file mode 100644 index 0000000000..c72c2569a1 --- /dev/null +++ b/src/Umbraco.Core/Cache/IJsonCacheRefresher.cs @@ -0,0 +1,13 @@ +using umbraco.interfaces; + +namespace Umbraco.Core.Cache +{ + /// + /// A cache refresher that supports refreshing or removing cache based on a custom Json payload + /// + interface IJsonCacheRefresher : ICacheRefresher + { + void Refresh(string jsonPayload); + void Remove(string jsonPayload); + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/ContentTypeBase.cs b/src/Umbraco.Core/Models/ContentTypeBase.cs index 419e265e7f..0159621a9e 100644 --- a/src/Umbraco.Core/Models/ContentTypeBase.cs +++ b/src/Umbraco.Core/Models/ContentTypeBase.cs @@ -22,8 +22,8 @@ namespace Umbraco.Core.Models private string _alias; private string _description; private int _sortOrder; - private string _icon; - private string _thumbnail; + private string _icon = "folder.png"; + private string _thumbnail = "folder.png"; private int _creatorId; private bool _allowedAsRoot; private bool _isContainer; diff --git a/src/Umbraco.Core/Models/ContentTypeExtensions.cs b/src/Umbraco.Core/Models/ContentTypeExtensions.cs index b328cb5e0b..df8c09b3d1 100644 --- a/src/Umbraco.Core/Models/ContentTypeExtensions.cs +++ b/src/Umbraco.Core/Models/ContentTypeExtensions.cs @@ -1,16 +1,17 @@ using System.Collections.Generic; using System.Linq; +using Umbraco.Core.Services; namespace Umbraco.Core.Models { - public static class ContentTypeExtensions + internal static class ContentTypeExtensions { /// /// Get all descendant content types /// /// /// - public static IEnumerable Descendants(this IContentType contentType) + public static IEnumerable Descendants(this IContentTypeBase contentType) { var contentTypeService = ApplicationContext.Current.Services.ContentTypeService; var descendants = contentTypeService.GetContentTypeChildren(contentType.Id) @@ -23,13 +24,21 @@ namespace Umbraco.Core.Models /// /// /// - public static IEnumerable DescendantsAndSelf(this IContentType contentType) + public static IEnumerable DescendantsAndSelf(this IContentTypeBase contentType) { - var contentTypeService = ApplicationContext.Current.Services.ContentTypeService; - var descendants = contentTypeService.GetContentTypeChildren(contentType.Id) - .FlattenList(type => contentTypeService.GetContentTypeChildren(type.Id)); var descendantsAndSelf = new[] { contentType }.Concat(contentType.Descendants()); return descendantsAndSelf; } + + ///// + ///// Returns the descendant content type Ids for the given content type + ///// + ///// + ///// + //public static IEnumerable DescendantIds(this IContentTypeBase contentType) + //{ + // return ((ContentTypeService) ApplicationContext.Current.Services.ContentTypeService) + // .GetDescendantContentTypeIds(contentType.Id); + //} } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/EntityBase/Entity.cs b/src/Umbraco.Core/Models/EntityBase/Entity.cs index 3c58b61695..f5b228aad6 100644 --- a/src/Umbraco.Core/Models/EntityBase/Entity.cs +++ b/src/Umbraco.Core/Models/EntityBase/Entity.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using System.Reflection; using System.Runtime.Serialization; namespace Umbraco.Core.Models.EntityBase @@ -16,6 +17,15 @@ namespace Umbraco.Core.Models.EntityBase private int? _hash; private int _id; private Guid _key; + private DateTime _createDate; + private DateTime _updateDate; + + private static readonly PropertyInfo IdSelector = ExpressionHelper.GetPropertyInfo(x => x.Id); + private static readonly PropertyInfo KeySelector = ExpressionHelper.GetPropertyInfo(x => x.Key); + private static readonly PropertyInfo CreateDateSelector = ExpressionHelper.GetPropertyInfo(x => x.CreateDate); + private static readonly PropertyInfo UpdateDateSelector = ExpressionHelper.GetPropertyInfo(x => x.UpdateDate); + private static readonly PropertyInfo HasIdentitySelector = ExpressionHelper.GetPropertyInfo(x => x.HasIdentity); + /// /// Integer Id @@ -29,8 +39,12 @@ namespace Umbraco.Core.Models.EntityBase } set { - _id = value; - HasIdentity = true; + SetPropertyValueAndDetectChanges(o => + { + _id = value; + HasIdentity = true; //set the has Identity + return _id; + }, _id, IdSelector); } } @@ -49,20 +63,49 @@ namespace Umbraco.Core.Models.EntityBase return _key; } - set { _key = value; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _key = value; + return _key; + }, _key, KeySelector); + } } /// /// Gets or sets the Created Date /// [DataMember] - public DateTime CreateDate { get; set; } + public DateTime CreateDate + { + get { return _createDate; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _createDate = value; + return _createDate; + }, _createDate, CreateDateSelector); + } + } /// /// Gets or sets the Modified Date /// [DataMember] - public DateTime UpdateDate { get; set; } + public DateTime UpdateDate + { + get { return _updateDate; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _updateDate = value; + return _updateDate; + }, _updateDate, UpdateDateSelector); + } + } internal virtual void ResetIdentity() { @@ -98,7 +141,11 @@ namespace Umbraco.Core.Models.EntityBase } protected set { - _hasIdentity = value; + SetPropertyValueAndDetectChanges(o => + { + _hasIdentity = value; + return _hasIdentity; + }, _hasIdentity, HasIdentitySelector); } } diff --git a/src/Umbraco.Core/Models/File.cs b/src/Umbraco.Core/Models/File.cs index 7fefddfaed..978e54390f 100644 --- a/src/Umbraco.Core/Models/File.cs +++ b/src/Umbraco.Core/Models/File.cs @@ -14,7 +14,7 @@ namespace Umbraco.Core.Models public abstract class File : Entity, IFile { private string _path; - private string _content; + private string _content = string.Empty; //initialize to empty string, not null protected File(string path) { diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs index f577e0f9db..179db898df 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs @@ -21,7 +21,7 @@ namespace Umbraco.Core.Persistence.Repositories /// /// internal abstract class ContentTypeBaseRepository : PetaPocoRepositoryBase - where TEntity : IContentTypeComposition + where TEntity : class, IContentTypeComposition { protected ContentTypeBaseRepository(IDatabaseUnitOfWork work) : base(work) diff --git a/src/Umbraco.Core/Persistence/Repositories/PetaPocoRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/PetaPocoRepositoryBase.cs index f286392e87..a08dec6c76 100644 --- a/src/Umbraco.Core/Persistence/Repositories/PetaPocoRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/PetaPocoRepositoryBase.cs @@ -13,7 +13,7 @@ namespace Umbraco.Core.Persistence.Repositories /// /// internal abstract class PetaPocoRepositoryBase : RepositoryBase - where TEntity : IAggregateRoot + where TEntity : class, IAggregateRoot { protected PetaPocoRepositoryBase(IDatabaseUnitOfWork work) : base(work) diff --git a/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs index 33b4ad392b..c7c5b236e6 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs @@ -14,7 +14,7 @@ namespace Umbraco.Core.Persistence.Repositories /// Type of entity for which the repository is used /// Type of the Id used for this entity internal abstract class RepositoryBase : DisposableObject, IRepositoryQueryable, IUnitOfWorkRepository - where TEntity : IAggregateRoot + where TEntity : class, IAggregateRoot { private readonly IUnitOfWork _work; private readonly IRepositoryCacheProvider _cache; @@ -98,13 +98,17 @@ namespace Umbraco.Core.Persistence.Repositories _cache.Save(typeof(TEntity), entity); } - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 - var asEntity = entity as Entity; - if (asEntity != null) + if (entity != null) { - asEntity.ResetDirtyProperties(false); + //on initial construction we don't want to have dirty properties tracked + // http://issues.umbraco.org/issue/U4-1946 + Entity asEntity = entity as Entity; + if (asEntity != null) + { + asEntity.ResetDirtyProperties(false); + } } + return entity; } diff --git a/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs index 15266aae16..6364a106fb 100644 --- a/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs @@ -11,7 +11,7 @@ using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Persistence.Repositories { internal abstract class VersionableRepositoryBase : PetaPocoRepositoryBase - where TEntity : IAggregateRoot + where TEntity : class, IAggregateRoot { protected VersionableRepositoryBase(IDatabaseUnitOfWork work) : base(work) { diff --git a/src/Umbraco.Core/Services/ContentTypeService.cs b/src/Umbraco.Core/Services/ContentTypeService.cs index 3c81b7c80a..6afc43d9d8 100644 --- a/src/Umbraco.Core/Services/ContentTypeService.cs +++ b/src/Umbraco.Core/Services/ContentTypeService.cs @@ -9,6 +9,7 @@ using Umbraco.Core.Events; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.EntityBase; +using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.UnitOfWork; @@ -101,6 +102,26 @@ namespace Umbraco.Core.Services } } + ///// + ///// Returns the content type descendant Ids for the content type specified + ///// + ///// + ///// + //internal IEnumerable GetDescendantContentTypeIds(int contentTypeId) + //{ + // using (var uow = _uowProvider.GetUnitOfWork()) + // { + // //method to return the child content type ids for the id specified + // Func getChildIds = + // parentId => + // uow.Database.Fetch("WHERE parentContentTypeId = @Id", new {Id = parentId}) + // .Select(x => x.ChildId).ToArray(); + + // //recursively get all descendant ids + // return getChildIds(contentTypeId).FlattenList(getChildIds); + // } + //} + /// /// Checks whether an item has any children /// @@ -120,20 +141,23 @@ namespace Umbraco.Core.Services /// This is called after an IContentType is saved and is used to update the content xml structures in the database /// if they are required to be updated. /// - /// + /// A tuple of a content type and a boolean indicating if it is new (HasIdentity was false before committing) private void UpdateContentXmlStructure(params IContentType[] contentTypes) { - var toUpdate = new List(); + var toUpdate = new List(); foreach (var contentType in contentTypes) { //we need to determine if we need to refresh the xml content in the database. This is to be done when: + // - the item is not new (already existed in the db) // - a content type changes it's alias // - if a content type has it's property removed //here we need to check if the alias of the content type changed or if one of the properties was removed. var dirty = contentType as IRememberBeingDirty; - if (dirty != null && (dirty.WasPropertyDirty("Alias") || dirty.WasPropertyDirty("HasPropertyTypeBeenRemoved"))) + if (dirty != null + && !dirty.WasPropertyDirty("HasIdentity") //ensure it's now 'new' + && (dirty.WasPropertyDirty("Alias") || dirty.WasPropertyDirty("HasPropertyTypeBeenRemoved"))) { //if the alias was changed then we only need to update the xml structures for content of the current content type. //if a property was deleted then we need to update the xml structures for any content of the current content type @@ -145,7 +169,7 @@ namespace Umbraco.Core.Services } else { - //if a property was deleted (and maybe the alias changed too), the update all content of the current content type + //if a property was deleted (and maybe the alias changed too), then update all content of the current content type // and all of it's desscendant doc types. toUpdate.AddRange(contentType.DescendantsAndSelf()); } @@ -207,7 +231,6 @@ namespace Umbraco.Core.Services using (new WriteLock(Locker)) { - var uow = _uowProvider.GetUnitOfWork(); using (var repository = _repositoryFactory.CreateContentTypeRepository(uow)) { diff --git a/src/Umbraco.Core/Sync/DefaultServerMessenger.cs b/src/Umbraco.Core/Sync/DefaultServerMessenger.cs index f637fd995d..973bbb055d 100644 --- a/src/Umbraco.Core/Sync/DefaultServerMessenger.cs +++ b/src/Umbraco.Core/Sync/DefaultServerMessenger.cs @@ -56,6 +56,15 @@ namespace Umbraco.Core.Sync _getUserNamePasswordDelegate = getUserNamePasswordDelegate; } + public void PerformRefresh(IEnumerable 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.RefreshByJson, jsonPayload: jsonPayload); + } + public void PerformRefresh(IEnumerable servers, ICacheRefresher refresher,Func getNumericId, params T[] instances) { if (servers == null) throw new ArgumentNullException("servers"); @@ -82,6 +91,15 @@ namespace Umbraco.Core.Sync instances); } + public void PerformRemove(IEnumerable 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(IEnumerable servers, ICacheRefresher refresher, Func getNumericId, params T[] instances) { if (servers == null) throw new ArgumentNullException("servers"); @@ -100,7 +118,7 @@ namespace Umbraco.Core.Sync if (servers == null) throw new ArgumentNullException("servers"); if (refresher == null) throw new ArgumentNullException("refresher"); - MessageSeversForManyIds(servers, refresher, MessageType.RemoveById, numericIds.Cast()); + MessageSeversForIdsOrJson(servers, refresher, MessageType.RemoveById, numericIds.Cast()); } public void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, params int[] numericIds) @@ -108,7 +126,7 @@ namespace Umbraco.Core.Sync if (servers == null) throw new ArgumentNullException("servers"); if (refresher == null) throw new ArgumentNullException("refresher"); - MessageSeversForManyIds(servers, refresher, MessageType.RefreshById, numericIds.Cast()); + MessageSeversForIdsOrJson(servers, refresher, MessageType.RefreshById, numericIds.Cast()); } public void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, params Guid[] guidIds) @@ -116,12 +134,12 @@ namespace Umbraco.Core.Sync if (servers == null) throw new ArgumentNullException("servers"); if (refresher == null) throw new ArgumentNullException("refresher"); - MessageSeversForManyIds(servers, refresher, MessageType.RefreshById, guidIds.Cast()); + MessageSeversForIdsOrJson(servers, refresher, MessageType.RefreshById, guidIds.Cast()); } public void PerformRefreshAll(IEnumerable servers, ICacheRefresher refresher) { - MessageSeversForManyIds(servers, refresher, MessageType.RefreshAll, Enumerable.Empty().ToArray()); + MessageSeversForIdsOrJson(servers, refresher, MessageType.RefreshAll, Enumerable.Empty().ToArray()); } private void InvokeMethodOnRefresherInstance(ICacheRefresher refresher, MessageType dispatchType, Func getId, IEnumerable instances) @@ -216,7 +234,7 @@ namespace Umbraco.Core.Sync } } - private void InvokeMethodOnRefresherInstance(ICacheRefresher refresher, MessageType dispatchType, IEnumerable ids) + private void InvokeMethodOnRefresherInstance(ICacheRefresher refresher, MessageType dispatchType, IEnumerable ids = null, string jsonPayload = null) { if (refresher == null) throw new ArgumentNullException("refresher"); @@ -227,35 +245,55 @@ namespace Umbraco.Core.Sync } else { - foreach (var id in ids) + if (ids != null) { + foreach (var id in ids) + { + //if we are not, then just invoke the call on the cache refresher + switch (dispatchType) + { + case MessageType.RefreshById: + if (id is int) + { + refresher.Refresh((int) id); + } + else if (id is Guid) + { + refresher.Refresh((Guid) id); + } + else + { + throw new InvalidOperationException("The id must be either an int or a Guid"); + } + + break; + case MessageType.RemoveById: + refresher.Remove((int) id); + break; + } + } + } + else + { + //we can only proceed if the cache refresher is IJsonCacheRefresher! + var jsonRefresher = refresher as IJsonCacheRefresher; + if (jsonRefresher == null) + { + throw new InvalidOperationException("The cache refresher " + refresher.GetType() + " is not of type " + typeof(IJsonCacheRefresher)); + } + //if we are not, then just invoke the call on the cache refresher switch (dispatchType) - { - case MessageType.RefreshById: - if (id is int) - { - refresher.Refresh((int)id); - } - else if (id is Guid) - { - refresher.Refresh((Guid)id); - } - else - { - throw new InvalidOperationException("The id must be either an int or a Guid"); - } - + { + case MessageType.RefreshByJson: + jsonRefresher.Refresh(jsonPayload); break; - case MessageType.RemoveById: - refresher.Remove((int)id); + case MessageType.RemoveByJson: + jsonRefresher.Remove(jsonPayload); break; } } } - - - } private void MessageSeversForManyObjects( @@ -268,8 +306,6 @@ namespace Umbraco.Core.Sync if (servers == null) throw new ArgumentNullException("servers"); if (refresher == null) throw new ArgumentNullException("refresher"); - EnsureLazyUsernamePasswordDelegateResolved(); - //Now, check if we are using Distrubuted calls. If there are no servers in the list then we // can definitely not distribute. if (!_useDistributedCalls || !servers.Any()) @@ -280,36 +316,42 @@ namespace Umbraco.Core.Sync } //if we are distributing calls then we'll need to do it by id - MessageSeversForManyIds(servers, refresher, dispatchType, instances.Select(getId)); + MessageSeversForIdsOrJson(servers, refresher, dispatchType, instances.Select(getId)); } - private void MessageSeversForManyIds( + private void MessageSeversForIdsOrJson( IEnumerable servers, ICacheRefresher refresher, MessageType dispatchType, - IEnumerable ids) + IEnumerable ids = null, + string jsonPayload = null) { if (servers == null) throw new ArgumentNullException("servers"); if (refresher == null) throw new ArgumentNullException("refresher"); Type arrayType = null; - foreach (var id in ids) + if (ids != null) { - if (!(id is int) && (!(id is Guid))) - throw new ArgumentException("The id must be either an int or a Guid"); - if (arrayType == null) - arrayType = id.GetType(); - if (arrayType != id.GetType()) - throw new ArgumentException("The array must contain the same type of " + arrayType); + foreach (var id in ids) + { + if (!(id is int) && (!(id is Guid))) + throw new ArgumentException("The id must be either an int or a Guid"); + if (arrayType == null) + arrayType = id.GetType(); + if (arrayType != id.GetType()) + throw new ArgumentException("The array must contain the same type of " + arrayType); + } } - + //Now, check if we are using Distrubuted calls. If there are no servers in the list then we // can definitely not distribute. if (!_useDistributedCalls || !servers.Any()) { //if we are not, then just invoke the call on the cache refresher - InvokeMethodOnRefresherInstance(refresher, dispatchType, ids); + InvokeMethodOnRefresherInstance(refresher, dispatchType, ids, jsonPayload); return; } + + EnsureLazyUsernamePasswordDelegateResolved(); //We are using distributed calls, so lets make them... try @@ -334,6 +376,16 @@ namespace Umbraco.Core.Sync // Add the returned WaitHandle to the list for later checking switch (dispatchType) { + case MessageType.RefreshByJson: + 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; case MessageType.RefreshAll: asyncResultsList.Add( cacheRefresher.BeginRefreshAll( @@ -383,6 +435,12 @@ namespace Umbraco.Core.Sync // Find out if the call succeeded switch (dispatchType) { + case MessageType.RefreshByJson: + cacheRefresher.EndRefreshByJson(t); + break; + case MessageType.RemoveByJson: + cacheRefresher.EndRemoveByJson(t); + break; case MessageType.RefreshAll: cacheRefresher.EndRefreshAll(t); break; diff --git a/src/Umbraco.Core/Sync/IServerMessenger.cs b/src/Umbraco.Core/Sync/IServerMessenger.cs index b7321e5996..b39a6367db 100644 --- a/src/Umbraco.Core/Sync/IServerMessenger.cs +++ b/src/Umbraco.Core/Sync/IServerMessenger.cs @@ -9,6 +9,17 @@ namespace Umbraco.Core.Sync /// internal interface IServerMessenger { + + /// + /// Performs a refresh and sends along the JSON payload to each server + /// + /// + /// + /// + /// A pre-formatted custom json payload to be sent to the servers, the cache refresher will deserialize and use to refresh cache + /// + void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, string jsonPayload); + /// /// Performs a sync against all instance objects /// @@ -29,6 +40,16 @@ namespace Umbraco.Core.Sync /// void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, Func getGuidId, params T[] instances); + /// + /// Performs a remove and sends along the JSON payload to each server + /// + /// + /// + /// + /// A pre-formatted custom json payload to be sent to the servers, the cache refresher will deserialize and use to remove cache + /// + void PerformRemove(IEnumerable servers, ICacheRefresher refresher, string jsonPayload); + /// /// Removes the cache for the specified items /// diff --git a/src/Umbraco.Core/Sync/MessageType.cs b/src/Umbraco.Core/Sync/MessageType.cs index c4c66e99e2..8b0b69a026 100644 --- a/src/Umbraco.Core/Sync/MessageType.cs +++ b/src/Umbraco.Core/Sync/MessageType.cs @@ -7,6 +7,8 @@ { RefreshAll, RefreshById, - RemoveById + RefreshByJson, + RemoveById, + RemoveByJson } } \ No newline at end of file diff --git a/src/Umbraco.Core/Sync/ServerSyncWebServiceClient.cs b/src/Umbraco.Core/Sync/ServerSyncWebServiceClient.cs index ca2add64d4..c45ceace23 100644 --- a/src/Umbraco.Core/Sync/ServerSyncWebServiceClient.cs +++ b/src/Umbraco.Core/Sync/ServerSyncWebServiceClient.cs @@ -46,6 +46,60 @@ namespace Umbraco.Core.Sync this.EndInvoke(asyncResult); } + /// + [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}); + } + + /// + 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); + } + + /// + public void EndRemoveByJson(System.IAsyncResult asyncResult) + { + this.EndInvoke(asyncResult); + } + + /// + [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) + { + this.Invoke("RefreshByJson", new object[] { + uniqueIdentifier, + jsonPayload, + Login, + Password}); + } + + /// + public System.IAsyncResult BeginRefreshByJson(System.Guid uniqueIdentifier, string jsonPayload, string Login, string Password, System.AsyncCallback callback, object asyncState) + { + return this.BeginInvoke("RefreshByJson", new object[] { + uniqueIdentifier, + jsonPayload, + Login, + Password}, callback, asyncState); + } + + /// + public void EndRefreshByJson(System.IAsyncResult asyncResult) + { + this.EndInvoke(asyncResult); + } + /// [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://umbraco.org/webservices/RefreshByGuid", 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 RefreshByGuid(System.Guid uniqueIdentifier, System.Guid Id, string Login, string Password) diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 930c94dc72..06999311dc 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -109,6 +109,7 @@ + diff --git a/src/Umbraco.Tests/Sync/DistributedCacheTests.cs b/src/Umbraco.Tests/Sync/DistributedCacheTests.cs index cc8fc0fc76..5e667645b9 100644 --- a/src/Umbraco.Tests/Sync/DistributedCacheTests.cs +++ b/src/Umbraco.Tests/Sync/DistributedCacheTests.cs @@ -132,8 +132,15 @@ namespace Umbraco.Tests.Sync public List IntIdsRefreshed = new List(); public List GuidIdsRefreshed = new List(); public List IntIdsRemoved = new List(); + public List PayloadsRemoved = new List(); + public List PayloadsRefreshed = new List(); public int CountOfFullRefreshes = 0; - + + + public void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, string jsonPayload) + { + PayloadsRefreshed.Add(jsonPayload); + } public void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, Func getNumericId, params T[] instances) { @@ -145,6 +152,11 @@ namespace Umbraco.Tests.Sync GuidIdsRefreshed.AddRange(instances.Select(getGuidId)); } + public void PerformRemove(IEnumerable servers, ICacheRefresher refresher, string jsonPayload) + { + PayloadsRemoved.Add(jsonPayload); + } + public void PerformRemove(IEnumerable servers, ICacheRefresher refresher, Func getNumericId, params T[] instances) { IntIdsRemoved.AddRange(instances.Select(getNumericId)); diff --git a/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs b/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs index 73fe852abe..1b4ff703d6 100644 --- a/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs +++ b/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs @@ -21,8 +21,6 @@ namespace Umbraco.Web.Cache { protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext) { - if (UmbracoSettings.UmbracoLibraryCacheDuration <= 0) return; - //Bind to content events - currently used for: // - macro clearing // - clearing the xslt cache (MS.Internal.Xml.XPath.XPathSelectionIterator) @@ -97,7 +95,7 @@ namespace Umbraco.Web.Cache /// static void ContentTypeServiceSavedMediaType(IContentTypeService sender, Core.Events.SaveEventArgs e) { - e.SavedEntities.ForEach(x => DistributedCache.Instance.RemoveMediaTypeCache(x)); + e.SavedEntities.ForEach(x => DistributedCache.Instance.RefreshMediaTypeCache(x)); } /// @@ -107,7 +105,7 @@ namespace Umbraco.Web.Cache /// static void ContentTypeServiceSavedContentType(IContentTypeService sender, Core.Events.SaveEventArgs e) { - e.SavedEntities.ForEach(contentType => DistributedCache.Instance.RemoveContentTypeCache(contentType)); + e.SavedEntities.ForEach(contentType => DistributedCache.Instance.RefreshContentTypeCache(contentType)); } /// diff --git a/src/Umbraco.Web/Cache/ContentTypeCacheRefresher.cs b/src/Umbraco.Web/Cache/ContentTypeCacheRefresher.cs index 4f8aed80d8..ecf7a8d03f 100644 --- a/src/Umbraco.Web/Cache/ContentTypeCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/ContentTypeCacheRefresher.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Web.Script.Serialization; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Models; @@ -20,8 +21,90 @@ namespace Umbraco.Web.Cache /// /// This is not intended to be used directly in your code /// - public sealed class ContentTypeCacheRefresher : ICacheRefresher, ICacheRefresher + public sealed class ContentTypeCacheRefresher : IJsonCacheRefresher { + + #region Static helpers + + /// + /// Converts the json to a JsonPayload object + /// + /// + /// + private static JsonPayload[] DeserializeFromJsonPayload(string json) + { + var serializer = new JavaScriptSerializer(); + var jsonObject = serializer.Deserialize(json); + return jsonObject; + } + + /// + /// Converts a content type to a jsonPayload object + /// + /// + /// if the item was deleted + /// + private static JsonPayload FromContentType(IContentTypeBase contentType, bool isDeleted = false) + { + var payload = new JsonPayload + { + Alias = contentType.Alias, + Id = contentType.Id, + PropertyTypeIds = contentType.PropertyTypes.Select(x => x.Id).ToArray(), + //either IContentType or IMediaType + Type = (contentType is IContentType) ? typeof(IContentType).Name : typeof(IMediaType).Name, + DescendantPayloads = contentType.Descendants().Select(x => FromContentType(x)).ToArray(), + WasDeleted = isDeleted + }; + //here we need to check if the alias of the content type changed or if one of the properties was removed. + var dirty = contentType as IRememberBeingDirty; + if (dirty != null) + { + payload.PropertyRemoved = dirty.WasPropertyDirty("HasPropertyTypeBeenRemoved"); + payload.AliasChanged = dirty.WasPropertyDirty("Alias"); + payload.IsNew = dirty.WasPropertyDirty("HasIdentity"); + } + return payload; + } + + /// + /// Creates the custom Json payload used to refresh cache amongst the servers + /// + /// specify false if this is an update, otherwise true if it is a deletion + /// + /// + internal static string SerializeToJsonPayload(bool isDeleted, params IContentTypeBase[] contentTypes) + { + var serializer = new JavaScriptSerializer(); + var items = contentTypes.Select(x => FromContentType(x, isDeleted)).ToArray(); + var json = serializer.Serialize(items); + return json; + } + + #endregion + + #region Sub classes + + private class JsonPayload + { + public JsonPayload() + { + WasDeleted = false; + IsNew = false; + } + public string Alias { get; set; } + public int Id { get; set; } + public int[] PropertyTypeIds { get; set; } + public string Type { get; set; } + public bool AliasChanged { get; set; } + public bool PropertyRemoved { get; set; } + public JsonPayload[] DescendantPayloads { get; set; } + public bool WasDeleted { get; set; } + public bool IsNew { get; set; } + } + + #endregion + public Guid UniqueIdentifier { get { return new Guid(DistributedCache.ContentTypeCacheRefresherId); } @@ -45,42 +128,42 @@ namespace Umbraco.Web.Cache public void Refresh(int id) { - ClearContentTypeCache(id); + ClearContentTypeCache(false, id); } public void Remove(int id) { - ClearContentTypeCache(id); + ClearContentTypeCache(true, id); } public void Refresh(Guid id) { + } + + /// + /// Refreshes the cache using the custom jsonPayload provided + /// + /// + public void Refresh(string jsonPayload) + { + var payload = DeserializeFromJsonPayload(jsonPayload); + ClearContentTypeCache(payload); } - public void Refresh(IContentType instance) + /// + /// Removes the cache using the custom jsonPayload provided + /// + /// + public void Remove(string jsonPayload) { - ClearContentTypeCache(instance); - } - - public void Remove(IContentType instance) - { - ClearContentTypeCache(instance); - } - - public void Refresh(IMediaType instance) - { - ClearContentTypeCache(instance); - } - - public void Remove(IMediaType instance) - { - ClearContentTypeCache(instance); + var payload = DeserializeFromJsonPayload(jsonPayload); + ClearContentTypeCache(payload); } /// /// This clears out all cache associated with a content type /// - /// + /// /// /// The cache that is required to be cleared when a content type is updated is as follows: /// - ApplicationCache (keys to clear): @@ -95,26 +178,23 @@ namespace Umbraco.Web.Cache /// 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. /// - private static void ClearContentTypeCache(params IContentTypeBase[] contentTypes) + private static void ClearContentTypeCache(IEnumerable payloads) { var needsContentRefresh = false; - contentTypes.ForEach(contentType => + payloads.ForEach(payload => { //clear the cache for each item - ClearContentTypeCache(contentType); - - //we only need to do this for IContentType NOT for IMediaType, we don't want to refresh the whole cache - //if a media type has changed. - if (contentType is IContentType) + ClearContentTypeCache(payload); + + //we only need to do this for IContentType NOT for IMediaType, we don't want to refresh the whole cache. + //if the item was deleted or the alias changed or property removed then we need to refresh the content. + //and, don't refresh the cache if it is new. + if (payload.Type == typeof(IContentType).Name + && !payload.IsNew + && (payload.WasDeleted || payload.AliasChanged || payload.PropertyRemoved)) { - //here we need to check if the alias of the content type changed or if one of the properties was removed. - var dirty = contentType as IRememberBeingDirty; - if (dirty == null) return; - if (dirty.WasPropertyDirty("Alias") || dirty.WasPropertyDirty("HasPropertyTypeBeenRemoved")) - { - needsContentRefresh = true; - } + needsContentRefresh = true; } }); @@ -126,14 +206,16 @@ namespace Umbraco.Web.Cache } //clear the cache providers if there were any content types to clear - if (contentTypes.Any()) + if (payloads.Any()) { InMemoryCacheProvider.Current.Clear(); RuntimeCacheProvider.Current.Clear(); //we only need to do this for IContentType NOT for IMediaType, we don't want to refresh the whole routes //cache if only a media type has changed. - if (contentTypes.Any(x => x is IContentType)) + //we don't want to update the routes cache if all of the content types here are new. + if (payloads.Any(x => x.Type == typeof(IContentType).Name) + && !payloads.All(x => x.IsNew)) //if they are all new then don't proceed { //we need to clear the routes cache here! //TODO: Is there a better way to handle this without casting ? @@ -149,7 +231,7 @@ namespace Umbraco.Web.Cache /// /// Clears cache for an individual IContentTypeBase object /// - /// + /// /// /// See notes for the other overloaded ClearContentTypeCache for /// full details on clearing cache. @@ -157,44 +239,43 @@ namespace Umbraco.Web.Cache /// /// Return true if the alias of the content type changed /// - private static void ClearContentTypeCache(IContentTypeBase contentType) + private static void ClearContentTypeCache(JsonPayload payload) { //clears the cache for each property type associated with the content type - foreach (var p in contentType.PropertyTypes) + foreach (var pid in payload.PropertyTypeIds) { - ApplicationContext.Current.ApplicationCache.ClearCacheItem(CacheKeys.PropertyTypeCacheKey + p.Id); + ApplicationContext.Current.ApplicationCache.ClearCacheItem(CacheKeys.PropertyTypeCacheKey + pid); } //clears the cache associated with the Content type itself - ApplicationContext.Current.ApplicationCache.ClearCacheItem(string.Format("{0}{1}", CacheKeys.ContentTypeCacheKey, contentType.Id)); + ApplicationContext.Current.ApplicationCache.ClearCacheItem(string.Format("{0}{1}", CacheKeys.ContentTypeCacheKey, payload.Id)); //clears the cache associated with the content type properties collection - ApplicationContext.Current.ApplicationCache.ClearCacheItem(CacheKeys.ContentTypePropertiesCacheKey + contentType.Id); + ApplicationContext.Current.ApplicationCache.ClearCacheItem(CacheKeys.ContentTypePropertiesCacheKey + payload.Id); //clears the dictionary object cache of the legacy ContentType - global::umbraco.cms.businesslogic.ContentType.RemoveFromDataTypeCache(contentType.Alias); + global::umbraco.cms.businesslogic.ContentType.RemoveFromDataTypeCache(payload.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("WHERE parentContentTypeId = @Id", new { Id = contentType.Id }); - foreach (var dto in dtos) + foreach (var descendant in payload.DescendantPayloads) { - ClearContentTypeCache(dto.ChildId); + ClearContentTypeCache(descendant); } } /// /// Clears the cache for any content type with the specified Ids /// + /// true if the entity was deleted, false if it is just an update /// - private static void ClearContentTypeCache(params int[] ids) + private static void ClearContentTypeCache(bool isDeleted, params int[] ids) { ClearContentTypeCache( ids.Select( x => ApplicationContext.Current.Services.ContentTypeService.GetContentType(x) as IContentTypeBase) .WhereNotNull() + .Select(x => FromContentType(x, isDeleted)) .ToArray()); } - } } diff --git a/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs b/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs index e108c3d5cd..9d1cd2efeb 100644 --- a/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs +++ b/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs @@ -12,6 +12,7 @@ namespace Umbraco.Web.Cache /// public static class DistributedCacheExtensions { + #region User cache public static void RemoveUserCache(this DistributedCache dc, int userId) { dc.Remove(new Guid(DistributedCache.UserCacheRefresherId), userId); @@ -20,8 +21,10 @@ namespace Umbraco.Web.Cache public static void RefreshUserCache(this DistributedCache dc, int userId) { dc.Refresh(new Guid(DistributedCache.UserCacheRefresherId), userId); - } + } + #endregion + #region Template cache /// /// Refreshes the cache amongst servers for a template /// @@ -40,8 +43,10 @@ namespace Umbraco.Web.Cache public static void RemoveTemplateCache(this DistributedCache dc, int templateId) { dc.Remove(new Guid(DistributedCache.TemplateRefresherId), templateId); - } + } + #endregion + #region Page cache /// /// Refreshes the cache amongst servers for all pages /// @@ -59,7 +64,7 @@ namespace Umbraco.Web.Cache public static void RefreshPageCache(this DistributedCache dc, int documentId) { dc.Refresh(new Guid(DistributedCache.PageCacheRefresherId), documentId); - } + } /// /// Refreshes page cache for all instances passed in @@ -89,8 +94,10 @@ namespace Umbraco.Web.Cache public static void RemovePageCache(this DistributedCache dc, int documentId) { dc.Remove(new Guid(DistributedCache.PageCacheRefresherId), documentId); - } + } + #endregion + #region Member cache /// /// Refreshes the cache amongst servers for a member /// @@ -109,8 +116,10 @@ namespace Umbraco.Web.Cache public static void RemoveMemberCache(this DistributedCache dc, int memberId) { dc.Remove(new Guid(DistributedCache.MemberCacheRefresherId), memberId); - } + } + #endregion + #region Media Cache /// /// Refreshes the cache amongst servers for a media item /// @@ -149,8 +158,21 @@ namespace Umbraco.Web.Cache public static void RemoveMediaCache(this DistributedCache dc, params IMedia[] media) { dc.Remove(new Guid(DistributedCache.MediaCacheRefresherId), x => x.Id, media); - } + } + #endregion + #region Macro Cache + + /// + /// Clears the cache for all macros on the current server + /// + /// + public static void ClearAllMacroCacheOnCurrentServer(this DistributedCache dc) + { + //NOTE: The 'false' ensure that it will only refresh on the current server, not post to all servers + dc.RefreshAll(new Guid(DistributedCache.MacroCacheRefresherId), false); + } + /// /// Refreshes the cache amongst servers for a macro item /// @@ -182,7 +204,7 @@ namespace Umbraco.Web.Cache public static void RemoveMacroCache(this DistributedCache dc, int macroId) { dc.Remove(new Guid(DistributedCache.MacroCacheRefresherId), macroId); - } + } /// /// Removes the cache amongst servers for a macro item @@ -208,6 +230,39 @@ namespace Umbraco.Web.Cache { dc.Remove(new Guid(DistributedCache.MacroCacheRefresherId), macro1 => macro1.Model.Id, macro); } + } + #endregion + + #region Content type cache + + /// + /// Remove all cache for a given content type + /// + /// + /// + public static void RefreshContentTypeCache(this DistributedCache dc, IContentType contentType) + { + if (contentType != null) + { + //dc.Refresh(new Guid(DistributedCache.ContentTypeCacheRefresherId), x => x.Id, contentType); + dc.RefreshByJson(new Guid(DistributedCache.ContentTypeCacheRefresherId), + ContentTypeCacheRefresher.SerializeToJsonPayload(false, contentType)); + } + } + + /// + /// Remove all cache for a given media type + /// + /// + /// + public static void RefreshMediaTypeCache(this DistributedCache dc, IMediaType mediaType) + { + if (mediaType != null) + { + //dc.Refresh(new Guid(DistributedCache.ContentTypeCacheRefresherId), x => x.Id, mediaType); + dc.RefreshByJson(new Guid(DistributedCache.ContentTypeCacheRefresherId), + ContentTypeCacheRefresher.SerializeToJsonPayload(false, mediaType)); + } } /// @@ -219,7 +274,9 @@ namespace Umbraco.Web.Cache { if (contentType != null) { - dc.Remove(new Guid(DistributedCache.ContentTypeCacheRefresherId), x => x.Id, contentType); + //dc.Remove(new Guid(DistributedCache.ContentTypeCacheRefresherId), x => x.Id, contentType); + dc.RemoveByJson(new Guid(DistributedCache.ContentTypeCacheRefresherId), + ContentTypeCacheRefresher.SerializeToJsonPayload(true, contentType)); } } @@ -232,19 +289,12 @@ namespace Umbraco.Web.Cache { if (mediaType != null) { - dc.Remove(new Guid(DistributedCache.ContentTypeCacheRefresherId), x => x.Id, mediaType); + //dc.Remove(new Guid(DistributedCache.ContentTypeCacheRefresherId), x => x.Id, mediaType); + dc.RemoveByJson(new Guid(DistributedCache.ContentTypeCacheRefresherId), + ContentTypeCacheRefresher.SerializeToJsonPayload(true, mediaType)); } - } - - /// - /// Clears the cache for all macros on the current server - /// - /// - public static void ClearAllMacroCacheOnCurrentServer(this DistributedCache dc) - { - //NOTE: The 'false' ensure that it will only refresh on the current server, not post to all servers - dc.RefreshAll(new Guid(DistributedCache.MacroCacheRefresherId), false); - } + } + #endregion public static void ClearXsltCacheOnCurrentServer(this DistributedCache dc) { diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/nodetypeTasks.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/create/nodetypeTasks.cs index 16658ff59a..8bf4cfa489 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/nodetypeTasks.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/create/nodetypeTasks.cs @@ -2,6 +2,7 @@ using System; using System.Data; using System.Web.Security; using Umbraco.Core; +using Umbraco.Core.Models; using umbraco.BusinessLogic; using umbraco.DataLayer; using umbraco.BasePages; @@ -43,26 +44,27 @@ namespace umbraco public bool Save() { - var dt = cms.businesslogic.web.DocumentType.MakeNew(BusinessLogic.User.GetUser(_userID), Alias.Replace("'", "''")); - dt.IconUrl = "folder.gif"; - + //NOTE: TypeID is the parent id! + //NOTE: ParentID is aparently a flag to determine if we are to create a template! Hack much ?! :P + var contentType = new ContentType(TypeID != 0 ? TypeID : -1) + { + CreatorId = _userID, + Alias = Alias.Replace("'", "''"), + Icon = "folder.gif", + Name = Alias.Replace("'", "''") + }; // Create template? if (ParentID == 1) { - cms.businesslogic.template.Template[] t = { cms.businesslogic.template.Template.MakeNew(_alias, BusinessLogic.User.GetUser(_userID)) }; - dt.allowedTemplates = t; - dt.DefaultTemplate = t[0].Id; + var template = new Template(string.Empty, _alias, _alias); + ApplicationContext.Current.Services.FileService.SaveTemplate(template, _userID); + + contentType.AllowedTemplates = new[] {template}; + contentType.DefaultTemplateId = template.Id; } + ApplicationContext.Current.Services.ContentTypeService.Save(contentType); - // Master Content Type? - if (TypeID != 0) - { - dt.MasterContentType = TypeID; - } - - dt.Save(); - - m_returnUrl = "settings/editNodeTypeNew.aspx?id=" + dt.Id.ToString(); + m_returnUrl = "settings/editNodeTypeNew.aspx?id=" + contentType.Id.ToString(); return true; } @@ -77,13 +79,6 @@ namespace umbraco return false; } - public nodetypeTasks() - { - // - // TODO: Add constructor logic here - // - } - #region ITaskReturnUrl Members private string m_returnUrl = ""; public string ReturnUrl 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 23f43d7685..2be399f850 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/CacheRefresher.asmx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/CacheRefresher.asmx.cs @@ -9,6 +9,7 @@ using System.Web.Script.Serialization; using System.Web.Services; using System.Xml; using Umbraco.Core; +using Umbraco.Core.Cache; namespace umbraco.presentation.webservices { @@ -107,6 +108,54 @@ namespace umbraco.presentation.webservices } } + /// + /// Refreshes objects using the passed in Json payload, it will be up to the cache refreshers to deserialize + /// + /// + /// A custom JSON payload used by the cache refresher + /// + /// + /// + /// NOTE: the cache refresher defined by the ID MUST be of type IJsonCacheRefresher or an exception will be thrown + /// + [WebMethod] + public void RefreshByJson(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.Refresh(jsonPayload); + } + } + + /// + /// Removes objects using the passed in Json payload, it will be up to the cache refreshers to deserialize + /// + /// + /// A custom JSON payload used by the cache refresher + /// + /// + /// + /// NOTE: the cache refresher defined by the ID MUST be of type IJsonCacheRefresher or an exception will be thrown + /// + [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) {