/// <summary> /// Gets cache information for the specified domain operation entry. /// </summary> /// <param name="method">The domain operation entry to get cache information for.</param> /// <returns>Cache information.</returns> private static OutputCacheAttribute GetOutputCacheInformation(DomainOperationEntry method) { OutputCacheAttribute cacheAttribute = method.Attributes.OfType <OutputCacheAttribute>().FirstOrDefault(); if (cacheAttribute != null) { if (!String.IsNullOrEmpty(cacheAttribute.CacheProfile)) { if (QueryOperationInvoker.cacheProfiles == null) { lock (QueryOperationInvoker.syncRoot) { if (QueryOperationInvoker.cacheProfiles == null) { OutputCacheSettingsSection outputCacheSettings = (OutputCacheSettingsSection)WebConfigurationManager.GetWebApplicationSection("system.web/caching/outputCacheSettings"); QueryOperationInvoker.cacheProfiles = outputCacheSettings.OutputCacheProfiles; } } } OutputCacheProfile profile = QueryOperationInvoker.cacheProfiles[cacheAttribute.CacheProfile]; OutputCacheAttribute cacheInfo = new OutputCacheAttribute(QueryOperationInvoker.GetCacheLocation(profile.Location), profile.Duration); cacheInfo.VaryByHeaders = profile.VaryByHeader; cacheInfo.SqlCacheDependencies = profile.SqlDependency; return(cacheInfo); } return(cacheAttribute); } return(null); }
protected override object InvokeCore(object instance, object[] inputs, out object[] outputs) { outputs = ServiceUtility.EmptyObjectArray; ServiceQuery serviceQuery = null; QueryAttribute queryAttribute = (QueryAttribute)this.operation.OperationAttribute; if (queryAttribute.IsComposable) { object value; if (OperationContext.Current.IncomingMessageProperties.TryGetValue(ServiceQuery.QueryPropertyName, out value)) { serviceQuery = (ServiceQuery)value; } } IEnumerable <ValidationResult> validationErrors; int totalCount; QueryResult <TEntity> result; try { QueryOperationInvoker.SetOutputCachingPolicy(this.operation); result = QueryProcessor.Process <TEntity>((DomainService)instance, this.operation, inputs, serviceQuery, out validationErrors, out totalCount); } catch (Exception ex) { if (ex.IsFatal()) { throw; } QueryOperationInvoker.ClearOutputCachingPolicy(); throw ServiceUtility.CreateFaultException(ex); } if (validationErrors != null && validationErrors.Any()) { throw ServiceUtility.CreateFaultException(validationErrors); } return(result); }
protected override async ValueTask <object> InvokeCoreAsync(DomainService instance, object[] inputs, bool disableStackTraces) { ServiceQuery serviceQuery = null; QueryAttribute queryAttribute = (QueryAttribute)this.operation.OperationAttribute; // httpContext is lost on await so need to save it for later ise HttpContext httpContext = HttpContext.Current; if (queryAttribute.IsComposable) { object value; if (OperationContext.Current.IncomingMessageProperties.TryGetValue(ServiceQuery.QueryPropertyName, out value)) { serviceQuery = (ServiceQuery)value; } } QueryResult <TEntity> result; try { QueryOperationInvoker.SetOutputCachingPolicy(httpContext, this.operation); result = await QueryProcessor.ProcessAsync <TEntity>(instance, this.operation, inputs, serviceQuery); } catch (Exception ex) { if (ex.IsFatal()) { throw; } QueryOperationInvoker.ClearOutputCachingPolicy(httpContext); throw ServiceUtility.CreateFaultException(ex, disableStackTraces); } if (result.ValidationErrors != null && result.ValidationErrors.Any()) { throw ServiceUtility.CreateFaultException(result.ValidationErrors, disableStackTraces); } return(result); }
/// <summary> /// Sets the output cache policy for the specified domain operation entry. /// </summary> /// <param name="context">Current HttpContext</param> /// <param name="domainOperationEntry">The domain operation entry we need to define the cache policy for.</param> private static void SetOutputCachingPolicy(HttpContext context, DomainOperationEntry domainOperationEntry) { if (context == null) { return; } if (QueryOperationInvoker.SupportsCaching(context, domainOperationEntry)) { OutputCacheAttribute outputCacheInfo = QueryOperationInvoker.GetOutputCacheInformation(domainOperationEntry); if (outputCacheInfo != null) { HttpCachePolicy policy = context.Response.Cache; if (outputCacheInfo.UseSlidingExpiration) { policy.SetSlidingExpiration(true); policy.AddValidationCallback((HttpCacheValidateHandler) delegate(HttpContext c, object d, ref HttpValidationStatus status) { SlidingExpirationValidator validator = (SlidingExpirationValidator)d; if (validator.IsValid()) { status = HttpValidationStatus.Valid; } else { status = HttpValidationStatus.Invalid; } }, new SlidingExpirationValidator(outputCacheInfo.Duration)); } if (outputCacheInfo.Duration > -1) { // When sliding expiration is set, ASP.NET will use the following to figure out the sliding expiration delta. policy.SetExpires(DateTime.UtcNow.AddSeconds(outputCacheInfo.Duration)); policy.SetMaxAge(TimeSpan.FromSeconds(outputCacheInfo.Duration)); policy.SetValidUntilExpires(/* validUntilExpires */ true); } policy.SetCacheability(QueryOperationInvoker.GetCacheability(outputCacheInfo.Location)); if (!String.IsNullOrEmpty(outputCacheInfo.SqlCacheDependencies)) { // Syntax is <databaseEntry>:<tableName>[;<databaseEntry>:<tableName>]*. string[] dependencies = outputCacheInfo.SqlCacheDependencies.Split(QueryOperationInvoker.semiColonDelimiter, StringSplitOptions.RemoveEmptyEntries); foreach (string dependency in dependencies) { string[] dependencyTokens = dependency.Split(QueryOperationInvoker.colonDelimiter, StringSplitOptions.RemoveEmptyEntries); if (dependencyTokens.Length != 2) { throw new InvalidOperationException(Resource.DomainService_InvalidSqlDependencyFormat); } context.Response.AddCacheDependency(new SqlCacheDependency(dependencyTokens[0], dependencyTokens[1])); } } if (!String.IsNullOrEmpty(outputCacheInfo.VaryByHeaders)) { string[] headers = outputCacheInfo.VaryByHeaders.Split(QueryOperationInvoker.semiColonDelimiter, StringSplitOptions.RemoveEmptyEntries); foreach (string header in headers) { policy.VaryByHeaders[header] = true; } } // The cache is based on the values of the domain operation entry's parameters. foreach (DomainOperationParameter pi in domainOperationEntry.Parameters) { policy.VaryByParams[pi.Name] = true; } // We don't cache when query parameters are used. We need to vary by query parameters // though such that we can intercept requests with query parameters and by-pass the cache. foreach (string queryParameter in supportedQueryParameters) { policy.VaryByParams[queryParameter] = true; } return; } } // By default, don't let clients/proxies cache anything. context.Response.Cache.SetCacheability(HttpCacheability.NoCache); }