/// <summary> /// Process the message from the HTTP POST request and rebuild the search index. /// </summary> /// <param name="message"></param> public static void ProcessMessage(OrganizationServiceCachePluginMessage message, SearchIndexInvalidationData searchIndexInvalidationData = null, OrganizationServiceContext serviceContext = null) { if (!SearchManager.Enabled) { ADXTrace.Instance.TraceInfo(TraceCategory.Application, "Search isn't enabled for the current application."); return; } if (message == null) { ADXTrace.Instance.TraceError(TraceCategory.Application, "Search Index build failure. Plugin Message is null."); return; } SearchProvider provider; var forceBuild = message.Target != null && message.Target.LogicalName == "adx_website"; if (!TryGetSearchProvider(out provider)) { ADXTrace.Instance.TraceError(TraceCategory.Application, "Search Index build failure. Search Provider could not be found."); return; } if (forceBuild || BuildMessages.Contains(message.MessageName, MessageComparer)) { PerformBuild(provider); ADXTrace.Instance.TraceInfo(TraceCategory.Application, "Search Index was successfully built."); return; } IContentMapProvider contentMapProvider = AdxstudioCrmConfigurationManager.CreateContentMapProvider(); if (UpdateMessages.Contains(message.MessageName, MessageComparer)) { if (message.Target == null || string.IsNullOrEmpty(message.Target.LogicalName)) { throw new HttpException((int)HttpStatusCode.BadRequest, string.Format("Message {0} requires an EntityName (entity logical name) parameter.", message.MessageName)); } if (message.Target == null || message.Target.Id == Guid.Empty) { throw new HttpException((int)HttpStatusCode.BadRequest, string.Format("Message {0} requires an ID (entity ID) parameter.", message.MessageName)); } if (FeatureCheckHelper.IsFeatureEnabled(FeatureNames.CmsEnabledSearching)) { if (message.Target.LogicalName == "savedquery") { var cachedSavedQuery = SearchMetadataCache.Instance.SearchSavedQueries.FirstOrDefault(x => x.SavedQueryId == message.Target.Id); if (cachedSavedQuery != null) { // SavedQueryUniqueId is timestamp to verify which change was published and need to invalidate Search Index var actualSavedQueryUniqueId = SearchMetadataCache.Instance.GetSavedQueryUniqueId(message.Target.Id); if (cachedSavedQuery.SavedQueryIdUnique != actualSavedQueryUniqueId) { PerformUpdateAsync(provider, updater => updater.UpdateEntitySet(cachedSavedQuery.EntityName)); SearchMetadataCache.Instance.CompleteMetadataUpdateForSearchSavedQuery(cachedSavedQuery, actualSavedQueryUniqueId); } } } if (message.Target.LogicalName == "adx_webpage" && searchIndexInvalidationData != null) { // bypass cache and go straight to CRM in case cache hasn't been updated yet var response = serviceContext.Execute(new RetrieveRequest { Target = new EntityReference(message.Target.LogicalName, message.Target.Id), ColumnSet = new ColumnSet(new string[] { "adx_parentpageid", "adx_websiteid", "adx_publishingstateid", "adx_partialurl" }) }) as RetrieveResponse; if (response != null && response.Entity != null) { var updatedWebPage = response.Entity; // If the parent page or website change, we need to invalidate that whole section of the web page hierarchy since the web roles // may change, including both root and content pages if MLP is enabled. if (!EntityReferenceEquals(searchIndexInvalidationData.ParentPage, updatedWebPage.GetAttributeValue <EntityReference>("adx_parentpageid")) || !EntityReferenceEquals(searchIndexInvalidationData.Website, updatedWebPage.GetAttributeValue <EntityReference>("adx_websiteid"))) { PerformUpdateAsync(provider, updater => updater.UpdateCmsEntityTree("adx_webpage", message.Target.Id)); } // If the publishing state or partial URL change, this will effect all the content pages and localized entities underneath them // if MLP is enabled. If MLP is disabled, LCID will equal null, and then just all pages/entities underneath this will reindex. else if (!EntityReferenceEquals(searchIndexInvalidationData.PublishingState, updatedWebPage.GetAttributeValue <EntityReference>("adx_publishingstateid")) || searchIndexInvalidationData.PartialUrl != updatedWebPage.GetAttributeValue <string>("adx_partialurl")) { PerformUpdateAsync(provider, updater => updater.UpdateCmsEntityTree("adx_webpage", message.Target.Id, searchIndexInvalidationData.Lcid)); } } } if (message.Target.LogicalName == "adx_webpageaccesscontrolrule_webrole") { contentMapProvider.Using(contentMap => { WebPageAccessControlRuleToWebRoleNode webAccessControlToWebRoleNode; if (contentMap.TryGetValue(new EntityReference(message.Target.LogicalName, message.Target.Id), out webAccessControlToWebRoleNode)) { PerformUpdateAsync(provider, updater => updater.UpdateCmsEntityTree("adx_webpage", webAccessControlToWebRoleNode.WebPageAccessControlRule.WebPage.Id)); } }); } if (message.Target.LogicalName == "adx_webpageaccesscontrolrule") { contentMapProvider.Using(contentMap => { WebPageAccessControlRuleNode webAccessControlNode; if (contentMap.TryGetValue(new EntityReference(message.Target.LogicalName, message.Target.Id), out webAccessControlNode)) { PerformUpdateAsync(provider, updater => updater.UpdateCmsEntityTree("adx_webpage", webAccessControlNode.WebPage.Id)); } }); } if (message.Target.LogicalName == "adx_communityforumaccesspermission") { contentMapProvider.Using(contentMap => { ForumAccessPermissionNode forumAccessNode; if (contentMap.TryGetValue(new EntityReference(message.Target.LogicalName, message.Target.Id), out forumAccessNode)) { PerformUpdateAsync(provider, updater => updater.UpdateCmsEntityTree("adx_communityforum", forumAccessNode.Forum.Id)); } }); } if (message.Target.LogicalName == "connection") { var fetch = new Fetch { Entity = new FetchEntity("connection") { Filters = new[] { new Filter { Conditions = new List <Condition> { new Condition("connectionid", ConditionOperator.Equal, message.Target.Id) } } } } }; var connectionEntity = ((RetrieveSingleResponse)serviceContext.Execute(fetch.ToRetrieveSingleRequest())).Entity; var record1Id = connectionEntity.GetAttributeValue <EntityReference>("record1id"); var record2Id = connectionEntity.GetAttributeValue <EntityReference>("record2id"); // new product association to knowledge article could mean new product filtering rules if (record1Id != null && record1Id.LogicalName == "knowledgearticle" && record2Id != null && record2Id.LogicalName == "product") { PerformUpdate(provider, updater => updater.UpdateEntity("knowledgearticle", record1Id.Id)); } } if (message.Target.LogicalName == "adx_contentaccesslevel") { var fetch = GetEntityFetch("adx_knowledgearticlecontentaccesslevel", "knowledgearticleid", "adx_contentaccesslevelid", message.Target.Id.ToString()); var entities = FetchEntities(serviceContext, fetch); var guids = entities.Select(e => e.GetAttributeValue <Guid>("knowledgearticleid")).ToList(); PerformUpdateAsync(provider, updater => updater.UpdateEntitySet("knowledgearticle", "knowledgearticleid", guids)); } if (message.Target.LogicalName == "product") { var fetch = GetEntityFetch("connection", "record2id", "record1id", message.Target.Id.ToString()); var entities = FetchEntities(serviceContext, fetch); var guids = entities.Select(e => e.GetAttributeValue <EntityReference>("record2id")).Select(g => g.Id).Distinct().ToList(); PerformUpdateAsync(provider, updater => updater.UpdateEntitySet("knowledgearticle", "knowledgearticleid", guids)); } if (message.Target.LogicalName == "annotation") { var annotationFetch = new Fetch { Entity = new FetchEntity("annotation", new[] { "objectid" }) { Filters = new[] { new Filter { Conditions = new List <Condition> { new Condition("annotationid", ConditionOperator.Equal, message.Target.Id), } } } } }; var response = (RetrieveSingleResponse)serviceContext.Execute(annotationFetch.ToRetrieveSingleRequest()); if (response.Entity == null) { ADXTrace.Instance.TraceError(TraceCategory.Application, $"Retrieve of annotation entity failed for annotationId : {message.Target.Id}"); throw new TransientNullReferenceException($"Retrieve of annotation entity failed for annotationId : {message.Target.Id}"); } var knowledgeArticle = response.Entity?.GetAttributeValue <EntityReference>("objectid"); if (knowledgeArticle == null) { ADXTrace.Instance.TraceError(TraceCategory.Application, $"Could not find objectId in the retrieved annotation with annotationId : {message.Target.Id}"); throw new TransientNullReferenceException($"Could not find objectId in the retrieved annotation with annotationId : {message.Target.Id}"); } if (knowledgeArticle.Id != Guid.Empty && knowledgeArticle.LogicalName == "knowledgearticle") { //Updating Knowledge Article related to this Annotation PerformUpdate(provider, updater => updater.UpdateEntity("knowledgearticle", knowledgeArticle.Id)); } } //Re-indexing annotations and related knowledge articles if NotesFilter gets changes if (message.Target.LogicalName == "adx_sitesetting" && message.Target.Name == "KnowledgeManagement/NotesFilter") { var notes = GetAllNotes(serviceContext); var knowledgeArticles = notes.Select(n => n.GetAttributeValue <EntityReference>("objectid")) .Distinct().Select(ka => ka.Id).Distinct() .ToList(); PerformUpdateAsync(provider, updater => updater.UpdateEntitySet("annotation", "annotationid", notes.Select(n => n.Id).Distinct().ToList())); PerformUpdateAsync(provider, updater => updater.UpdateEntitySet("knowledgearticle", "knowledgearticleid", knowledgeArticles)); return; } if (message.Target.LogicalName == "adx_sitesetting" && message.Target.Name == "KnowledgeManagement/DisplayNotes") { PerformUpdateAsync(provider, updater => updater.DeleteEntitySet("annotation")); var notes = GetAllNotes(serviceContext); var knowledgeArticles = notes.Select(n => n.GetAttributeValue <EntityReference>("objectid")) .Distinct().Select(ka => ka.Id).Distinct() .ToList(); PerformUpdateAsync(provider, updater => updater.UpdateEntitySet("annotation", "annotationid", notes.Select(n => n.Id).Distinct().ToList())); PerformUpdateAsync(provider, updater => updater.UpdateEntitySet("knowledgearticle", "knowledgearticleid", knowledgeArticles)); return; } } PerformUpdate(provider, updater => updater.UpdateEntity(message.Target.LogicalName, message.Target.Id)); ADXTrace.Instance.TraceInfo(TraceCategory.Application, string.Format("Search Index was successfully updated. ({0}, {1}:{2})", message.MessageName, EntityNamePrivacy.GetEntityName(message.Target.LogicalName), message.Target.Id)); return; } if (FeatureCheckHelper.IsFeatureEnabled(FeatureNames.CmsEnabledSearching)) { if (AssociateDisassociateMessages.Contains(message.MessageName, MessageComparer)) { if (message.Target == null || string.IsNullOrEmpty(message.Target.LogicalName)) { throw new HttpException((int)HttpStatusCode.BadRequest, string.Format("Message {0} requires an EntityName (entity logical name) parameter.", message.MessageName)); } if (message.Target == null || message.Target.Id == Guid.Empty) { throw new HttpException((int)HttpStatusCode.BadRequest, string.Format("Message {0} requires an ID (entity ID) parameter.", message.MessageName)); } if (message.RelatedEntities == null) { throw new HttpException((int)HttpStatusCode.BadRequest, string.Format("Message {0} requires an EntityName (entity logical name) parameter.", message.MessageName)); } if ((message.Target.LogicalName == "adx_webpage" && HasRelatedEntityType(message, "adx_webpageaccesscontrolrule")) || (message.Target.LogicalName == "adx_communityforum" && HasRelatedEntityType(message, "adx_communityforumaccesspermission")) || (message.Target.LogicalName == "adx_ideaforum" && HasRelatedEntityType(message, "adx_webrole"))) { PerformUpdateAsync(provider, updater => updater.UpdateCmsEntityTree(message.Target.LogicalName, message.Target.Id)); } if (message.Target.LogicalName == "adx_webpageaccesscontrolrule" && (HasRelatedEntityType(message, "adx_webrole") || HasRelatedEntityType(message, "adx_publishingstate"))) { contentMapProvider.Using(contentMap => { WebPageAccessControlRuleNode webAccessControlNode; if (contentMap.TryGetValue(new EntityReference(message.Target.LogicalName, message.Target.Id), out webAccessControlNode)) { PerformUpdateAsync(provider, updater => updater.UpdateCmsEntityTree("adx_webpage", webAccessControlNode.WebPage.Id)); } }); } if (message.Target.LogicalName == "adx_communityforumaccesspermission" && HasRelatedEntityType(message, "adx_webrole")) { contentMapProvider.Using(contentMap => { ForumAccessPermissionNode forumAccessNode; if (contentMap.TryGetValue(new EntityReference(message.Target.LogicalName, message.Target.Id), out forumAccessNode)) { PerformUpdateAsync(provider, updater => updater.UpdateCmsEntityTree("adx_communityforum", forumAccessNode.Forum.Id)); } }); } if (message.Target.LogicalName == "adx_contentaccesslevel" && HasRelatedEntityType(message, "knowledgearticle")) { foreach (var entityReference in message.RelatedEntities) { if (entityReference.LogicalName == "knowledgearticle") { PerformUpdate(provider, updater => updater.UpdateEntity(entityReference.LogicalName, entityReference.Id)); } } } //Perform update for disassociate messages from WebNotification Plugin if (message.Target.LogicalName == "knowledgearticle" && (HasRelatedEntityType(message, "adx_contentaccesslevel"))) { PerformUpdate(provider, updater => updater.UpdateEntity(message.Target.LogicalName, message.Target.Id)); } ADXTrace.Instance.TraceInfo(TraceCategory.Application, string.Format("Search Index was successfully updated. ({0}, {1}:{2})", message.MessageName, EntityNamePrivacy.GetEntityName(message.Target.LogicalName), message.Target.Id)); return; } } if (DeleteMessages.Contains(message.MessageName, MessageComparer)) { if (message.Target == null || string.IsNullOrEmpty(message.Target.LogicalName)) { throw new HttpException((int)HttpStatusCode.BadRequest, string.Format("Message {0} requires an EntityName (entity logical name) parameter.", message.MessageName)); } if (message.Target == null || message.Target.Id == Guid.Empty) { throw new HttpException((int)HttpStatusCode.BadRequest, string.Format("Message {0} requires an ID (entity ID) parameter.", message.MessageName)); } if (FeatureCheckHelper.IsFeatureEnabled(FeatureNames.CmsEnabledSearching)) { if (message.Target.LogicalName == "adx_webpageaccesscontrolrule" && searchIndexInvalidationData.WebPage != null) { PerformUpdateAsync(provider, updater => updater.UpdateCmsEntityTree(searchIndexInvalidationData.WebPage.LogicalName, searchIndexInvalidationData.WebPage.Id)); } if (message.Target.LogicalName == "adx_webpageaccesscontrolrule_webrole" && searchIndexInvalidationData.WebPage != null) { PerformUpdateAsync(provider, updater => updater.UpdateCmsEntityTree(searchIndexInvalidationData.WebPage.LogicalName, searchIndexInvalidationData.WebPage.Id)); } if (message.Target.LogicalName == "adx_communityforumaccesspermission" && searchIndexInvalidationData.Forum != null) { PerformUpdateAsync(provider, updater => updater.UpdateCmsEntityTree(searchIndexInvalidationData.Forum.LogicalName, searchIndexInvalidationData.Forum.Id)); } if (message.Target.LogicalName == "connection") { // To update Knowledge Article that was related to this connection(Product) we need to retrieve KAid from index var relatedEntityList = GetRelatedEntities("connectionid", message.Target.Id, 1); if (!relatedEntityList.Any()) { return; } // Taking first here since there can only be one Knowledge Article related to connection var entity = relatedEntityList.First(); if (entity.LogicalName != null && entity.LogicalName.Equals("knowledgearticle")) { PerformUpdate(provider, updater => updater.UpdateEntity("knowledgearticle", entity.Id)); } return; } if (message.Target.LogicalName == "product" || message.Target.LogicalName == "adx_contentaccesslevel") { IEnumerable <EntityReference> relatedKnowledgeArticles = new List <EntityReference>(); var indexedFieldName = "adx_contentaccesslevel"; if (message.Target.LogicalName == "product") { indexedFieldName = FixedFacetsConfiguration.ProductFieldFacetName; } relatedKnowledgeArticles = GetRelatedEntities(indexedFieldName, message.Target.Id, 10000); if (!relatedKnowledgeArticles.Any()) { return; } var knowledgeArticlesIds = relatedKnowledgeArticles.Where(r => r.LogicalName.Equals("knowledgearticle")).Select(i => i.Id).ToList(); PerformUpdateAsync(provider, updater => updater.UpdateEntitySet("knowledgearticle", "knowledgearticleid", knowledgeArticlesIds)); } if (message.Target.LogicalName == "annotation") { var relatedKnowledgeArticles = GetRelatedEntities("annotationid", message.Target.Id, 10); var knowledgeArticleId = relatedKnowledgeArticles.Where(a => a.LogicalName == "knowledgearticle").Select(ka => ka.Id).FirstOrDefault(); //Updating Knowledge Article related to this Annotation PerformUpdate(provider, updater => updater.UpdateEntity("knowledgearticle", knowledgeArticleId)); } } PerformUpdate(provider, updater => updater.DeleteEntity(message.Target.LogicalName, message.Target.Id)); ADXTrace.Instance.TraceInfo(TraceCategory.Application, string.Format("Search Index was successfully updated. ({0}, {1}:{2})", message.MessageName, EntityNamePrivacy.GetEntityName(message.Target.LogicalName), message.Target.Id)); return; } var supportedMessages = DeleteMessages.Union(BuildMessages.Union(UpdateMessages, MessageComparer), MessageComparer); ADXTrace.Instance.TraceError(TraceCategory.Application, string.Format(@"Search Index Build Failed. Message ""{0}"" is not supported. Valid messages are {1}.", message.MessageName, string.Join(", ", supportedMessages.ToArray()))); }
private static bool HasRelatedEntityType(OrganizationServiceCachePluginMessage message, string relatedEntityLogicalName) { return(message.RelatedEntities.Exists(relatedEntity => relatedEntity != null && relatedEntity.LogicalName == relatedEntityLogicalName)); }
/// <summary> /// Posts notifications of entity changes /// </summary> /// <param name="messages">List of Notification messages</param> /// <param name="isMetadataChangeMessage">True if it is a metadata change notification.</param> /// <param name="isSearchIndexInvalidation">True for search index invlaidation and false for cache invalidation.</param> /// <returns></returns> private List <string> PostRequest(IEnumerable <PluginMessage> messages, bool isMetadataChangeMessage, bool isSearchIndexInvalidation = false) { List <string> entitiesWithSuccessfulInvalidation = new List <string>(); try { var batchedMessages = messages .Where(mesg => mesg != null) .GroupBy(mesg => mesg.Target == null ? string.Empty : mesg.Target.LogicalName); foreach (var batchedmessage in batchedMessages) { List <OrganizationServiceCachePluginMessage> batchedPluginMessage = new List <OrganizationServiceCachePluginMessage>(); var searchInvalidationDatum = new Dictionary <Guid, SearchIndexBuildRequest.SearchIndexInvalidationData>(); foreach (var changedItem in batchedmessage) { if (changedItem != null) { if (changedItem.Target != null) { ADXTrace.Instance.TraceInfo(TraceCategory.Application, string.Format("Posting Request for message with Entity: {0} and ChangeType: {1}", changedItem.Target.LogicalName, changedItem.MessageName)); } var restartMessage = new ApplicationRestartPortalBusMessage(); // Conversion to OrganizationServiceCachePluginMessage type var message = new OrganizationServiceCachePluginMessage(); message.MessageName = changedItem.MessageName; message.RelatedEntities = changedItem.RelatedEntities; message.Relationship = changedItem.Relationship; message.Target = changedItem.Target; if (restartMessage.Validate(changedItem)) { // The restart messages should be processed only once when the message is received from Cache subscription. if (!isSearchIndexInvalidation) { // restart the web application var task = restartMessage.InvokeAsync(new OwinContext()).WithCurrentCulture(); task.GetAwaiter().GetResult(); SearchIndexBuildRequest.ProcessMessage(message); } } else { if (!isSearchIndexInvalidation && FeatureCheckHelper.IsFeatureEnabled(FeatureNames.CmsEnabledSearching) && message.Target != null && message.Target.Id != Guid.Empty) { // Get relevant info for search index invalidation from content map before cache invalidation // MUST OCCUR BEFORE CACHE INVALIDATION if (!searchInvalidationDatum.ContainsKey(message.Target.Id)) { searchInvalidationDatum.Add(message.Target.Id, GetSearchIndexInvalidationData(message)); } } batchedPluginMessage.Add(message); } } else { //logging ADXTrace.Instance.TraceWarning(TraceCategory.Application, string.Format("ChangedItem Record is Null ")); } } if (batchedPluginMessage.Count > 0) { if (isMetadataChangeMessage) { // Invalidate both search index as well as cache try { InvalidateSearchIndex(batchedPluginMessage, searchInvalidationDatum); } catch (Exception e) { // Even if exception occurs, we still need to invalidate cache, hence cathing exception here and logging error. ADXTrace.Instance.TraceError(TraceCategory.Exception, e.ToString()); } InvalidateCache(batchedPluginMessage); } else if (isSearchIndexInvalidation) { InvalidateSearchIndex(batchedPluginMessage, searchInvalidationDatum); } else { // Invalidate cache InvalidateCache(batchedPluginMessage); } } entitiesWithSuccessfulInvalidation.Add(batchedmessage.Key); } return(entitiesWithSuccessfulInvalidation); } catch (Exception e) { ADXTrace.Instance.TraceError(TraceCategory.Application, e.ToString()); return(entitiesWithSuccessfulInvalidation); } }
private SearchIndexBuildRequest.SearchIndexInvalidationData GetSearchIndexInvalidationData(OrganizationServiceCachePluginMessage message) { if (message.Target == null || string.IsNullOrEmpty(message.Target.LogicalName) || message.Target.Id == Guid.Empty) { return(null); } var searchIndexInvalidationData = new SearchIndexBuildRequest.SearchIndexInvalidationData(); IContentMapProvider contentMapProvider = AdxstudioCrmConfigurationManager.CreateContentMapProvider(); contentMapProvider.Using(contentMap => { if (message.Target.LogicalName == "adx_webpage") { WebPageNode webPageNode; if (contentMap.TryGetValue(new EntityReference(message.Target.LogicalName, message.Target.Id), out webPageNode)) { searchIndexInvalidationData.PartialUrl = webPageNode.PartialUrl; if (webPageNode.Parent != null) { searchIndexInvalidationData.ParentPage = new EntityReference("adx_webpage", webPageNode.Parent.Id); } if (webPageNode.Website != null) { searchIndexInvalidationData.Website = new EntityReference("adx_website", webPageNode.Website.Id); } if (webPageNode.PublishingState != null) { searchIndexInvalidationData.PublishingState = new EntityReference("adx_publishingstate", webPageNode.PublishingState.Id); } } } if (message.Target.LogicalName == "adx_webpageaccesscontrolrule_webrole") { WebPageAccessControlRuleToWebRoleNode webAccessControlToWebRoleNode; if (contentMap.TryGetValue(new EntityReference(message.Target.LogicalName, message.Target.Id), out webAccessControlToWebRoleNode)) { WebPageAccessControlRuleNode webAccessControlNode = webAccessControlToWebRoleNode.WebPageAccessControlRule; if (webAccessControlNode != null && webAccessControlNode.WebPage != null) { searchIndexInvalidationData.WebPage = new EntityReference("adx_webpage", webAccessControlNode.WebPage.Id); } } } if (message.Target.LogicalName == "adx_webpageaccesscontrolrule") { WebPageAccessControlRuleNode webAccessControlNode; if (contentMap.TryGetValue(new EntityReference(message.Target.LogicalName, message.Target.Id), out webAccessControlNode)) { if (webAccessControlNode.WebPage != null) { searchIndexInvalidationData.WebPage = new EntityReference("adx_webpage", webAccessControlNode.WebPage.Id); } } } if (message.Target.LogicalName == "adx_communityforumaccesspermission") { ForumAccessPermissionNode forumAccessNode; if (contentMap.TryGetValue(new EntityReference(message.Target.LogicalName, message.Target.Id), out forumAccessNode)) { if (forumAccessNode.Forum != null) { searchIndexInvalidationData.Forum = new EntityReference("adx_communityforum", forumAccessNode.Forum.Id); } } } }); return(searchIndexInvalidationData); }
public virtual void RemoveLocal(OrganizationServiceCachePluginMessage message) { Remove(message); }
public virtual void Remove(OrganizationServiceCachePluginMessage message) { Inner.Remove(message); }
private static bool IsMetadataMessage(OrganizationServiceCachePluginMessage message) { return(message != null && message.Category.HasValue && message.Category.Value.HasFlag(CacheItemCategory.Metadata)); }
public override void RemoveLocal(OrganizationServiceCachePluginMessage message) { message.ThrowOnNull("message"); base.Remove(message); }
public OrganizationServiceCachePluginMessageHandler(string portalName, OrganizationServiceCachePluginMessage message) { PortalName = portalName; Message = message; }