/// <summary>
            /// Deserializes the message into requests parameters. Also parses query parameters
            /// from the request and stores the results in the original message.
            /// </summary>
            /// <param name="message">The incoming message to deserialize.</param>
            /// <param name="parameters">The parameters that are passed to the query operation.
            /// </param>
            void IDispatchMessageFormatter.DeserializeRequest(Message message, object[] parameters)
            {
                Message originalMessage = message;
                var     isPost          = MessageUtility.IsHttpPOSTMethod(message.Properties);

                // If user tried to GET a query with side-effects then fail
                // Note: This should never ever happen since it would fail earlier in the WCF pipeline since the method should be "POST"-only
                if (_queryHasSideEffects && !isPost)
                {
                    throw new FaultException("Must use POST to for queries with side effects");
                }

                if (isPost)
                {
                    // If the HTTP Method is POST, get the query from the message body instead from the URL
                    ServiceQuery serviceQuery = MessageUtility.GetServiceQuery(ref message);
                    if (serviceQuery != null)
                    {
                        // Since a new message is returned by the GetServiceQueryFromMessageBody, the OperationContext does not find the property
                        // if set on the new message. So we set the property directly on the current OperationContext IncomingMessageProperties.
                        OperationContext.Current.IncomingMessageProperties[ServiceQuery.QueryPropertyName] = serviceQuery;
                    }
                }
                else if (!String.IsNullOrEmpty(message.Properties.Via.Query))
                {
                    string query          = HttpUtility.UrlDecode(message.Properties.Via.Query);
                    string fullRequestUrl = HttpUtility.UrlDecode(HttpContext.Current.Request.RawUrl);
                    message.Properties[ServiceQuery.QueryPropertyName] = DomainServiceWebHttpBehavior.GetServiceQuery(query, fullRequestUrl);
                }

                try
                {
                    this._innerDispatchMessageFormatter.DeserializeRequest(message, parameters);
                }
                finally
                {
                    // The original message belongs to the service model pipeline. We cannot
                    // dispose it. On the other hand we could have just created a new message. If
                    // that is the case we are responsible for disposing the new message.
                    if (message != originalMessage)
                    {
                        message.Properties.Clear();
                        message.Headers.Clear();
                        message.Close();
                    }
                }
            }
            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);
            }
예제 #3
0
            public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
            {
                int headerPosition = request.Headers.FindHeader(QueryHeaderName, QueryHeaderNamespace);

                if (headerPosition > -1)
                {
                    XmlDictionaryReader reader = request.Headers.GetReaderAtHeader(headerPosition);

                    // Advance to contents, ReadServiceQuery expect to be on the QueryOption
                    if (reader.IsStartElement(QueryPropertyName))
                    {
                        reader.Read();
                    }

                    ServiceQuery serviceQuery = MessageUtility.ReadServiceQuery(reader);
                    request.Properties[QueryPropertyName] = serviceQuery;
                }

                return(null);
            }
            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);
            }
        public void DomainService_DirectQuery()
        {
            DomainServiceDescription description = DomainServiceDescription.GetDescription(typeof(TestDomainServices.EF.Catalog));

            TestDomainServices.EF.Catalog service = new TestDomainServices.EF.Catalog();
            DomainServiceContext dsc = new DomainServiceContext(new MockDataService(new MockUser("mathew") { IsAuthenticated = true }), DomainOperationType.Query);
            service.Initialize(dsc);

            DomainOperationEntry queryOperation = description.GetQueryMethod("GetPurchaseOrders");

            ServiceQuery serviceQuery = new ServiceQuery();
            serviceQuery.QueryParts = new ServiceQueryPart[]
            {
                new ServiceQueryPart("where", "(it.Freight!=0)"),
                new ServiceQueryPart("take", "1")
            };

            IEnumerable<ValidationResult> validationErrors;
            int totalCount;
            QueryResult<AdventureWorksModel.PurchaseOrder> result = QueryProcessor.Process<AdventureWorksModel.PurchaseOrder>(service, queryOperation, new object[0], serviceQuery, out validationErrors, out totalCount);

            Assert.AreEqual(1, result.RootResults.Count());
        }
        /// <summary>
        /// This method returns a ServiceQuery for the specified URL and query string.
        /// <remarks>
        /// This method must ensure that the original ordering of the query parts is maintained
        /// in the results. We want to do this without doing any custom URL parsing. The approach
        /// taken is to use HttpUtility to parse the query string, and from those results we search
        /// in the full URL for the relative positioning of those elements.
        /// </remarks>
        /// </summary>
        /// <param name="queryString">The query string portion of the URL</param>
        /// <param name="fullRequestUrl">The full request URL</param>
        /// <returns>The corresponding ServiceQuery</returns>
        internal static ServiceQuery GetServiceQuery(string queryString, string fullRequestUrl)
        {
            NameValueCollection queryPartCollection = HttpUtility.ParseQueryString(queryString);
            bool includeTotalCount = false;

            // Reconstruct a list of all key/value pairs
            List<string> queryParts = new List<string>();
            foreach (string queryPart in queryPartCollection)
            {
                if (queryPart == null || !queryPart.StartsWith("$", StringComparison.Ordinal))
                {
                    // not a special query string
                    continue;
                }

                if (queryPart.Equals("$includeTotalCount", StringComparison.OrdinalIgnoreCase))
                {
                    string value = queryPartCollection.GetValues(queryPart).First();
                    Boolean.TryParse(value, out includeTotalCount);
                    continue;
                }

                foreach (string value in queryPartCollection.GetValues(queryPart))
                {
                    queryParts.Add(queryPart + "=" + value);
                }
            }

            string decodedQueryString = HttpUtility.UrlDecode(fullRequestUrl);

            // For each query part, find all occurrences of it in the Url (could be duplicates)
            List<KeyValuePair<string, int>> keyPairIndicies = new List<KeyValuePair<string, int>>();
            foreach (string queryPart in queryParts.Distinct())
            {
                int idx = -1;
                int endIdx = 0;
                while (((idx = decodedQueryString.IndexOf(queryPart, endIdx, StringComparison.Ordinal)) != -1) &&
                        (endIdx < decodedQueryString.Length - 1))
                {
                    // We found a match, however, we must ensure that the match is exact. For example,
                    // The string "$take=1" will be found twice in query string "?$take=10&$orderby=Name&$take=1",
                    // but the first match should be discarded. Therefore, before adding the match, we ensure
                    // the next character is EOS or the param seperator '&'.
                    endIdx = idx + queryPart.Length - 1;
                    if ((endIdx == decodedQueryString.Length - 1) ||
                        (endIdx < decodedQueryString.Length - 1 && (decodedQueryString[endIdx + 1] == '&')))
                    {
                        keyPairIndicies.Add(new KeyValuePair<string, int>(queryPart, idx));
                    }
                }
            }

            // create the list of ServiceQueryParts in order, ordered by
            // their location in the query string
            IEnumerable<string> orderedParts = keyPairIndicies.OrderBy(p => p.Value).Select(p => p.Key);
            IEnumerable<ServiceQueryPart> serviceQueryParts =
                from p in orderedParts
                let idx = p.IndexOf('=')
                select new ServiceQueryPart(p.Substring(1, idx - 1), p.Substring(idx + 1));

            ServiceQuery serviceQuery = new ServiceQuery()
            {
                QueryParts = serviceQueryParts.ToList(),
                IncludeTotalCount = includeTotalCount
            };

            return serviceQuery;
        }
        /// <summary>
        /// This method returns a ServiceQuery for the specified URL and query string.
        /// <remarks>
        /// This method must ensure that the original ordering of the query parts is maintained
        /// in the results. We want to do this without doing any custom URL parsing. The approach
        /// taken is to use HttpUtility to parse the query string, and from those results we search
        /// in the full URL for the relative positioning of those elements.
        /// </remarks>
        /// </summary>
        /// <param name="queryString">The query string portion of the URL</param>
        /// <param name="fullRequestUrl">The full request URL</param>
        /// <returns>The corresponding ServiceQuery</returns>
        internal static ServiceQuery GetServiceQuery(string queryString, string fullRequestUrl)
        {
            NameValueCollection queryPartCollection = HttpUtility.ParseQueryString(queryString);
            bool includeTotalCount = false;

            // Reconstruct a list of all key/value pairs
            List <string> queryParts = new List <string>();

            foreach (string queryPart in queryPartCollection)
            {
                if (queryPart == null || !queryPart.StartsWith("$", StringComparison.Ordinal))
                {
                    // not a special query string
                    continue;
                }

                if (queryPart.Equals("$includeTotalCount", StringComparison.OrdinalIgnoreCase))
                {
                    string value = queryPartCollection.GetValues(queryPart).First();
                    Boolean.TryParse(value, out includeTotalCount);
                    continue;
                }

                foreach (string value in queryPartCollection.GetValues(queryPart))
                {
                    queryParts.Add(queryPart + "=" + value);
                }
            }

            string decodedQueryString = HttpUtility.UrlDecode(fullRequestUrl);

            // For each query part, find all occurrences of it in the Url (could be duplicates)
            List <KeyValuePair <string, int> > keyPairIndicies = new List <KeyValuePair <string, int> >();

            foreach (string queryPart in queryParts.Distinct())
            {
                int idx    = -1;
                int endIdx = 0;
                while (((idx = decodedQueryString.IndexOf(queryPart, endIdx, StringComparison.Ordinal)) != -1) &&
                       (endIdx < decodedQueryString.Length - 1))
                {
                    // We found a match, however, we must ensure that the match is exact. For example,
                    // The string "$take=1" will be found twice in query string "?$take=10&$orderby=Name&$take=1",
                    // but the first match should be discarded. Therefore, before adding the match, we ensure
                    // the next character is EOS or the param seperator '&'.
                    endIdx = idx + queryPart.Length - 1;
                    if ((endIdx == decodedQueryString.Length - 1) ||
                        (endIdx < decodedQueryString.Length - 1 && (decodedQueryString[endIdx + 1] == '&')))
                    {
                        keyPairIndicies.Add(new KeyValuePair <string, int>(queryPart, idx));
                    }
                }
            }

            // create the list of ServiceQueryParts in order, ordered by
            // their location in the query string
            IEnumerable <string>           orderedParts      = keyPairIndicies.OrderBy(p => p.Value).Select(p => p.Key);
            IEnumerable <ServiceQueryPart> serviceQueryParts =
                from p in orderedParts
                let idx = p.IndexOf('=')
                          select new ServiceQueryPart(p.Substring(1, idx - 1), p.Substring(idx + 1));

            ServiceQuery serviceQuery = new ServiceQuery()
            {
                QueryParts        = serviceQueryParts.ToList(),
                IncludeTotalCount = includeTotalCount
            };

            return(serviceQuery);
        }
        public static async ValueTask <QueryResult <TEntity> > ProcessAsync <TEntity>(DomainService domainService, DomainOperationEntry queryOperation, object[] parameters, ServiceQuery serviceQuery)
        {
            DomainServiceDescription domainServiceDescription = DomainServiceDescription.GetDescription(domainService.GetType());

            // deserialize the query if specified
            IQueryable query             = null;
            bool       includeTotalCount = false;

            if (serviceQuery != null)
            {
                query             = GetQueryable <TEntity>(domainServiceDescription, serviceQuery);
                includeTotalCount = serviceQuery.IncludeTotalCount;
            }

            // invoke the query operation
            QueryDescription queryDescription = new QueryDescription(queryOperation, parameters, includeTotalCount, query);
            var res = await domainService.QueryAsync <TEntity>(queryDescription, CancellationToken.None);

            if (res.HasValidationErrors)
            {
                return(new QueryResult <TEntity>(res.ValidationErrors));
            }
            IEnumerable <TEntity> results = (IEnumerable <TEntity>)res.Result;
            int totalCount = res.TotalCount;

            // Performance optimization: if there are no included associations, we can assume we don't need to flatten.
            if (!QueryProcessor.RequiresFlattening(domainServiceDescription, typeof(TEntity)))
            {
                // if the root entity type doesn't have any included associations
                // return the results immediately, bypassing the flattening operation
                return(new QueryResult <TEntity>(results, totalCount));
            }

            List <TEntity> rootResults = null;

            if (!(results is ICollection <TEntity>))
            {
                // Not an ICollection<TEntity>... Copy over the items into a list of root results.
                rootResults = new List <TEntity>();
            }

            // flatten the results
            List <object> includedResults = new List <object>();

            FlattenGraph(results, rootResults, includedResults, new HashSet <object>(), domainServiceDescription);

            return(new QueryResult <TEntity>(rootResults ?? results, totalCount)
            {
                IncludedResults = includedResults
            });
        }
        private static IQueryable GetQueryable <TEntity>(DomainServiceDescription domainServiceDescription, ServiceQuery query)
        {
            if (query != null && query.QueryParts != null && query.QueryParts.Any())
            {
                IQueryable queryable = Enumerable.Empty <TEntity>().AsQueryable();
                return(QueryDeserializer.Deserialize(domainServiceDescription, queryable, query.QueryParts));
            }

            return(null);
        }