示例#1
0
        public void AddCustomFunction_FunctionNameCannotBeNull()
        {
            FunctionSignatureWithReturnType customFunctionSignature =
                new FunctionSignatureWithReturnType(EdmCoreModel.Instance.GetBoolean(false), EdmCoreModel.Instance.GetInt32(false));

            Action addNullFunctionNameAction = () =>
                                               CustomUriFunctions.AddCustomUriFunction(null, customFunctionSignature);

            addNullFunctionNameAction.ShouldThrow <ArgumentNullException>();
        }
        public void AddCustomFunction_FunctionNameCannotBeEmptyString()
        {
            FunctionSignatureWithReturnType customFunctionSignature =
                new FunctionSignatureWithReturnType(EdmCoreModel.Instance.GetBoolean(false), EdmCoreModel.Instance.GetInt32(false));

            Action addCustomFunctionSignature = () =>
                                                CustomUriFunctions.AddCustomUriFunction(string.Empty, customFunctionSignature);

            Assert.Throws <ArgumentNullException>("functionName", addCustomFunctionSignature);
        }
示例#3
0
        /// <summary xml:lang="en">
        /// Registering other custom method to the OData query parser
        /// </summary>
        /// <summary xml:lang="cs">
        /// Registrace dodatečné vlastní metody do OData query parseru
        /// </summary>
        public static void RegisterCustomFunction(MethodInfo methodInfo, string functionName = null)
        {
            var returnType = TypeToReference(methodInfo.ReturnType);
            var args       = methodInfo.GetParameters()
                             .Select(x => TypeToReference(x.ParameterType))
                             .ToArray();
            var signature = new FunctionSignatureWithReturnType(returnType, args);

            ODataUriFunctions.AddCustomUriFunction(functionName ?? methodInfo.Name.ToLower(), signature, methodInfo);
        }
示例#4
0
        public void AddCustomFunction_CannotAddFunctionSignatureWithNullReturnType()
        {
            FunctionSignatureWithReturnType customFunctionSignatureWithNullReturnType =
                new FunctionSignatureWithReturnType(null, EdmCoreModel.Instance.GetInt32(false));

            Action addCustomFunctionSignature = () =>
                                                CustomUriFunctions.AddCustomUriFunction("my.customFunctionWithNoReturnType",
                                                                                        customFunctionSignatureWithNullReturnType);

            addCustomFunctionSignature.ShouldThrow <ArgumentNullException>();
        }
        public void AddCustomFunction_CannotAddFunctionSignatureWithNullReturnType()
        {
            FunctionSignatureWithReturnType customFunctionSignatureWithNullReturnType =
                new FunctionSignatureWithReturnType(null, EdmCoreModel.Instance.GetInt32(false));

            Action addCustomFunctionSignature = () =>
                                                CustomUriFunctions.AddCustomUriFunction("my.customFunctionWithNoReturnType",
                                                                                        customFunctionSignatureWithNullReturnType);

            Assert.Throws <ArgumentNullException>("functionSignatureWithReturnType must contain a return type", addCustomFunctionSignature);
        }
        public void RemoveCustomFunction_CannotRemoveFunctionWhichDoesntExist_ByNameAndSignature()
        {
            string customFunctionName = "my.ExistingCustomFunction";
            FunctionSignatureWithReturnType customFunctionSignature =
                new FunctionSignatureWithReturnType(EdmCoreModel.Instance.GetDouble(false), EdmCoreModel.Instance.GetBoolean(false));

            // Test
            bool isRemoveSucceeded = CustomUriFunctions.RemoveCustomUriFunction(customFunctionName, customFunctionSignature);

            // Assert
            Assert.False(isRemoveSucceeded);
        }
        public void RemoveCustomFunction_FunctionSignatureWithoutAReturnType()
        {
            FunctionSignatureWithReturnType existingCustomFunctionSignature =
                new FunctionSignatureWithReturnType(null, EdmCoreModel.Instance.GetBoolean(false));

            // Test
            Action removeFunction = () =>
                                    CustomUriFunctions.RemoveCustomUriFunction("FunctionName", existingCustomFunctionSignature);

            // Assert
            Assert.Throws <ArgumentNullException>("functionSignatureWithReturnType must contain a return type", removeFunction);
        }
        public void TryFindFunctionSignatureWithNumberOfArgumentsReturnsNothingfNoMatchFound()
        {
            // regression test for: FunctionCallBinder should validate that the number of parameters matches for canonical function calls on open properties
            FunctionSignatureWithReturnType[] functions = new FunctionSignatureWithReturnType[]
            {
                new FunctionSignatureWithReturnType(EdmCoreModel.Instance.GetDateTimeOffset(true), new IEdmTypeReference[] { EdmCoreModel.Instance.GetString(true) }),
            };

            // we specifically want to find just the first function that matches the number of arguments, we don't care about
            // ambiguity here because we're already in an ambiguous case where we don't know what kind of types
            // those arguments are.
            functions.FirstOrDefault(candidateFunction => candidateFunction.ArgumentTypes.Count() == 2).Should().BeNull();
        }
示例#9
0
 /// <summary>
 /// Promotes types of arguments to match signature if possible.
 /// </summary>
 /// <param name="signature">The signature to match the types to.</param>
 /// <param name="argumentNodes">The types to promote.</param>
 internal static void TypePromoteArguments(FunctionSignatureWithReturnType signature, List <QueryNode> argumentNodes)
 {
     // Convert all argument nodes to the best signature argument type
     Debug.Assert(signature.ArgumentTypes.Length == argumentNodes.Count, "The best signature match doesn't have the same number of arguments.");
     for (int i = 0; i < argumentNodes.Count; i++)
     {
         Debug.Assert(argumentNodes[i] is SingleValueNode, "We should have already verified that all arguments are single values.");
         SingleValueNode   argumentNode          = (SingleValueNode)argumentNodes[i];
         IEdmTypeReference signatureArgumentType = signature.ArgumentTypes[i];
         Debug.Assert(signatureArgumentType.IsODataPrimitiveTypeKind(), "Only primitive types should be able to get here.");
         argumentNodes[i] = MetadataBindingUtils.ConvertToTypeIfNeeded(argumentNode, signatureArgumentType);
     }
 }
示例#10
0
 /// <summary>
 /// Promotes types of arguments to match signature if possible.
 /// </summary>
 /// <param name="signature">The signature to match the types to.</param>
 /// <param name="argumentNodes">The types to promote.</param>
 internal static void TypePromoteArguments(FunctionSignatureWithReturnType signature, List<QueryNode> argumentNodes)
 {
     // Convert all argument nodes to the best signature argument type
     Debug.Assert(signature.ArgumentTypes.Length == argumentNodes.Count, "The best signature match doesn't have the same number of arguments.");
     for (int i = 0; i < argumentNodes.Count; i++)
     {
         Debug.Assert(argumentNodes[i] is SingleValueNode, "We should have already verified that all arguments are single values.");
         SingleValueNode argumentNode = (SingleValueNode)argumentNodes[i];
         IEdmTypeReference signatureArgumentType = signature.ArgumentTypes[i];
         Debug.Assert(signatureArgumentType.IsODataPrimitiveTypeKind(), "Only primitive types should be able to get here.");
         argumentNodes[i] = MetadataBindingUtils.ConvertToTypeIfNeeded(argumentNode, signatureArgumentType);
     }
 }
        public void TryFundFunctionSignatureAllowsAmbiguousFunctionCall()
        {
            FunctionSignatureWithReturnType first  = new FunctionSignatureWithReturnType(EdmCoreModel.Instance.GetDateTimeOffset(true), new IEdmTypeReference[] { EdmCoreModel.Instance.GetString(true) });
            FunctionSignatureWithReturnType second = new FunctionSignatureWithReturnType(EdmCoreModel.Instance.GetDateTimeOffset(true), new IEdmTypeReference[] { EdmCoreModel.Instance.GetInt32(true) });

            FunctionSignatureWithReturnType[] functions = new FunctionSignatureWithReturnType[]
            {
                first,
                second
            };

            // we specifically want to find just the first function that matches the number of arguments, we don't care about
            // ambiguity here because we're already in an ambiguous case where we don't know what kind of types
            // those arguments are.
            functions.FirstOrDefault(candidateFunction => candidateFunction.ArgumentTypes.Count() == 1).Should().Be(first);
        }
示例#12
0
        /// <summary>
        /// This is a shortcut of adding the custom FunctionSignature through 'CustomUriFunctions' class and
        /// binding the function name to it's MethodInfo through 'UriFunctionsBinder' class.
        /// See these classes documentations.
        /// In case of an exception, both operations(adding the signature and binding the function) will be undone.
        /// </summary>
        /// <param name="functionName">The uri function name that appears in the OData request uri.</param>
        /// <param name="functionSignature">The new custom function signature.</param>
        /// <param name="methodInfo">The MethodInfo to bind the given function name.</param>
        /// <exception cref="Exception">Any exception thrown by 'CustomUriFunctions.AddCustomUriFunction' and 'UriFunctionBinder.BindUriFunctionName' methods.</exception>
        public static void AddCustomUriFunction(string functionName,
                                                FunctionSignatureWithReturnType functionSignature, MethodInfo methodInfo)
        {
            try
            {
                // Add to OData.Libs function signature
                CustomUriFunctions.AddCustomUriFunction(functionName, functionSignature);

                // Bind the method to it's MethoInfo
                UriFunctionsBinder.BindUriFunctionName(functionName, methodInfo);
            }
            catch
            {
                // Clear in case of excpetion
                RemoveCustomUriFunction(functionName, functionSignature, methodInfo);
                throw;
            }
        }
        public void RemoveCustomFunction_ShouldRemoveAnExistingFunction_ByName()
        {
            string customFunctionName = "my.ExistingCustomFunction";

            // Prepare
            FunctionSignatureWithReturnType existingCustomFunctionSignature =
                new FunctionSignatureWithReturnType(EdmCoreModel.Instance.GetDouble(false), EdmCoreModel.Instance.GetBoolean(false));

            CustomUriFunctions.AddCustomUriFunction(customFunctionName, existingCustomFunctionSignature);

            Assert.True(GetCustomFunctionSignaturesOrNull(customFunctionName)[0].Equals(existingCustomFunctionSignature));

            // Test
            bool isRemoveSucceeded = CustomUriFunctions.RemoveCustomUriFunction(customFunctionName);

            // Assert
            Assert.True(isRemoveSucceeded);
            Assert.Null(GetCustomFunctionSignaturesOrNull(customFunctionName));
        }
示例#14
0
        public static void RegisterFunctions(IEdmTypeReference enumRef)
        {
            if (!registred)
            {
                lock (locker)
                {
                    if (!registred)
                    {
                        RegisterFunction(typeof(BoundFunctions), nameof(BoundFunctions.GetBestOrders));
                        RegisterFunction(typeof(SelectExpandTestCustomerWithCustom), nameof(SelectExpandTestCustomerWithCustom.GetInstanceBestOrders));
                        RegisterFunction(typeof(SelectExpandTestCustomerWithCustom), nameof(SelectExpandTestCustomerWithCustom.GetInt));
                        RegisterFunction(typeof(SelectExpandTestCustomerWithCustom), nameof(SelectExpandTestCustomerWithCustom.ConvertEnum));

                        var s = new FunctionSignatureWithReturnType(EdmLibHelpers.GetEdmPrimitiveTypeReferenceOrNull(typeof(double?)), enumRef);
                        ODataUriFunctions.AddCustomUriFunction("convert_enum", s, typeof(BoundFunctions).GetMethods().Where(m => m.Name == nameof(BoundFunctions.ConvertEnum)).First());

                        registred = true;
                    }
                }
            }
        }
        public void ParseWithCustomUriFunction()
        {
            try
            {
                FunctionSignatureWithReturnType myStringFunction
                    = new FunctionSignatureWithReturnType(EdmCoreModel.Instance.GetBoolean(true), EdmCoreModel.Instance.GetString(true), EdmCoreModel.Instance.GetString(true));

                // Add a custom uri function
                CustomUriFunctions.AddCustomUriFunction("mystringfunction", myStringFunction);

                var            fullUri = new Uri("http://www.odata.com/OData/People" + "?$filter=mystringfunction(Name, 'BlaBla')");
                ODataUriParser parser  = new ODataUriParser(HardCodedTestModel.TestModel, new Uri("http://www.odata.com/OData/"), fullUri);

                var startsWithArgs = parser.ParseFilter().Expression.ShouldBeSingleValueFunctionCallQueryNode("mystringfunction").Parameters.ToList();
                startsWithArgs[0].ShouldBeSingleValuePropertyAccessQueryNode(HardCodedTestModel.GetPersonNameProp());
                startsWithArgs[1].ShouldBeConstantQueryNode("BlaBla");
            }
            finally
            {
                Assert.True(CustomUriFunctions.RemoveCustomUriFunction("mystringfunction"));
            }
        }
示例#16
0
        /// <summary>
        /// Finds the signature that best matches the arguments
        /// </summary>
        /// <param name="functionName">The name of the function</param>
        /// <param name="argumentNodes">The nodes of the arguments, can be new {null,null}.</param>
        /// <param name="signatures">The signatures to match against</param>
        /// <returns>Returns the matching signature or throws</returns>
        internal static FunctionSignatureWithReturnType MatchSignatureToUriFunction(string functionName, SingleValueNode[] argumentNodes, FunctionSignatureWithReturnType[] signatures)
        {
            FunctionSignatureWithReturnType signature;

            IEdmTypeReference[] argumentTypes = argumentNodes.Select(s => s.TypeReference).ToArray();

            // Handle the cases where we don't have type information (null literal, open properties) for ANY of the arguments
            int argumentCount = argumentTypes.Length;

            if (argumentTypes.All(a => a == null) && argumentCount > 0)
            {
                // we specifically want to find just the first function that matches the number of arguments, we don't care about
                // ambiguity here because we're already in an ambiguous case where we don't know what kind of types
                // those arguments are.
                signature = signatures.FirstOrDefault(candidateFunction => candidateFunction.ArgumentTypes.Count() == argumentCount);
                if (signature == null)
                {
                    throw new ODataException(ODataErrorStrings.FunctionCallBinder_CannotFindASuitableOverload(functionName, argumentTypes.Count()));
                }
                else
                {
                    // in this case we can't assert the return type, we can only assert that a function exists... so
                    // we need to set the return type to null.
                    signature = new FunctionSignatureWithReturnType(null, signature.ArgumentTypes);
                }
            }
            else
            {
                signature = TypePromotionUtils.FindBestFunctionSignature(signatures, argumentNodes);
                if (signature == null)
                {
                    throw new ODataException(ODataErrorStrings.MetadataBinder_NoApplicableFunctionFound(
                                                 functionName,
                                                 UriFunctionsHelper.BuildFunctionSignatureListDescription(functionName, signatures)));
                }
            }

            return(signature);
        }
示例#17
0
        public void RemoveCustomFunction_RemoveFunctionWithSameNameAndSignature_OtherOverloadsExists()
        {
            string customFunctionName = "my.ExistingCustomFunction";

            try
            {
                // Prepare
                FunctionSignatureWithReturnType existingCustomFunctionSignature =
                    new FunctionSignatureWithReturnType(EdmCoreModel.Instance.GetDouble(false), EdmCoreModel.Instance.GetBoolean(false));
                CustomUriFunctions.AddCustomUriFunction(customFunctionName, existingCustomFunctionSignature);

                FunctionSignatureWithReturnType existingCustomFunctionSignatureTwo =
                    new FunctionSignatureWithReturnType(EdmCoreModel.Instance.GetBoolean(false), EdmCoreModel.Instance.GetDate(false));
                CustomUriFunctions.AddCustomUriFunction(customFunctionName, existingCustomFunctionSignatureTwo);

                // Validate that the two overloads as
                GetCustomFunctionSignaturesOrNull(customFunctionName).
                All(funcSignature => funcSignature.Equals(existingCustomFunctionSignature) ||
                    funcSignature.Equals(existingCustomFunctionSignatureTwo)).
                Should().BeTrue();

                // Remove the first overload, second overload should not be removed
                bool isRemoveSucceeded = CustomUriFunctions.RemoveCustomUriFunction(customFunctionName, existingCustomFunctionSignature);

                // Assert
                isRemoveSucceeded.Should().BeTrue();

                FunctionSignatureWithReturnType[] overloads = GetCustomFunctionSignaturesOrNull(customFunctionName);
                overloads.Length.Should().Be(1);
                overloads[0].Should().Be(existingCustomFunctionSignatureTwo);
            }
            finally
            {
                // Clean up cache
                CustomUriFunctions.RemoveCustomUriFunction(customFunctionName);
            }
        }
        public void ParseWithCustomUriFunction_AddAsOverloadToBuiltIn()
        {
            FunctionSignatureWithReturnType customStartWithFunctionSignature =
                new FunctionSignatureWithReturnType(EdmCoreModel.Instance.GetBoolean(true),
                                                    EdmCoreModel.Instance.GetString(true),
                                                    EdmCoreModel.Instance.GetInt32(true));

            try
            {
                // Add with override 'true'
                CustomUriFunctions.AddCustomUriFunction("startswith", customStartWithFunctionSignature);

                var            fullUri = new Uri("http://www.odata.com/OData/People" + "?$filter=startswith(Name, 66)");
                ODataUriParser parser  = new ODataUriParser(HardCodedTestModel.TestModel, new Uri("http://www.odata.com/OData/"), fullUri);

                var startsWithArgs = parser.ParseFilter().Expression.ShouldBeSingleValueFunctionCallQueryNode("startswith").Parameters.ToList();
                startsWithArgs[0].ShouldBeSingleValuePropertyAccessQueryNode(HardCodedTestModel.GetPersonNameProp());
                startsWithArgs[1].ShouldBeConstantQueryNode(66);
            }
            finally
            {
                Assert.True(CustomUriFunctions.RemoveCustomUriFunction("startswith"));
            }
        }
        public void ParseWithExactMatchCustomUriFunction_EnableCaseInsensitive_ShouldWorkForMultipleEquivalentArgumentsMatches()
        {
            string lowerCaseName = "myfunction";
            string upperCaseName = lowerCaseName.ToUpper();

            FunctionSignatureWithReturnType myStringFunction
                = new FunctionSignatureWithReturnType(EdmCoreModel.Instance.GetBoolean(true), EdmCoreModel.Instance.GetString(true), EdmCoreModel.Instance.GetString(true));

            // Add two customer uri functions with same argument types, with names different in cases.
            CustomUriFunctions.AddCustomUriFunction(lowerCaseName, myStringFunction);
            CustomUriFunctions.AddCustomUriFunction(upperCaseName, myStringFunction);
            string rootUri     = "http://www.odata.com/OData/";
            string uriTemplate = rootUri + "People?$filter={0}(Name,'BlaBla')";

            try
            {
                foreach (string functionName in new string[] { lowerCaseName, upperCaseName })
                {
                    // Uri with case-sensitive function names referring to equivalent-argument-typed functions,
                    // should work for resolver with case insensitive enabled.
                    var            fullUri = new Uri(string.Format(uriTemplate, functionName));
                    ODataUriParser parser  = new ODataUriParser(HardCodedTestModel.TestModel, new Uri(rootUri), fullUri);
                    parser.Resolver.EnableCaseInsensitive = true;

                    var startsWithArgs = parser.ParseFilter().Expression.ShouldBeSingleValueFunctionCallQueryNode(functionName)
                                         .Parameters.ToList();
                    startsWithArgs[0].ShouldBeSingleValuePropertyAccessQueryNode(HardCodedTestModel.GetPersonNameProp());
                    startsWithArgs[1].ShouldBeConstantQueryNode("BlaBla");
                }
            }
            finally
            {
                Assert.True(CustomUriFunctions.RemoveCustomUriFunction(lowerCaseName));
                Assert.True(CustomUriFunctions.RemoveCustomUriFunction(upperCaseName));
            }
        }
示例#20
0
        /// <summary>
        /// Finds the signature that best matches the arguments
        /// </summary>
        /// <param name="functionName">The name of the function</param>
        /// <param name="argumentNodes">The nodes of the arguments, can be new {null,null}.</param>
        /// <param name="signatures">The signatures to match against</param>
        /// <returns>Returns the matching signature or throws</returns>
        internal static FunctionSignatureWithReturnType MatchSignatureToBuiltInFunction(string functionName, SingleValueNode[] argumentNodes, FunctionSignatureWithReturnType[] signatures)
        {
            FunctionSignatureWithReturnType signature;
            IEdmTypeReference[] argumentTypes = argumentNodes.Select(s => s.TypeReference).ToArray();

            // Handle the cases where we don't have type information (null literal, open properties) for ANY of the arguments
            int argumentCount = argumentTypes.Length;
            if (argumentTypes.All(a => a == null) && argumentCount > 0)
            {
                // we specifically want to find just the first function that matches the number of arguments, we don't care about
                // ambiguity here because we're already in an ambiguous case where we don't know what kind of types 
                // those arguments are.
                signature = signatures.FirstOrDefault(candidateFunction => candidateFunction.ArgumentTypes.Count() == argumentCount);
                if (signature == null)
                {
                    throw new ODataException(ODataErrorStrings.FunctionCallBinder_CannotFindASuitableOverload(functionName, argumentTypes.Count()));
                }
                else
                {
                    // in this case we can't assert the return type, we can only assert that a function exists... so 
                    // we need to set the return type to null.
                    signature = new FunctionSignatureWithReturnType(null, signature.ArgumentTypes);
                }
            }
            else
            {
                signature = TypePromotionUtils.FindBestFunctionSignature(signatures, argumentNodes);
                if (signature == null)
                {
                    throw new ODataException(ODataErrorStrings.MetadataBinder_NoApplicableFunctionFound(
                        functionName,
                        BuiltInFunctions.BuildFunctionSignatureListDescription(functionName, signatures)));
                }
            }

            return signature;
        }