/// <summary>
        /// Boostrapping all the canonical functions for the EDM Provider Manifest
        /// </summary>
        private void InitializeCanonicalFunctions()
        {
            if (_functions != null)
            {
                return;
            }
                        
            // Ensure primitive types are available
            InitializePrimitiveTypes();

            EdmProviderManifestFunctionBuilder functions = new EdmProviderManifestFunctionBuilder(_primitiveTypes);
            PrimitiveTypeKind[] parameterTypes;
            
            #region Aggregate Functions

            // Max, Min
            parameterTypes = new[] { PrimitiveTypeKind.Byte,
                                     PrimitiveTypeKind.DateTime,
                                     PrimitiveTypeKind.Decimal,
                                     PrimitiveTypeKind.Double,
                                     PrimitiveTypeKind.Int16,
                                     PrimitiveTypeKind.Int32,
                                     PrimitiveTypeKind.Int64,
                                     PrimitiveTypeKind.SByte,
                                     PrimitiveTypeKind.Single,
                                     PrimitiveTypeKind.String,
                                     PrimitiveTypeKind.Binary,
                                     PrimitiveTypeKind.Time,
                                     PrimitiveTypeKind.DateTimeOffset };

            functions.ForTypes(parameterTypes, type => functions.AddAggregate("Max", type));
            functions.ForTypes(parameterTypes, type => functions.AddAggregate("Min", type));

            // Avg, Sum
            parameterTypes = new[] { PrimitiveTypeKind.Decimal,
                                     PrimitiveTypeKind.Double,
                                     PrimitiveTypeKind.Int32,
                                     PrimitiveTypeKind.Int64 };

            functions.ForTypes(parameterTypes, type => functions.AddAggregate("Avg", type));
            functions.ForTypes(parameterTypes, type => functions.AddAggregate("Sum", type));

            // STDEV, STDEVP, VAR, VARP
            parameterTypes = new[] { PrimitiveTypeKind.Decimal,
                                     PrimitiveTypeKind.Double,
                                     PrimitiveTypeKind.Int32,
                                     PrimitiveTypeKind.Int64};

            functions.ForTypes(parameterTypes, type => functions.AddAggregate(PrimitiveTypeKind.Double, "StDev", type));
            functions.ForTypes(parameterTypes, type => functions.AddAggregate(PrimitiveTypeKind.Double, "StDevP", type));
            functions.ForTypes(parameterTypes, type => functions.AddAggregate(PrimitiveTypeKind.Double, "Var", type));
            functions.ForTypes(parameterTypes, type => functions.AddAggregate(PrimitiveTypeKind.Double, "VarP", type));

            // Count and Big Count must be supported for all edm types, except the strong spatial types.
            functions.ForAllBasePrimitiveTypes(type => functions.AddAggregate(PrimitiveTypeKind.Int32, "Count", type));
            functions.ForAllBasePrimitiveTypes(type => functions.AddAggregate(PrimitiveTypeKind.Int64, "BigCount", type));
            
            #endregion

            #region String Functions

            functions.AddFunction(PrimitiveTypeKind.String, "Trim", PrimitiveTypeKind.String, "stringArgument");
            functions.AddFunction(PrimitiveTypeKind.String, "RTrim", PrimitiveTypeKind.String, "stringArgument");
            functions.AddFunction(PrimitiveTypeKind.String, "LTrim", PrimitiveTypeKind.String, "stringArgument");
            functions.AddFunction(PrimitiveTypeKind.String, "Concat", PrimitiveTypeKind.String, "string1", PrimitiveTypeKind.String, "string2");
            functions.AddFunction(PrimitiveTypeKind.Int32, "Length", PrimitiveTypeKind.String, "stringArgument");

            // Substring, Left, Right overloads 
            parameterTypes = new[] { PrimitiveTypeKind.Byte,
                                     PrimitiveTypeKind.Int16,
                                     PrimitiveTypeKind.Int32,
                                     PrimitiveTypeKind.Int64,
                                     PrimitiveTypeKind.SByte };

            functions.ForTypes(parameterTypes, type => functions.AddFunction(PrimitiveTypeKind.String, "Substring", PrimitiveTypeKind.String, "stringArgument", type, "start", type, "length"));
            functions.ForTypes(parameterTypes, type => functions.AddFunction(PrimitiveTypeKind.String, "Left", PrimitiveTypeKind.String, "stringArgument", type, "length"));
            functions.ForTypes(parameterTypes, type => functions.AddFunction(PrimitiveTypeKind.String, "Right", PrimitiveTypeKind.String, "stringArgument", type, "length"));
            
            functions.AddFunction(PrimitiveTypeKind.String,  "Replace", PrimitiveTypeKind.String, "stringArgument", PrimitiveTypeKind.String, "toReplace", PrimitiveTypeKind.String, "replacement");
            functions.AddFunction(PrimitiveTypeKind.Int32,   "IndexOf", PrimitiveTypeKind.String, "searchString", PrimitiveTypeKind.String, "stringToFind");
            functions.AddFunction(PrimitiveTypeKind.String,  "ToUpper", PrimitiveTypeKind.String, "stringArgument");
            functions.AddFunction(PrimitiveTypeKind.String,  "ToLower", PrimitiveTypeKind.String, "stringArgument");
            functions.AddFunction(PrimitiveTypeKind.String,  "Reverse", PrimitiveTypeKind.String, "stringArgument");
            functions.AddFunction(PrimitiveTypeKind.Boolean, "Contains", PrimitiveTypeKind.String, "searchedString", PrimitiveTypeKind.String, "searchedForString");
            functions.AddFunction(PrimitiveTypeKind.Boolean, "StartsWith", PrimitiveTypeKind.String, "stringArgument", PrimitiveTypeKind.String, "prefix");
            functions.AddFunction(PrimitiveTypeKind.Boolean, "EndsWith", PrimitiveTypeKind.String, "stringArgument", PrimitiveTypeKind.String, "suffix");
            
            #endregion

            #region DateTime Functions
            
            PrimitiveTypeKind[] dateTimeParameterTypes = { PrimitiveTypeKind.DateTimeOffset,
                                                           PrimitiveTypeKind.DateTime };
            functions.ForTypes(dateTimeParameterTypes, type => functions.AddFunction(PrimitiveTypeKind.Int32, "Year", type, "dateValue"));
            functions.ForTypes(dateTimeParameterTypes, type => functions.AddFunction(PrimitiveTypeKind.Int32, "Month", type, "dateValue"));
            functions.ForTypes(dateTimeParameterTypes, type => functions.AddFunction(PrimitiveTypeKind.Int32, "Day", type, "dateValue"));
            functions.ForTypes(dateTimeParameterTypes, type => functions.AddFunction(PrimitiveTypeKind.Int32, "DayOfYear", type, "dateValue"));

            PrimitiveTypeKind[] timeParameterTypes = { PrimitiveTypeKind.DateTimeOffset,
                                                       PrimitiveTypeKind.DateTime,
                                                       PrimitiveTypeKind.Time };
            functions.ForTypes(timeParameterTypes, type => functions.AddFunction(PrimitiveTypeKind.Int32, "Hour", type, "timeValue"));
            functions.ForTypes(timeParameterTypes, type => functions.AddFunction(PrimitiveTypeKind.Int32, "Minute", type, "timeValue"));
            functions.ForTypes(timeParameterTypes, type => functions.AddFunction(PrimitiveTypeKind.Int32, "Second", type, "timeValue"));
            functions.ForTypes(timeParameterTypes, type => functions.AddFunction(PrimitiveTypeKind.Int32, "Millisecond", type, "timeValue"));

            functions.AddFunction(PrimitiveTypeKind.DateTime, "CurrentDateTime");
            functions.AddFunction(PrimitiveTypeKind.DateTimeOffset, "CurrentDateTimeOffset");
            functions.AddFunction(PrimitiveTypeKind.Int32, "GetTotalOffsetMinutes", PrimitiveTypeKind.DateTimeOffset, "dateTimeOffsetArgument");
            functions.AddFunction(PrimitiveTypeKind.DateTime, "CurrentUtcDateTime");
            
            //TruncateTime
            functions.ForTypes(dateTimeParameterTypes, type => functions.AddFunction(type, "TruncateTime", type, "dateValue"));
            
            //DateTime constructor
            functions.AddFunction(PrimitiveTypeKind.DateTime, "CreateDateTime", PrimitiveTypeKind.Int32, "year",
                                                                                PrimitiveTypeKind.Int32, "month",
                                                                                PrimitiveTypeKind.Int32, "day",
                                                                                PrimitiveTypeKind.Int32, "hour",
                                                                                PrimitiveTypeKind.Int32, "minute",
                                                                                PrimitiveTypeKind.Double, "second");


            //DateTimeOffset constructor
            functions.AddFunction(PrimitiveTypeKind.DateTimeOffset, "CreateDateTimeOffset", PrimitiveTypeKind.Int32, "year",
                                                                                            PrimitiveTypeKind.Int32, "month",
                                                                                            PrimitiveTypeKind.Int32, "day",
                                                                                            PrimitiveTypeKind.Int32, "hour",
                                                                                            PrimitiveTypeKind.Int32, "minute",
                                                                                            PrimitiveTypeKind.Double, "second", 
                                                                                            PrimitiveTypeKind.Int32, "timeZoneOffset");

            //Time constructor
            functions.AddFunction(PrimitiveTypeKind.Time, "CreateTime", PrimitiveTypeKind.Int32, "hour", PrimitiveTypeKind.Int32, "minute", PrimitiveTypeKind.Double, "second");

            //Date and time addition functions
            functions.ForTypes(dateTimeParameterTypes, type => functions.AddFunction(type, "AddYears", type, "dateValue", PrimitiveTypeKind.Int32, "addValue"));
            functions.ForTypes(dateTimeParameterTypes, type => functions.AddFunction(type, "AddMonths", type, "dateValue", PrimitiveTypeKind.Int32, "addValue"));
            functions.ForTypes(dateTimeParameterTypes, type => functions.AddFunction(type, "AddDays", type, "dateValue", PrimitiveTypeKind.Int32, "addValue"));

            functions.ForTypes(timeParameterTypes, type => functions.AddFunction(type, "AddHours", type, "timeValue", PrimitiveTypeKind.Int32, "addValue"));
            functions.ForTypes(timeParameterTypes, type => functions.AddFunction(type, "AddMinutes", type, "timeValue", PrimitiveTypeKind.Int32, "addValue"));
            functions.ForTypes(timeParameterTypes, type => functions.AddFunction(type, "AddSeconds", type, "timeValue", PrimitiveTypeKind.Int32, "addValue"));
            functions.ForTypes(timeParameterTypes, type => functions.AddFunction(type, "AddMilliseconds", type, "timeValue", PrimitiveTypeKind.Int32, "addValue"));
            functions.ForTypes(timeParameterTypes, type => functions.AddFunction(type, "AddMicroseconds", type, "timeValue", PrimitiveTypeKind.Int32, "addValue"));
            functions.ForTypes(timeParameterTypes, type => functions.AddFunction(type, "AddNanoseconds", type, "timeValue", PrimitiveTypeKind.Int32, "addValue"));
            
            // Date and time diff functions
            functions.ForTypes(dateTimeParameterTypes, type => functions.AddFunction(PrimitiveTypeKind.Int32, "DiffYears", type, "dateValue1", type, "dateValue2"));
            functions.ForTypes(dateTimeParameterTypes, type => functions.AddFunction(PrimitiveTypeKind.Int32, "DiffMonths", type, "dateValue1", type, "dateValue2"));
            functions.ForTypes(dateTimeParameterTypes, type => functions.AddFunction(PrimitiveTypeKind.Int32, "DiffDays", type, "dateValue1", type, "dateValue2"));
            
            functions.ForTypes(timeParameterTypes, type => functions.AddFunction(PrimitiveTypeKind.Int32, "DiffHours", type, "timeValue1", type, "timeValue2"));
            functions.ForTypes(timeParameterTypes, type => functions.AddFunction(PrimitiveTypeKind.Int32, "DiffMinutes", type, "timeValue1", type, "timeValue2"));
            functions.ForTypes(timeParameterTypes, type => functions.AddFunction(PrimitiveTypeKind.Int32, "DiffSeconds", type, "timeValue1", type, "timeValue2"));
            functions.ForTypes(timeParameterTypes, type => functions.AddFunction(PrimitiveTypeKind.Int32, "DiffMilliseconds", type, "timeValue1", type, "timeValue2"));
            functions.ForTypes(timeParameterTypes, type => functions.AddFunction(PrimitiveTypeKind.Int32, "DiffMicroseconds", type, "timeValue1", type, "timeValue2"));
            functions.ForTypes(timeParameterTypes, type => functions.AddFunction(PrimitiveTypeKind.Int32, "DiffNanoseconds", type, "timeValue1", type, "timeValue2"));
                        
            #endregion // DateTime Functions
            
            #region Math Functions

            // Overloads for ROUND, FLOOR, CEILING functions
            parameterTypes = new[] { PrimitiveTypeKind.Single,
                                     PrimitiveTypeKind.Double,
                                     PrimitiveTypeKind.Decimal };
            functions.ForTypes(parameterTypes, type => functions.AddFunction(type, "Round", type, "value"));
            functions.ForTypes(parameterTypes, type => functions.AddFunction(type, "Floor", type, "value"));
            functions.ForTypes(parameterTypes, type => functions.AddFunction(type, "Ceiling", type, "value"));

            // Overloads for ROUND, TRUNCATE
            parameterTypes = new [] { PrimitiveTypeKind.Double,
                                      PrimitiveTypeKind.Decimal };
            functions.ForTypes(parameterTypes, type => functions.AddFunction(type, "Round", type, "value", PrimitiveTypeKind.Int32, "digits"));
            functions.ForTypes(parameterTypes, type => functions.AddFunction(type, "Truncate", type, "value", PrimitiveTypeKind.Int32, "digits"));
            
            // Overloads for ABS functions
            parameterTypes = new[] { PrimitiveTypeKind.Decimal,
                                     PrimitiveTypeKind.Double,
                                     PrimitiveTypeKind.Int16,
                                     PrimitiveTypeKind.Int32,
                                     PrimitiveTypeKind.Int64,
                                     PrimitiveTypeKind.Byte,
                                     PrimitiveTypeKind.Single };
            functions.ForTypes(parameterTypes, type => functions.AddFunction(type, "Abs", type, "value"));
            
            // Overloads for POWER functions
            PrimitiveTypeKind[] powerFirstParameterTypes = { PrimitiveTypeKind.Decimal,
                                        PrimitiveTypeKind.Double,
                                        PrimitiveTypeKind.Int32,
                                        PrimitiveTypeKind.Int64};

            PrimitiveTypeKind[] powerSecondParameterTypes = { PrimitiveTypeKind.Decimal,
                                        PrimitiveTypeKind.Double,
                                        PrimitiveTypeKind.Int64 };

            foreach (PrimitiveTypeKind kind1 in powerFirstParameterTypes)
            {
                foreach (PrimitiveTypeKind kind2 in powerSecondParameterTypes)
                {
                    functions.AddFunction(kind1, "Power", kind1, "baseArgument", kind2, "exponent");
                }
            }

            #endregion // Math Functions

            #region Bitwise Functions

            // Overloads for BitwiseAND, BitwiseNOT, BitwiseOR, BitwiseXOR functions
            parameterTypes = new[] { PrimitiveTypeKind.Int16,
                                     PrimitiveTypeKind.Int32,
                                     PrimitiveTypeKind.Int64,
                                     PrimitiveTypeKind.Byte };

            functions.ForTypes(parameterTypes, type => functions.AddFunction(type, "BitwiseAnd", type, "value1", type, "value2"));
            functions.ForTypes(parameterTypes, type => functions.AddFunction(type, "BitwiseOr", type, "value1", type, "value2"));
            functions.ForTypes(parameterTypes, type => functions.AddFunction(type, "BitwiseXor", type, "value1", type, "value2"));
            functions.ForTypes(parameterTypes, type => functions.AddFunction(type, "BitwiseNot", type, "value"));
            
            #endregion

            #region Misc Functions

            functions.AddFunction(PrimitiveTypeKind.Guid, "NewGuid");
            
            #endregion // Misc Functions

            #region Spatial Functions

            EdmProviderManifestSpatialFunctions.AddFunctions(functions);

            #endregion

            System.Collections.ObjectModel.ReadOnlyCollection<EdmFunction> readOnlyFunctions = functions.ToFunctionCollection();

            Interlocked.CompareExchange<System.Collections.ObjectModel.ReadOnlyCollection<EdmFunction>>(ref _functions, readOnlyFunctions, null);
        }