コード例 #1
0
        private static HttpConfiguration InitializeConfiguration(string controllerName, bool useCustomEdmModel,
                                                                 ODataUriResolver resolver = null)
        {
            var controllers = new[]
            {
                typeof(QueryCompositionPrimitiveController), typeof(QueryCompositionCustomerController),
                typeof(QueryCompositionCustomerQueryableController),
                typeof(QueryCompositionCustomerWithTaskOfIEnumerableController),
                typeof(QueryCompositionCustomerGlobalController), typeof(QueryCompositionCustomerValidationController),
                typeof(QueryCompositionCustomerLowLevelController),
                typeof(QueryCompositionCustomerLowLevel_ODataQueryOptionsOfTController),
                typeof(QueryCompositionCategoryController), typeof(QueryCompositionAnonymousTypesController)
            };

            var config = RoutingConfigurationFactory.CreateWithTypes(controllers);

            config.Routes.MapHttpRoute("default", "{controller}/{key}", new { key = RouteParameter.Optional });
            config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;

            if (controllerName == "QueryCompositionCustomerGlobal")
            {
                config.Filters.Add(new EnableQueryAttribute());
            }

            if (useCustomEdmModel)
            {
                if (_queryCompositionCustomerModel == null)
                {
                    ODataModelBuilder modelBuilder = ODataConventionModelBuilderFactory.Create();
                    modelBuilder.EntitySet <QueryCompositionCustomer>(typeof(QueryCompositionCustomer).Name);
                    _queryCompositionCustomerModel = modelBuilder.GetEdmModel();
                }
                if (resolver == null)
                {
                    config.EnableODataDependencyInjectionSupport("default", _queryCompositionCustomerModel);
                }
                else
                {
                    config.EnableODataDependencyInjectionSupport("default",
                                                                 b => b.AddService(ServiceLifetime.Singleton, sp => _queryCompositionCustomerModel)
                                                                 .AddService(ServiceLifetime.Singleton, sp => resolver));
                }

                config.Filters.Add(new SetModelFilter(_queryCompositionCustomerModel));
            }
            else
            {
                config.EnableODataDependencyInjectionSupport("default");
            }

            return(config);
        }
コード例 #2
0
        /// <summary>
        /// Initializes a new instance of the <see cref="ODataQueryOptions"/> class based on the incoming request and some metadata information from
        /// the <see cref="ODataQueryContext"/>.
        /// </summary>
        /// <param name="context">The <see cref="ODataQueryContext"/> which contains the <see cref="IEdmModel"/> and some type information.</param>
        private void Initialize(ODataQueryContext context)
        {
            Contract.Assert(context != null);

            ODataUriResolver uriResolver = null;

            if (context.RequestContainer != null)
            {
                uriResolver = context.RequestContainer.GetService <ODataUriResolver>();
            }

            if (uriResolver != null)
            {
                _enableNoDollarSignQueryOptions = uriResolver.EnableNoDollarQueryOptions;
            }
            else
            {
                // Use the global setting
                _enableNoDollarSignQueryOptions = context.Request.IsNoDollarQueryEnable();
            }

            // Parse the query from request Uri, including only keys which are OData query parameters or parameter alias
            // OData query parameters are normalized with the $-sign prefixes when the
            // <code>EnableNoDollarSignPrefixSystemQueryOption</code> option is used.
            RawValues = new ODataRawQueryOptions();
            IDictionary <string, string> normalizedQueryParameters = GetODataQueryParameters();

            _queryOptionParser = new ODataQueryOptionParser(
                context.Model,
                context.ElementType,
                context.NavigationSource,
                normalizedQueryParameters);

            if (uriResolver != null)
            {
                _queryOptionParser.Resolver = uriResolver;
            }
            else
            {
                // By default, let's enable the property name case-insensitive
                _queryOptionParser.Resolver = new ODataUriResolver {
                    EnableCaseInsensitive = true
                };
            }

            BuildQueryOptions(normalizedQueryParameters);

            Validator = ODataQueryValidator.GetODataQueryValidator(context);
        }
コード例 #3
0
ファイル: ODataQueryOptions.cs プロジェクト: kosinsky/WebApi
        public bool IsSupportedQueryOption(string queryOptionName)
        {
            ODataUriResolver resolver = _queryOptionParser != null
                ? _queryOptionParser.Resolver
                : Request.GetRequestContainer().GetRequiredService <ODataUriResolver>();

            if (!resolver.EnableCaseInsensitive)
            {
                return(IsSystemQueryOption(queryOptionName, this._enableNoDollarSignQueryOptions));
            }

            string lowcaseQueryOptionName = queryOptionName.ToLowerInvariant();

            return(IsSystemQueryOption(lowcaseQueryOptionName, this._enableNoDollarSignQueryOptions));
        }
コード例 #4
0
 /// <summary>
 /// Enables common OData functionality like pagination, filtering, selection
 /// </summary>
 /// <param name="builder"></param>
 /// <returns></returns>
 public static IRouteBuilder EnableCommonOData(this IRouteBuilder builder)
 {
     builder.EnableDependencyInjection(o =>
     {
         o.AddService(
             ServiceLifetime.Singleton,
             _ =>
         {
             var resolver = new ODataUriResolver {
                 EnableCaseInsensitive = true
             };
             return(resolver);
         });
     });
     builder.Filter().Count().Select().MaxTop(200).OrderBy();
     return(builder);
 }
コード例 #5
0
        private static HttpConfiguration GetQueryOptionConfiguration(bool caseInsensitive)
        {
            var config = RoutingConfigurationFactory.CreateWithTypes(new[] { typeof(ParserExtenstionCustomersController) });
            ODataUriResolver resolver = new ODataUriResolver
            {
                EnableCaseInsensitive = caseInsensitive,
            };

            config.Count().OrderBy().Filter().Expand().MaxTop(null).Select();
            config.MapODataServiceRoute("query", "query",
                                        builder =>
                                        builder.AddService(ServiceLifetime.Singleton, sp => GetEdmModel())
                                        .AddService <IEnumerable <IODataRoutingConvention> >(ServiceLifetime.Singleton, sp =>
                                                                                             ODataRoutingConventions.CreateDefaultWithAttributeRouting("query", config))
                                        .AddService(ServiceLifetime.Singleton, sp => resolver));
            return(config);
        }
コード例 #6
0
        /// <summary>
        /// Initializes a new instance of the <see cref="ODataQueryOptions"/> class based on the incoming request and some metadata information from
        /// the <see cref="ODataQueryContext"/>.
        /// </summary>
        /// <param name="context">The <see cref="ODataQueryContext"/> which contains the <see cref="IEdmModel"/> and some type information.</param>
        /// <param name="request">The incoming request message.</param>
        public ODataQueryOptions(ODataQueryContext context, HttpRequestMessage request)
        {
            if (context == null)
            {
                throw Error.ArgumentNull("context");
            }

            if (request == null)
            {
                throw Error.ArgumentNull("request");
            }

            // Set the request container into context
            Contract.Assert(context.RequestContainer == null);
            context.RequestContainer = request.GetRequestContainer();

            // Remember the context and request
            Context = context;
            Request = request;

            ODataUriResolver uriResolver = request.GetRequestContainer().GetRequiredService <ODataUriResolver>();

            if (uriResolver != null)
            {
                _enableNoDollarSignQueryOptions = uriResolver.EnableNoDollarQueryOptions;
            }

            // Parse the query from request Uri, including only keys which are OData query parameters or parameter alias
            // OData query parameters are normalized with the $-sign prefixes when the
            // <code>EnableNoDollarSignPrefixSystemQueryOption</code> option is used.
            RawValues = new ODataRawQueryOptions();
            IDictionary <string, string> normalizedqueryParameters = GetODataQueryParameters();

            _queryOptionParser = new ODataQueryOptionParser(
                context.Model,
                context.ElementType,
                context.NavigationSource,
                normalizedqueryParameters);

            _queryOptionParser.Resolver = uriResolver;

            BuildQueryOptions(normalizedqueryParameters);

            Validator = ODataQueryValidator.GetODataQueryValidator(context);
        }
コード例 #7
0
        public static ODataPath Parse(this IODataPathHandler handler, IEdmModel model, string serviceRoot,
                                      string odataPath, ODataUriResolver resolver = null)
        {
            Contract.Assert(handler != null);
            Action <IContainerBuilder> action;

            if (resolver != null)
            {
                action = b => b.AddService(ServiceLifetime.Singleton, sp => model)
                         .AddService(ServiceLifetime.Singleton, sp => resolver);
            }
            else
            {
                action = b => b.AddService(ServiceLifetime.Singleton, sp => model);
            }

            return(handler.Parse(serviceRoot, odataPath, new MockContainer(action)));
        }
コード例 #8
0
        public async Task QueryComposition_WorkAsExpect_ForCaseInsensitive()
        {
            // Arrange
            const string     caseInSensitive = "?$fIlTer=iD Eq 33";
            ODataUriResolver resolver        = new ODataUriResolver
            {
                EnableCaseInsensitive = true,
            };
            HttpServer server = new HttpServer(InitializeConfiguration("QueryCompositionCustomer", true, resolver));
            HttpClient client = new HttpClient(server);

            // Act
            HttpResponseMessage response = await GetResponse(client, server.Configuration,
                                                             "http://localhost:8080/QueryCompositionCustomer" + caseInSensitive);

            // Assert
            Assert.True(response.IsSuccessStatusCode);
            Assert.Contains("[{\"Name\":\"Highest\",\"Add", await response.Content.ReadAsStringAsync());
        }
コード例 #9
0
        public void QueryComposition_WorkAsExpect_ForOptionalDollarSignPrefixForSystemQuery(
            string noDollarSignSystemQuery, bool enableCaseInsensitive)
        {
            // Arrange
            ODataUriResolver resolver = new ODataUriResolver
            {
                EnableNoDollarQueryOptions = true,
                EnableCaseInsensitive      = enableCaseInsensitive
            };
            HttpServer server = new HttpServer(InitializeConfiguration("QueryCompositionCustomer", true, resolver));
            HttpClient client = new HttpClient(server);

            // Act
            HttpResponseMessage response = GetResponse(client, server.Configuration,
                                                       "http://localhost:8080/QueryCompositionCustomer" + noDollarSignSystemQuery);

            // Assert
            Assert.True(response.IsSuccessStatusCode);
            Assert.Contains("[{\"Name\":\"Highest\",\"Add", response.Content.ReadAsStringAsync().Result);
        }
コード例 #10
0
        public bool IsSupportedQueryOption(string queryOptionName)
        {
            if (string.IsNullOrEmpty(queryOptionName))
            {
                throw Error.ArgumentNullOrEmpty(nameof(queryOptionName));
            }

            ODataUriResolver resolver = _queryOptionParser != null
                ? _queryOptionParser.Resolver
                : Request.GetSubServiceProvider().GetRequiredService <ODataUriResolver>();

            if (!resolver.EnableCaseInsensitive)
            {
                return(IsSystemQueryOption(queryOptionName, this._enableNoDollarSignQueryOptions));
            }

            string lowcaseQueryOptionName = queryOptionName.ToLowerInvariant();

            return(IsSystemQueryOption(lowcaseQueryOptionName, this._enableNoDollarSignQueryOptions));
        }
コード例 #11
0
        public void QueryComposition_ThrowsException_ForCaseSensitive()
        {
            // Arrange
            const string     caseInSensitive = "?$fIlTer=iD Eq 33";
            ODataUriResolver resolver        = new ODataUriResolver
            {
                EnableCaseInsensitive = false
            };
            HttpServer server =
                new HttpServer(InitializeConfiguration("QueryCompositionCustomer", true, resolver));
            HttpClient client = new HttpClient(server);

            // Act
            HttpResponseMessage response = GetResponse(client, server.Configuration,
                                                       "http://localhost:8080/QueryCompositionCustomer" + caseInSensitive);

            // Assert
            Assert.False(response.IsSuccessStatusCode);
            Assert.Contains("The query parameter '$fIlTer' is not supported.", response.Content.ReadAsStringAsync().Result);
        }
コード例 #12
0
        /// <summary>
        /// Adds the default OData services to the <see cref="IContainerBuilder"/>.
        /// </summary>
        /// <param name="builder">The <see cref="IContainerBuilder"/> to add the services to.</param>
        /// <returns>The <see cref="IContainerBuilder"/> instance itself.</returns>
        public static IContainerBuilder AddDefaultODataServices(this IContainerBuilder builder)
        {
            Debug.Assert(builder != null, "builder != null");

            builder.AddService <IJsonReaderFactory, DefaultJsonReaderFactory>(ServiceLifetime.Singleton);
            builder.AddService <IJsonWriterFactory, DefaultJsonWriterFactory>(ServiceLifetime.Singleton);
            builder.AddService(ServiceLifetime.Singleton, sp => ODataMediaTypeResolver.GetMediaTypeResolver(null));
            builder.AddService <ODataMessageInfo>(ServiceLifetime.Scoped);
            builder.AddServicePrototype(new ODataMessageReaderSettings());
            builder.AddService(ServiceLifetime.Scoped, sp => sp.GetServicePrototype <ODataMessageReaderSettings>().Clone());
            builder.AddServicePrototype(new ODataMessageWriterSettings());
            builder.AddService(ServiceLifetime.Scoped, sp => sp.GetServicePrototype <ODataMessageWriterSettings>().Clone());
            builder.AddService(ServiceLifetime.Singleton, sp => ODataPayloadValueConverter.GetPayloadValueConverter(null));
            builder.AddService <IEdmModel>(ServiceLifetime.Singleton, sp => EdmCoreModel.Instance);
            builder.AddService(ServiceLifetime.Singleton, sp => ODataUriResolver.GetUriResolver(null));
            builder.AddService <ODataUriParserSettings>(ServiceLifetime.Scoped);
            builder.AddService <UriPathParser>(ServiceLifetime.Scoped);
            builder.AddServicePrototype(new ODataSimplifiedOptions());
            builder.AddService(ServiceLifetime.Scoped, sp => sp.GetServicePrototype <ODataSimplifiedOptions>().Clone());

            return(builder);
        }
コード例 #13
0
        private static ODataQueryOptions GetQueryOptions(string queryOption)
        {
            string uri = "Http://localhost/RoutingCustomers?" + queryOption;

            ODataUriResolver resolver = new ODataUriResolver
            {
                EnableCaseInsensitive = true
            };

            var configuration = RoutingConfigurationFactory.CreateWithRootContainer("OData", b => b.AddService(ServiceLifetime.Singleton, sp => resolver));
            var request       = RequestFactory.Create(HttpMethod.Get, uri, configuration, "OData");

            IEdmModel model = ODataRoutingModel.GetModel();

            IEdmEntitySet  entityset  = model.EntityContainer.FindEntitySet("RoutingCustomers");
            IEdmEntityType entityType =
                model.SchemaElements.OfType <IEdmEntityType>().Single(e => e.Name == "RoutingCustomer");

            ODataPath         path    = new ODataPath(new[] { new EntitySetSegment(entityset) });
            ODataQueryContext context = new ODataQueryContext(model, entityType, path);

            return(new ODataQueryOptions(context, request));
        }
コード例 #14
0
ファイル: ExtensionTestBase.cs プロジェクト: zhonli/odata.net
        protected void TestConflictWithExactMatch <TResult>(
            string originalStr,
            string caseInsensitiveString,
            Func <ODataUriParser, TResult> parse,
            Action <TResult> verify,
            string conflictMessage,
            IEdmModel model,
            ODataUriResolver resolver)
        {
            Uri originalCase    = new Uri(originalStr, UriKind.Relative);
            Uri insensitiveCase = new Uri(caseInsensitiveString, UriKind.Relative);

            // Original string should pass case sensitive
            ODataUriParser parser = new ODataUriParser(model, originalCase)
            {
                Resolver = new ODataUriResolver()
                {
                    EnableCaseInsensitive = false
                }
            };

            verify(parse(parser));

            // Original string should pass case insensitive
            verify(parse(new ODataUriParser(model, originalCase)
            {
                Resolver = resolver
            }));

            // Insensitive case should fail with case insensitive parser with errorMessage
            Action action = () => parse(new ODataUriParser(model, insensitiveCase)
            {
                Resolver = resolver
            });

            action.ShouldThrow <ODataException>().WithMessage(conflictMessage);
        }
コード例 #15
0
        private static HttpConfiguration GetConfiguration(bool caseInsensitive, bool unqualifiedNameCall)
        {
            IEdmModel         model  = ODataRoutingModel.GetModel();
            HttpConfiguration config = RoutingConfigurationFactory.CreateWithTypes(new[]
            {
                typeof(MetadataController),
                typeof(ProductsController),
                typeof(RoutingCustomersController),
            });

            ODataUriResolver resolver = new ODataUriResolver();

            if (unqualifiedNameCall)
            {
                resolver = new UnqualifiedODataUriResolver();
                if (caseInsensitive)
                {
                    resolver = new UnqualifiedCaseInsensitiveResolver();
                }
            }
            else
            {
                if (caseInsensitive)
                {
                    resolver = new CaseInsensitiveResolver();
                }
            }

            config.Count().Filter().OrderBy().Expand().MaxTop(null).Select();
            config.MapODataServiceRoute("odata", "odata",
                                        builder =>
                                        builder.AddService(ServiceLifetime.Singleton, sp => model)
                                        .AddService <IEnumerable <IODataRoutingConvention> >(ServiceLifetime.Singleton, sp =>
                                                                                             ODataRoutingConventions.CreateDefaultWithAttributeRouting("odata", config))
                                        .AddService(ServiceLifetime.Singleton, sp => resolver));
            return(config);
        }
コード例 #16
0
        public ODataUriResolver CreateResolver()
        {
            ODataUriResolver resolver;

            if (UnqualifiedNameCall && EnumPrefixFree)
            {
                resolver = new UnqualifiedCallAndEnumPrefixFreeResolver();
            }
            else if (UnqualifiedNameCall)
            {
                resolver = new UnqualifiedODataUriResolver();
            }
            else if (EnumPrefixFree)
            {
                resolver = new StringAsEnumResolver();
            }
            else
            {
                resolver = new ODataUriResolver();
            }

            resolver.EnableCaseInsensitive = CaseInsensitive;
            return(resolver);
        }
コード例 #17
0
        /// <summary>
        /// Initializes a new instance of the <see cref="ODataQueryOptions"/> class based on the incoming request and some metadata information from
        /// the <see cref="ODataQueryContext"/>.
        /// </summary>
        /// <param name="context">The <see cref="ODataQueryContext"/> which contains the <see cref="IEdmModel"/> and some type information.</param>
        private void Initialize(ODataQueryContext context)
        {
            Contract.Assert(context != null);

            // ODataUriResolver uriResolver = context.RequestContainer.GetRequiredService<ODataUriResolver>();
            ODataUriResolver uriResolver = context.RequestContainer.GetService <ODataUriResolver>();

            if (uriResolver != null)
            {
                _enableNoDollarSignQueryOptions = uriResolver.EnableNoDollarQueryOptions;
            }

            // Parse the query from request Uri, including only keys which are OData query parameters or parameter alias
            // OData query parameters are normalized with the $-sign prefixes when the
            // <code>EnableNoDollarSignPrefixSystemQueryOption</code> option is used.
            RawValues = new ODataRawQueryOptions();
            IDictionary <string, string> normalizedQueryParameters = GetODataQueryParameters();

            _queryOptionParser = new ODataQueryOptionParser(
                context.Model,
                context.ElementType,
                context.NavigationSource,
                normalizedQueryParameters);

            // Note: the context.RequestContainer must be set by the ODataQueryOptions constructor.
            Contract.Assert(context.RequestContainer != null);

            if (uriResolver != null)
            {
                _queryOptionParser.Resolver = uriResolver;
            }

            BuildQueryOptions(normalizedQueryParameters);

            Validator = ODataQueryValidator.GetODataQueryValidator(context);
        }
コード例 #18
0
        /// <summary>Tries to create a key segment for the given filter if it is non empty.</summary>
        /// <param name="previous">Segment on which to compose.</param>
        /// <param name="previousKeySegment">The parent node's key segment.</param>
        /// <param name="parenthesisExpression">Parenthesis expression of segment.</param>
        /// <param name="keySegment">The key segment that was created if the key was non-empty.</param>
        /// <param name="enableUriTemplateParsing">Whether Uri template parsing is enabled.</param>
        /// <param name="resolver">The resolver to use.</param>
        /// <returns>Whether the key was non-empty.</returns>
        internal static bool TryCreateKeySegmentFromParentheses(ODataPathSegment previous, KeySegment previousKeySegment, string parenthesisExpression, out ODataPathSegment keySegment, bool enableUriTemplateParsing = false, ODataUriResolver resolver = null)
        {
            Debug.Assert(parenthesisExpression != null, "parenthesisExpression != null");
            Debug.Assert(previous != null, "segment!= null");

            if (resolver == null)
            {
                resolver = ODataUriResolver.Default;
            }

            if (previous.SingleResult)
            {
                throw ExceptionUtil.CreateSyntaxError();
            }

            SegmentArgumentParser key;

            if (!SegmentArgumentParser.TryParseKeysFromUri(parenthesisExpression, out key, enableUriTemplateParsing))
            {
                throw ExceptionUtil.CreateSyntaxError();
            }

            // People/NS.Employees() is OK, just like People() is OK
            if (key.IsEmpty)
            {
                keySegment = null;
                return(false);
            }

            keySegment = CreateKeySegment(previous, previousKeySegment, key, resolver);
            return(true);
        }
コード例 #19
0
 /// <summary>
 /// Build a property visitor to visit the select tree and decorate a SelectExpandClause
 /// </summary>
 /// <param name="model">The model used for binding.</param>
 /// <param name="edmType">The entity type that the $select is being applied to.</param>
 /// <param name="maxDepth">the maximum recursive depth.</param>
 /// <param name="expandClauseToDecorate">The already built expand clause to decorate</param>
 /// <param name="resolver">Resolver for uri parser.</param>
 public SelectPropertyVisitor(IEdmModel model, IEdmStructuredType edmType, int maxDepth, SelectExpandClause expandClauseToDecorate, ODataUriResolver resolver)
 {
     this.model    = model;
     this.edmType  = edmType;
     this.maxDepth = maxDepth;
     this.expandClauseToDecorate = expandClauseToDecorate;
     this.resolver = resolver ?? ODataUriResolver.Default;
 }
コード例 #20
0
        internal static bool ResolveOperationImportFromList(string identifier, IList <string> parameterNames, IEdmModel model, out IEdmOperationImport matchingOperationImport, ODataUriResolver resolver)
        {
            IList <IEdmOperationImport> candidateMatchingOperationImports         = null;
            IList <IEdmActionImport>    foundActionImportsWhenLookingForFunctions = new List <IEdmActionImport>();

            try
            {
                if (parameterNames.Count > 0)
                {
                    // In this case we have to return a function so filter out actions because the number of parameters > 0.
                    candidateMatchingOperationImports = resolver.ResolveOperationImports(model, identifier).RemoveActionImports(out foundActionImportsWhenLookingForFunctions).FilterFunctionsByParameterNames(parameterNames, resolver.EnableCaseInsensitive).Cast <IEdmOperationImport>().ToList();
                }
                else
                {
                    candidateMatchingOperationImports = resolver.ResolveOperationImports(model, identifier).ToList();
                }
            }
            catch (Exception exc)
            {
                if (!ExceptionUtils.IsCatchableExceptionType(exc))
                {
                    throw;
                }

                throw new ODataException(ODataErrorStrings.FunctionOverloadResolver_FoundInvalidOperationImport(identifier), exc);
            }

            if (foundActionImportsWhenLookingForFunctions.Count > 0)
            {
                throw ExceptionUtil.CreateBadRequestError(ODataErrorStrings.RequestUriProcessor_SegmentDoesNotSupportKeyPredicates(identifier));
            }

            // If any of the things returned are an action, it better be the only thing returned, and there can't be parameters in the URL
            if (candidateMatchingOperationImports.Any(f => f.IsActionImport()))
            {
                if (candidateMatchingOperationImports.Count > 1)
                {
                    if (candidateMatchingOperationImports.Any(o => o.IsFunctionImport()))
                    {
                        throw new ODataException(ODataErrorStrings.FunctionOverloadResolver_MultipleOperationImportOverloads(identifier));
                    }
                    else
                    {
                        throw new ODataException(ODataErrorStrings.FunctionOverloadResolver_MultipleActionImportOverloads(identifier));
                    }
                }

                if (parameterNames.Count() != 0)
                {
                    throw ExceptionUtil.CreateBadRequestError(ODataErrorStrings.RequestUriProcessor_SegmentDoesNotSupportKeyPredicates(identifier));
                }

                matchingOperationImport = candidateMatchingOperationImports.Single();
                return(true);
            }

            // If parameter count is zero and there is one function import whoese parameter count is zero, return this function import.
            if (candidateMatchingOperationImports.Count > 1 && parameterNames.Count == 0)
            {
                candidateMatchingOperationImports = candidateMatchingOperationImports.Where(operationImport => operationImport.Operation.Parameters.Count() == 0).ToList();
            }

            if (candidateMatchingOperationImports.Count == 0)
            {
                matchingOperationImport = null;
                return(false);
            }

            if (candidateMatchingOperationImports.Count > 1)
            {
                throw new ODataException(ODataErrorStrings.FunctionOverloadResolver_MultipleOperationImportOverloads(identifier));
            }

            matchingOperationImport = candidateMatchingOperationImports.Single();

            return(matchingOperationImport != null);
        }
コード例 #21
0
        internal static bool ResolveOperationFromList(string identifier, IEnumerable <string> parameterNames, IEdmType bindingType, IEdmModel model, out IEdmOperation matchingOperation, ODataUriResolver resolver)
        {
            // TODO: update code that is duplicate between operation and operation import, add more tests.
            // If the previous segment is an open type, the service action name is required to be fully qualified or else we always treat it as an open property name.
            if (bindingType != null)
            {
                // TODO: look up actual container names here?
                // When using extension, there may be function call with unqualified name. So loose the restriction here.
                if (bindingType.IsOpenType() && !identifier.Contains(".") && resolver.GetType() == typeof(ODataUriResolver))
                {
                    matchingOperation = null;
                    return(false);
                }
            }

            IList <IEdmOperation> candidateMatchingOperations = null;

            // The extension method FindBoundOperations & FindOperations call IEdmModel.FindDeclaredBoundOperations which can be implemented by anyone and it could throw any type of exception
            // so catching all of them and simply putting it in the inner exception.
            try
            {
                if (bindingType != null)
                {
                    candidateMatchingOperations = resolver.ResolveBoundOperations(model, identifier, bindingType).ToList();
                }
                else
                {
                    candidateMatchingOperations = resolver.ResolveUnboundOperations(model, identifier).ToList();
                }
            }
            catch (Exception exc)
            {
                if (ExceptionUtils.IsCatchableExceptionType(exc))
                {
                    throw new ODataException(ODataErrorStrings.FunctionOverloadResolver_FoundInvalidOperation(identifier), exc);
                }

                throw;
            }

            IList <IEdmAction> foundActionsWhenLookingForFunctions = new List <IEdmAction>();
            int parameterNamesCount = parameterNames.Count();

            if (bindingType != null)
            {
                candidateMatchingOperations = candidateMatchingOperations.EnsureOperationsBoundWithBindingParameter().ToList();
            }

            // If the number of parameters > 0 then this has to be a function as actions can't have parameters on the uri only in the payload. Filter further by parameters in this case, otherwise don't.
            if (parameterNamesCount > 0)
            {
                // can only be a function as only functions have parameters on the uri.
                candidateMatchingOperations = candidateMatchingOperations.RemoveActions(out foundActionsWhenLookingForFunctions).FilterFunctionsByParameterNames(parameterNames, resolver.EnableCaseInsensitive).Cast <IEdmOperation>().ToList();
            }
            else if (bindingType != null)
            {
                // Filter out functions with more than one parameter. Actions should not be filtered as the parameters are in the payload not the uri
                candidateMatchingOperations = candidateMatchingOperations.Where(o => (o.IsFunction() && o.Parameters.Count() == 1) || o.IsAction()).ToList();
            }
            else
            {
                // Filter out functions with any parameters
                candidateMatchingOperations = candidateMatchingOperations.Where(o => (o.IsFunction() && !o.Parameters.Any()) || o.IsAction()).ToList();
            }

            // Only filter if there is more than one and its needed.
            if (candidateMatchingOperations.Count > 1)
            {
                candidateMatchingOperations = candidateMatchingOperations.FilterBoundOperationsWithSameTypeHierarchyToTypeClosestToBindingType(bindingType).ToList();
            }

            // If any of the things returned are an action, it better be the only thing returned, and there can't be parameters in the URL
            if (candidateMatchingOperations.Any(f => f.IsAction()))
            {
                if (candidateMatchingOperations.Count > 1)
                {
                    if (candidateMatchingOperations.Any(o => o.IsFunction()))
                    {
                        throw new ODataException(ODataErrorStrings.FunctionOverloadResolver_MultipleOperationOverloads(identifier));
                    }
                    else
                    {
                        throw new ODataException(ODataErrorStrings.FunctionOverloadResolver_MultipleActionOverloads(identifier));
                    }
                }

                if (parameterNames.Count() != 0)
                {
                    throw ExceptionUtil.CreateBadRequestError(ODataErrorStrings.RequestUriProcessor_SegmentDoesNotSupportKeyPredicates(identifier));
                }

                matchingOperation = candidateMatchingOperations.Single();
                return(true);
            }

            if (foundActionsWhenLookingForFunctions.Count > 0)
            {
                throw ExceptionUtil.CreateBadRequestError(ODataErrorStrings.RequestUriProcessor_SegmentDoesNotSupportKeyPredicates(identifier));
            }

            if (candidateMatchingOperations.Count > 1)
            {
                throw new ODataException(ODataErrorStrings.FunctionOverloadResolver_NoSingleMatchFound(identifier, string.Join(",", parameterNames.ToArray())));
            }

            matchingOperation = candidateMatchingOperations.SingleOrDefault();
            return(matchingOperation != null);
        }
コード例 #22
0
        /// <summary>Tries to convert values to the keys of the specified type.</summary>
        /// <param name="targetEntityType">The specified type.</param>
        /// <param name="keyPairs">The converted key-value pairs.</param>
        /// <param name="resolver">The resolver to use.</param>
        /// <returns>true if all values were converted; false otherwise.</returns>
        public bool TryConvertValues(IEdmEntityType targetEntityType, out IEnumerable <KeyValuePair <string, object> > keyPairs, ODataUriResolver resolver)
        {
            Debug.Assert(!this.IsEmpty, "!this.IsEmpty -- caller should check");
            IList <IEdmStructuralProperty> keyProperties = targetEntityType.Key().ToList();

            Debug.Assert(keyProperties.Count == this.ValueCount || resolver.GetType() != typeof(ODataUriResolver), "type.KeyProperties.Count == this.ValueCount -- will change with containment");

            if (this.NamedValues != null)
            {
                keyPairs = resolver.ResolveKeys(targetEntityType, this.NamedValues, this.ConvertValueWrapper);
            }
            else
            {
                Debug.Assert(this.positionalValues != null, "positionalValues != null -- otherwise this is Empty");
                Debug.Assert(this.PositionalValues.Count == keyProperties.Count || resolver.GetType() != typeof(ODataUriResolver), "Count of positional values does not match.");
                keyPairs = resolver.ResolveKeys(targetEntityType, this.PositionalValues, this.ConvertValueWrapper);
            }

            return(true);
        }
コード例 #23
0
 public BinaryOperatorBinderTests()
 {
     this.shouldReturnLeft     = true;
     this.binaryOperatorBinder = new BinaryOperatorBinder(this.BindMethodThatReturnsSingleValueQueryNode, ODataUriResolver.GetUriResolver(null));
 }
        /// <summary>
        /// Explores the OData query options for the specified API descriptions.
        /// </summary>
        /// <param name="apiDescriptions">The <see cref="IEnumerable{T}">sequence</see> of <see cref="ApiDescription">API descriptions</see> to explore.</param>
        /// <param name="uriResolver">The associated <see cref="ODataUriResolver">OData URI resolver</see>.</param>
        protected virtual void ExploreQueryOptions(IEnumerable <ApiDescription> apiDescriptions, ODataUriResolver uriResolver)
        {
            Arg.NotNull(apiDescriptions, nameof(apiDescriptions));
            Arg.NotNull(uriResolver, nameof(uriResolver));

            var queryOptions = Options.QueryOptions;
            var settings     = new ODataQueryOptionSettings()
            {
                NoDollarPrefix        = uriResolver.EnableNoDollarQueryOptions,
                DescriptionProvider   = queryOptions.DescriptionProvider,
                DefaultQuerySettings  = DefaultQuerySettings,
                ModelMetadataProvider = MetadataProvider,
            };

            queryOptions.ApplyTo(apiDescriptions, settings);
        }
コード例 #25
0
        /// <summary>
        /// Follow any type segments on the path, stopping at the first segment that isn't a type token.
        /// </summary>
        /// <param name="firstTypeToken">the first type segment</param>
        /// <param name="model">the model these types are contained in.</param>
        /// <param name="maxDepth">the maximum recursive depth</param>
        /// <param name="resolver">Resolver for uri parser.</param>
        /// <param name="currentLevelType">the top level type, will be overwritten with the last entity type in the chain</param>
        /// <param name="firstNonTypeToken">the first non type token in the path</param>
        /// <returns>A path with type segments added to it.</returns>
        public static IEnumerable <ODataPathSegment> FollowTypeSegments(PathSegmentToken firstTypeToken, IEdmModel model, int maxDepth, ODataUriResolver resolver, ref IEdmStructuredType currentLevelType, out PathSegmentToken firstNonTypeToken)
        {
            ExceptionUtils.CheckArgumentNotNull(firstTypeToken, "firstTypeToken");
            ExceptionUtils.CheckArgumentNotNull(model, "model");

            if (!firstTypeToken.IsNamespaceOrContainerQualified())
            {
                throw new ODataException(ODataErrorStrings.SelectExpandPathBinder_FollowNonTypeSegment(firstTypeToken.Identifier));
            }

            int index = 0;
            List <ODataPathSegment> pathToReturn = new List <ODataPathSegment>();
            PathSegmentToken        currentToken = firstTypeToken;

            while (currentToken.IsNamespaceOrContainerQualified() && currentToken.NextToken != null)
            {
                IEdmType previousLevelEntityType = currentLevelType;
                currentLevelType = UriEdmHelpers.FindTypeFromModel(model, currentToken.Identifier, resolver) as IEdmStructuredType;
                if (currentLevelType == null)
                {
                    // TODO: fix this error message?
                    throw new ODataException(ODataErrorStrings.ExpandItemBinder_CannotFindType(currentToken.Identifier));
                }

                UriEdmHelpers.CheckRelatedTo(previousLevelEntityType, currentLevelType);
                pathToReturn.Add(new TypeSegment(currentLevelType, /*entitySet*/ null));

                index++;
                currentToken = currentToken.NextToken;

                if (index >= maxDepth)
                {
                    throw new ODataException(ODataErrorStrings.ExpandItemBinder_PathTooDeep);
                }
            }

            firstNonTypeToken = currentToken;

            return(pathToReturn);
        }
コード例 #26
0
        /// <summary>
        /// Try to get an IEdmTypeReference for a given type as a string, returns null if none exists
        /// </summary>
        /// <param name="model">the model for validation</param>
        /// <param name="fullTypeName">the type name to find</param>
        /// <param name="resolver">Resolver for this func.</param>
        /// <returns>an IEdmTypeReference for this type string.</returns>
        private static IEdmTypeReference TryGetTypeReference(IEdmModel model, string fullTypeName, ODataUriResolver resolver)
        {
            IEdmTypeReference typeReference = UriEdmHelpers.FindTypeFromModel(model, fullTypeName, resolver).ToTypeReference();

            if (typeReference == null)
            {
                if (fullTypeName.StartsWith("Collection", StringComparison.Ordinal))
                {
                    string[] tokenizedString = fullTypeName.Split('(');
                    string   baseElementType = tokenizedString[1].Split(')')[0];
                    return(EdmCoreModel.GetCollection(UriEdmHelpers.FindTypeFromModel(model, baseElementType, resolver).ToTypeReference()));
                }
                else
                {
                    return(null);
                }
            }

            return(typeReference);
        }
コード例 #27
0
        /// <summary>
        /// Parses the key properties based on the segment's target type, then creates a new segment for the key.
        /// </summary>
        /// <param name="segment">The segment to apply the key to.</param>
        /// <param name="previousKeySegment">The parent node's key segment.</param>
        /// <param name="key">The key to apply.</param>
        /// <param name="resolver">The resolver to use.</param>
        /// <returns>The newly created key segment.</returns>
        private static KeySegment CreateKeySegment(ODataPathSegment segment, KeySegment previousKeySegment, SegmentArgumentParser key, ODataUriResolver resolver)
        {
            Debug.Assert(segment != null, "segment != null");
            Debug.Assert(key != null && !key.IsEmpty, "key != null && !key.IsEmpty");
            Debug.Assert(segment.SingleResult == false, "segment.SingleResult == false");

            IEdmEntityType targetEntityType = null;

            if (!(segment.TargetEdmType != null && segment.TargetEdmType.IsEntityOrEntityCollectionType(out targetEntityType)))
            {
                throw ExceptionUtil.CreateSyntaxError();
            }

            Debug.Assert(targetEntityType != null, "targetEntityType != null");

            // Make sure the keys specified in the uri matches with the number of keys in the metadata
            var keyProperties = targetEntityType.Key().ToList();

            if (keyProperties.Count != key.ValueCount)
            {
                NavigationPropertySegment currentNavPropSegment = segment as NavigationPropertySegment;
                if (currentNavPropSegment != null)
                {
                    key = KeyFinder.FindAndUseKeysFromRelatedSegment(key, keyProperties, currentNavPropSegment.NavigationProperty, previousKeySegment);
                }

                // if we still didn't find any keys, then throw an error.
                if (keyProperties.Count != key.ValueCount && resolver.GetType() == typeof(ODataUriResolver))
                {
                    throw ExceptionUtil.CreateBadRequestError(ErrorStrings.BadRequest_KeyCountMismatch(targetEntityType.FullName()));
                }
            }

            if (!key.AreValuesNamed && key.ValueCount > 1 && resolver.GetType() == typeof(ODataUriResolver))
            {
                throw ExceptionUtil.CreateBadRequestError(ErrorStrings.RequestUriProcessor_KeysMustBeNamed);
            }

            IEnumerable <KeyValuePair <string, object> > keyPairs;

            if (!key.TryConvertValues(targetEntityType, out keyPairs, resolver))
            {
                throw ExceptionUtil.CreateSyntaxError();
            }

            IEdmEntityType entityType;
            bool           isEntity = segment.TargetEdmType.IsEntityOrEntityCollectionType(out entityType);

            Debug.Assert(isEntity, "Key target type should be an entity type.");

            var keySegment = new KeySegment(keyPairs, entityType, segment.TargetEdmNavigationSource);

            keySegment.CopyValuesFrom(segment);
            keySegment.SingleResult = true;

            return(keySegment);
        }
コード例 #28
0
        /// <summary>
        /// Given a property name, if the associated type reference is strucutred, then this returns
        /// the property of the structured type. Otherwise, it returns null.
        /// </summary>
        /// <param name="parentReference">The parent type to be used to find binding options.</param>
        /// <param name="propertyName">The string designated the property name to be bound.</param>
        /// <param name="resolver">Resolver for uri parser.</param>
        /// <returns>The property associated with string and parent type.</returns>
        internal static IEdmProperty BindProperty(IEdmTypeReference parentReference, string propertyName, ODataUriResolver resolver = null)
        {
            if (resolver == null)
            {
                resolver = ODataUriResolver.Default;
            }

            IEdmStructuredTypeReference structuredParentType =
                parentReference == null ? null : parentReference.AsStructuredOrNull();

            return(structuredParentType == null ? null : resolver.ResolveProperty(structuredParentType.StructuredDefinition(), propertyName));
        }
コード例 #29
0
 /// <summary>
 /// Constructs a BinaryOperatorBinder with the given method to be used binding the parent token if needed.
 /// </summary>
 /// <param name="bindMethod">Method to use for binding the parent token, if needed.</param>
 /// <param name="resolver">Resolver for parsing.</param>
 internal BinaryOperatorBinder(Func <QueryToken, QueryNode> bindMethod, ODataUriResolver resolver)
 {
     this.bindMethod = bindMethod;
     this.resolver   = resolver ?? ODataUriResolver.Default;
 }
コード例 #30
0
        /// <summary>
        /// Tries to handle the current segment as a key property value.
        /// </summary>
        /// <param name="segmentText">The segment text.</param>
        /// <param name="previous">The previous segment.</param>
        /// <param name="previousKeySegment">The parent node's key segment.</param>
        /// <param name="urlConvention">The current url convention for the server.</param>
        /// <param name="keySegment">The key segment that was created if the segment could be interpreted as a key.</param>
        /// <param name="enableUriTemplateParsing">Whether Uri template parsing is enabled.</param>
        /// <param name="resolver">The resolver to use.</param>
        /// <returns>Whether or not the segment was interpreted as a key.</returns>
        internal static bool TryHandleSegmentAsKey(string segmentText, ODataPathSegment previous, KeySegment previousKeySegment, UrlConvention urlConvention, out KeySegment keySegment, bool enableUriTemplateParsing = false, ODataUriResolver resolver = null)
        {
            Debug.Assert(previous != null, "previous != null");
            Debug.Assert(urlConvention != null, "urlConvention != null");

            if (resolver == null)
            {
                resolver = ODataUriResolver.Default;
            }

            keySegment = null;

            // If the current convention does not support keys-as-segments, then this does not apply.
            if (!(urlConvention.GenerateKeyAsSegment || urlConvention.ODataSimplified))
            {
                return(false);
            }

            // Keys only apply to collections, so if the prior segment is already a singleton, do not treat this segment as a key.
            if (previous.SingleResult)
            {
                return(false);
            }

            // System segments (ie '$count') are never keys.
            if (IsSystemSegment(segmentText))
            {
                return(false);
            }

            // If the previous type is not an entity collection type
            // TODO: collapse this and SingleResult.
            IEdmEntityType targetEntityType;

            if (previous.TargetEdmType == null || !previous.TargetEdmType.IsEntityOrEntityCollectionType(out targetEntityType))
            {
                return(false);
            }

            // Previously KeyAsSegment only allows single key, but we can also leverage related key finder to auto fill
            // missed key value from referential constraint information, which would be done in CreateKeySegment.
            // CreateKeySegment method will check whether key properties are missing after taking in related key values.
            keySegment = CreateKeySegment(previous, previousKeySegment, SegmentArgumentParser.FromSegment(segmentText, enableUriTemplateParsing), resolver);

            return(true);
        }