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