/// <summary> /// Using this method user could modify scalar function code generation options: /// <list type="bullet"> /// <item>Return scalar value or tuple descriptor: <see cref="ScalarFunctionModel.Return"/> or <see cref="ScalarFunctionModel.ReturnTuple"/></item> /// <item>Function metadata: <see cref="ScalarFunctionModelBase.Metadata"/></item> /// <item>Method code-generation options: <see cref="FunctionModelBase.Method"/></item> /// <item>Parameters: <see cref="FunctionModelBase.Parameters"/></item> /// </list> /// </summary> /// <param name="typeParser">Type parser service to create type tokens.</param> /// <param name="functionModel">Function model descriptor.</param> public virtual void PreprocessScalarFunction(ITypeParser typeParser, ScalarFunctionModel functionModel) { }
/// <summary> /// Generates scalar function model from schema data. /// </summary> /// <param name="dataContext">Data context model.</param> /// <param name="func">Function schema.</param> /// <param name="defaultSchemas">List of default database schema names.</param> private void BuildScalarFunction(DataContextModel dataContext, ScalarFunction func, ISet <string> defaultSchemas) { var(name, isNonDefaultSchema) = ProcessObjectName(func.Name, defaultSchemas); var method = new MethodModel( _namingServices.NormalizeIdentifier(_options.DataModel.ProcedureNameOptions, (func.Name.Package != null ? $"{func.Name.Package}_" : null) + name.Name)) { Modifiers = Modifiers.Public | Modifiers.Static, Summary = func.Description }; var metadata = new FunctionMetadata() { Name = name, ServerSideOnly = true }; var funcModel = new ScalarFunctionModel(name, method, metadata); BuildParameters(func.Parameters, funcModel.Parameters); // thanks to pgsql, scalar function could return not only scalar, but also tuple or just nothing switch (func.Result.Kind) { case ResultKind.Scalar: { var scalarResult = (ScalarResult)func.Result; var typeMapping = MapType(scalarResult.Type); funcModel.Return = typeMapping.CLRType.WithNullability(scalarResult.Nullable); // TODO: DataType not used by current scalar function mapping API break; } case ResultKind.Tuple: { var tupleResult = (TupleResult)func.Result; // tuple model class var @class = new ClassModel( _namingServices.NormalizeIdentifier( _options.DataModel.FunctionTupleResultClassNameOptions, func.Name.Name)) { Modifiers = Modifiers.Public | Modifiers.Partial }; funcModel.ReturnTuple = new TupleModel(@class) { CanBeNull = tupleResult.Nullable }; // fields order must be preserved, as tuple fields mapped by ordinal foreach (var field in tupleResult.Fields) { var typeMapping = MapType(field.Type); var prop = new PropertyModel(_namingServices.NormalizeIdentifier(_options.DataModel.FunctionTupleResultPropertyNameOptions, field.Name ?? "Field"), typeMapping.CLRType.WithNullability(field.Nullable)) { Modifiers = Modifiers.Public, IsDefault = true, HasSetter = true }; funcModel.ReturnTuple.Fields.Add(new TupleFieldModel(prop, field.Type) { DataType = typeMapping.DataType }); } break; } case ResultKind.Void: // just regular postgresql void function, nothing to see here... // because function must have return type to be callable in query, we set return type to object? funcModel.Return = WellKnownTypes.System.ObjectNullable; break; } _interceptors.PreprocessScalarFunction(_languageProvider.TypeParser, funcModel); if (isNonDefaultSchema && _options.DataModel.GenerateSchemaAsType) { GetOrAddAdditionalSchema(dataContext, func.Name.Schema !).ScalarFunctions.Add(funcModel); } else { dataContext.ScalarFunctions.Add(funcModel); } }