{"id":19992691,"url":"https://github.com/DataBooster/DbWebApi","last_synced_at":"2025-05-04T11:31:47.524Z","repository":{"id":26317657,"uuid":"29765901","full_name":"DataBooster/DbWebApi","owner":"DataBooster","description":"(Migrated from CodePlex) DbWebApi is a .Net library that implement an entirely generic Web API (RESTful) for HTTP clients to call database (Oracle \u0026 SQL Server) stored procedures or functions in a managed way out-of-the-box without any configuration or coding.","archived":false,"fork":false,"pushed_at":"2022-07-02T02:21:27.000Z","size":15739,"stargazers_count":98,"open_issues_count":2,"forks_count":50,"subscribers_count":17,"default_branch":"master","last_synced_at":"2024-05-29T01:06:46.340Z","etag":null,"topics":["ado-net","asp-net","bulk","csv","dataaccess","database","ddtek","excel","json","jsonp","odp-net","oracle","powershell","razor","restful","sql-server","stored-procedure","webapi","xlsx","xml"],"latest_commit_sha":null,"homepage":"https://dbwebapi.codeplex.com/","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/DataBooster.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-01-24T04:51:04.000Z","updated_at":"2023-10-20T13:50:03.000Z","dependencies_parsed_at":"2022-09-05T13:00:53.392Z","dependency_job_id":null,"html_url":"https://github.com/DataBooster/DbWebApi","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DataBooster%2FDbWebApi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DataBooster%2FDbWebApi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DataBooster%2FDbWebApi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DataBooster%2FDbWebApi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DataBooster","download_url":"https://codeload.github.com/DataBooster/DbWebApi/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252329596,"owners_count":21730647,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ado-net","asp-net","bulk","csv","dataaccess","database","ddtek","excel","json","jsonp","odp-net","oracle","powershell","razor","restful","sql-server","stored-procedure","webapi","xlsx","xml"],"created_at":"2024-11-13T04:52:16.611Z","updated_at":"2025-05-04T11:31:42.513Z","avatar_url":"https://github.com/DataBooster.png","language":"C#","readme":"﻿# DbWebApi\r\n-- -- -- -- -- -- -- -- -- -- -- -- Extension to ASP.NET Web API (RESTful)\r\n\r\n### What is it?\r\n\r\nWith DbWebApi you can access SQL Server or Oracle package stored procedures in a managed way out of the box (like \r\n`http://BaseUrl/fully_qualified_name_of_stored_procedure/mediatype`) from any http client, get the results as JSON, BSON, XML, CSV, Excel xlsx, JSONP, or any text generated by Razor dynamic templating. For examples,\r\n\r\n**SQL Server**:\r\n* `http://dbwebapi.dev.com/sqldev/TestDb.dbo.your_sp/json`\r\n* `http://dbwebapi.dev.com/sqldev/TestDb.dbo.your_sp/bson`\r\n* `http://dbwebapi.dev.com/sqldev/TestDb.dbo.your_sp/jsonp?callback=jsFunc1`\r\n* `http://dbwebapi.dev.com/sqldev/TestDb.dbo.your_sp/xml`\r\n* `http://dbwebapi.dev.com/sqldev/TestDb.dbo.your_sp/xlsx?filename=Rpt2015`\r\n* `http://dbwebapi.dev.com/sqldev/TestDb.dbo.your_sp/csv?resultset=0\u0026filename=Rpt2015`\r\n* `http://dbwebapi.dev.com/sqldev/TestDb.dbo.your_sp/razor?RazorTemplate=outTemplateSpParameter`\r\n\r\n**Oracle**:\r\n* `http://dbwebapi.dev.com/oradev/test_schema.prj_package.your_sp/json`\r\n* `http://dbwebapi.dev.com/oradev/test_schema.prj_package.your_sp/bson`\r\n* `http://dbwebapi.dev.com/oradev/test_schema.prj_package.your_sp/jsonp?callback=jsFunc1`\r\n* `http://dbwebapi.dev.com/oradev/test_schema.prj_package.your_sp/xml`\r\n* `http://dbwebapi.dev.com/oradev/test_schema.prj_package.your_sp/xlsx?filename=Rpt2015`\r\n* `http://dbwebapi.dev.com/oradev/test_schema.prj_package.your_sp/csv?resultset=0\u0026filename=Rpt2015`\r\n* `http://dbwebapi.dev.com/sqldev/test_schema.prj_package.your_sp/razor?RazorTemplate=outTemplateSpParameter`\r\n\r\nThe input parameters of your stored procedure can be supplied in URL query-string or in request body by JSON (recommended), XML or multipart/form-data. Oracle PL/SQL Associative Array Parameter (Bulk Bind - for bulk insert, bulk update) and SQL Server Table-Valued Parameter are natively supported for high-performance.\r\n\r\nDbWebApi handles type inferencing, for example, if a client passes in a Base64 string and your stored procedure expects to receive a binary input, the Base64 string is automatically decoded into the binary parameter.\r\n\r\nIf the client further wraps a batch of parameter sets into an array as the HTTP request body, the server will sequentially call the stored procedure by each parameter set in the array, and wrap all the result sets in a more outer array before return to the client.\r\n\r\n***\r\n### Contents:\r\n- [Overview](#Overview)\r\n- [What are the benefits of DbWebApi?](#what-are-the-benefits-of-dbwebapi)\r\n- [Usage - server side](#usage)\r\n    - [ApiController](#apicontroller)\r\n    - [Web.config](#webconfig)\r\n    - [HTTP Request](#http-request)\r\n        - [Url](#url)\r\n        - [Input Parameters](#input-parameters)\r\n            - [Simple Parameters](#simple-parameters)\r\n            - [Array of Parameter Sets](#array-of-parameter-sets)\r\n            - [PL/SQL Associative Array Parameters](#associative-array-parameters)\r\n            - [Table-Valued Parameters](#table-valued-parameters)\r\n\t\t\t- [Summary of Input Parameters and Execution Modes](#summary-of-input-parameters-and-execution-modes)\r\n        - [Accept Response MediaType](#accept-response-mediatype)\r\n            - [JSON](#accept-json)\r\n            - [BSON](#accept-bson) _(only available to targetFramework=\"4.5\" or higher - ASP.NET Web API 2)_\r\n            - [JSONP](#accept-jsonp)\r\n            - [XML](#accept-xml)\r\n            - [Excel .xlsx](#accept-xlsx)\r\n            - [CSV](#accept-csv)\r\n            - [Razor Templating](#accept-razor-templating)\r\n            - [Other MediaTypes](#accept-other-mediatypes)\r\n                - [Control in low level](#other-mediatype-low-lev)\r\n                - [Control in high level](#other-mediatype-high-lev)\r\n    - [HTTP Response](#http-response)\r\n        - [Response data internal structure](#response-data-internal-structure)\r\n        - [Response body formats](#response-body-formats)\r\n            - [application/json, text/json](#applicationjson-textjson)\r\n            - [application/xml, text/xml](#applicationxml-textxml)\r\n            - [text/csv](#textcsv)\r\n        - [Property Naming Convention](#property-naming-convention)\r\n        - [Exceptions](#exceptions)\r\n        - [Permission Control](#permission-control)\r\n        - [UserName](#username)\r\n    - [Performance](#performance)\r\n    - [Bulk Manipulation](#bulk-manipulation)\r\n        - [Quasi Bulk (BulkExecute action)](#quasi-bulk)\r\n        - [Thorough Bulk - Table-Valued Parameters or PL/SQL Associative Array Parameters](#thorough-bulk)\r\n- [Clients](#clients)\r\n    - [Swagger UI](#swagger-ui)\r\n    - [.Net Client](#net-client)\r\n    - [JavaScript Client](#javascript-client)\r\n        - [Cross-domain](#cross-domain)\r\n            - [CORS](#cors)\r\n            - [JSONP](#jsonp-added-server-lib-v124-client-js-v108-alpha)\r\n    - [AngularJS Client](#angularjs-client)\r\n    - [Angular 6+ Client](#angular-6-client)\r\n    - [Python(3+) Client](#python3-client)\r\n    - [PowerShell Client](#powershell-client)\r\n        - [Bulk Data Post Back](#bulk-data-post-back)\r\n            - [Hundreds or less](#hundreds-or-less)\r\n            - [Thousands or more](#thousands-or-more)\r\n                - [Table-Valued Parameters](#ps-table-valued-parameters)\r\n                - [PL/SQL Associative Array Parameters](#ps-associative-array-parameters)\r\n    - [Windows Command Line Client](#windows-command-line-client)\r\n    - [Power Query Client - Power BI](#power-query-client)\r\n- [Restrictions](#restrictions)\r\n- [NuGet](#nugget)\r\n    - [Server packages](#server-side)\r\n    - [Clients](#clients-1)\r\n- [SymbolSource](#symbolsource)\r\n- [Examples](#examples)\r\n\r\n***\r\n\r\n### Overview  \r\nDbWebApi is a .Net library that implement an entirely generic Web API for HTTP clients to call database (Oracle \u0026 SQL Server) stored procedures or functions out-of-the-box without any configuration or extra coding, the http response JSON or XML will have all Result Sets, Output Parameters and Return Value. If client request a CSV format (accept: text/csv), the http response will transmit one result set as a CSV stream for large amounts of data. DbWebApi also supports xlsx (Excel 2007/2010) format response for multiple resultsets _(each resultset presents as an Excel worksheet)_. While being regarded as a gateway service, DbWebApi reflects in two directions: Data Access Gateway and Media Format Gateway.\r\n\r\nIn other words, DbWebApi provides an alternative way to implement your Web APIs by implementing some stored procedures or functions in database. The DbWebApi will expose these stored procedures or functions as Web APIs straight away.\r\n\r\nIn essence, DbWebApi is still a ASP.NET Web API instead of a naked tunnel for database. It just be generic, and provides a few extension methods to your ASP.NET Web API services.\r\n- Security:  \r\nThe security of DbWebApi is entirely dependent on what you can do in ASP.NET Web API. What security you did for your existing Web API services, should still apply in the DbWebApi. For information about access control, please see the [[Permission Control](#permission-control)] section later in this wiki.  \r\n_Some people may concern about the name of some stored procedures being exposed to the public. Hereby, it is necessary to clarify that all the exposed names of [stored procedures](https://msdn.microsoft.com/en-us/library/ms190782.aspx) are essentially some names of public services. No matter how hard the service provider try to hide/disguise the name of service function, as long as a service function is a businesses need for the service consumers to invoke, the service consumers always can get the real intention of the service according to its effect. Hiding/disguising service name make no contribution to improve security, it's fundamentally different from hiding any piece of credential information._\r\n\r\n- Data Contract:  \r\nSince there is no setup at all, the domain entities returned from DbWebApi simply reflect the result sets returned from your stored procedure. So the data contract is driven by your stored procedure.  \r\nTo isolate the downstream consumers from the source raw schemas, you can slimly achieve the isolation in your stored procedure only once, or do some data transportation once after DbWebApi.  \r\n_Actually, the contract transformation can be done in any one node of the intermediate links of your data flow. Just to keep the isolation simple, and reduce dogmatic data-isolation repeated in multiple links of a closed process chain over and over again._\r\n\r\n### What are the benefits of DbWebApi?\r\n\r\n- The underlying tenet:  \r\nLess coding, less configuration, less deployment, less maintenance.  \r\nThe conciseness of using DbWebApi is down-to-earth for hands-on developers, to access database stored procedures or functions is completely coding-free and configuration-free. Don't need to explicitly specify any metadata about database objects _(such as parameters type, size, direction... or columns attributes)_ by coding or configuration, don't need to write any controller for handling new data models from database, don't need to write any method for calling new stored procedures or functions ... No more dazzling _The Emperor's New Services(Clothes)_ to test(fitting), deploy or maintain.\r\n- In database applications area, there are a large number of scenarios without substantial logic in data access web services, however they wasted a lot of our efforts on very boring data-moving coding or configurations, we've had enough of it. Since now on, most of thus repetitive works can be dumped onto DbWebApi. Let the application developer focus on the important thing, which is building the functionality that's needed, instead of focusing on all the plumbing underneath it.\r\n- Unlike WCF Data Services or other similar web services, DbWebApi has no design time within the service itself. In terms of the overall system, stored procedures design has already undertaken the corresponding part of contract design when stored procedure based development was adopted as part of the whole development.  \r\n_It's unnecessary to repeat design for the same part of contract again. Repeated designs inevitably lead to redundant configuration, recompilation ... redeployment for every intermediate links._\r\n- DbWebApi can coexist within your existing ASP.NET Web API, as a supplementary service to reduce new boring manual works for most common of application scenarios.\r\n\r\n## Usage\r\n\r\nAs one of the quickest learning ways, https://github.com/DataBooster/DbWebApi/releases provides several examples of using the DbWebApi library. Download it, select one of the projects to configure the database connection, compile and deploy it to IIS server, then start your intuitive experience by following the registered URL route. You can come back to read the details later in this section when you have time.\r\n\r\n#### ApiController:  \r\nPlease reference the sample [DbWebApiController.cs](https://github.com/DataBooster/DbWebApi/blob/master/Server/Sample/MyDbWebApi/Controllers/DbWebApiController.cs):\r\n``` CSharp\r\nusing System.Net.Http;\r\nusing System.Web.Http;\r\nusing DataBooster.DbWebApi;\r\n\r\nnamespace MyDbWebApi.Controllers\r\n{\r\n    [DbWebApiAuthorize]\r\n    public class DbWebApiController : ApiController\r\n    {\r\n        /// \u003cparam name=\"sp\"\u003eStored Procedure's fully qualified name\u003c/param\u003e\r\n        /// \u003cparam name=\"allParameters\"\u003eAuto-binding from the request body\u003c/param\u003e\r\n        [AcceptVerbs(\"GET\", \"POST\", \"PUT\", \"DELETE\")]\r\n        public HttpResponseMessage DynExecute(string sp, InputParameters allParameters)\r\n        {\r\n            // Supplement input parameters from URI query string.\r\n            allParameters = InputParameters.SupplementFromQueryString(allParameters, Request);\r\n\r\n            // The main entry point to call the DbWebApi.\r\n            return this.DynExecuteDbApi(sp, allParameters);\r\n        }\r\n    }\r\n}\r\n```\r\nThat's all, **DynExecute** is the extension method to ApiController.  \r\n_(It combines the Execute and BulkExecute methods internally, auto-detect a post request body, invoking BulkExecute if sets of input parameters are encapsulated in an arrray; or invoking Execute if input parameters are encapsulated in a single dictionary)_  \r\nDetail in [DbWebApiController.cs](https://github.com/DataBooster/DbWebApi/blob/master/Server/Sample/MyDbWebApi/Controllers/DbWebApiController.cs).\r\nAnd the sample [WebApiConfig.cs](https://github.com/DataBooster/DbWebApi/blob/master/Server/Sample/MyDbWebApi/App_Start/WebApiConfig.cs) demonstrates the Web API routing for this action.\r\n\r\n#### Web.config  \r\n\"\u003cu\u003eDataBooster.DbWebApi.MainConnection\u003c/u\u003e\" is the only one configuration item needs to be customized:\r\n``` Xml\r\n\u003cconnectionStrings\u003e\r\n  \u003cadd name=\"DataBooster.DbWebApi.MainConnection\" providerName=\"System.Data.SqlClient\" connectionString=\"Data Source=.\\SQLEXPRESS;Initial Catalog=SAMPLEDB;Integrated Security=SSPI\" /\u003e\r\n\u003c/connectionStrings\u003e\r\n```\r\n\r\n#### HTTP Request  \r\n##### Url:  \r\nAs registered in your [WebApiConfig](https://github.com/DataBooster/DbWebApi/blob/master/Server/Sample/MyDbWebApi/App_Start/WebApiConfig.cs) Routes (e.g. http://BaseUrl/Your.StoredProcedure.FullyQualifiedName)  \r\n##### Input Parameters  \r\nDbWebApi takes advantages of the Parameter-Binding mechanism in ASP.NET Web API. Current implementation is self-adaptive to four kinds of media types:\r\n- JSON format request _(Content-Type: application/json or text/json)_\r\n- XML format request _(Content-Type: application/xml or text/xml)_\r\n- HTML Form request _(Content-Type: application/x-www-form-urlencoded)_\r\n- Multipart MIME request _(Content-Type: multipart/form-data)_ - for file upload\r\n\r\n\r\n###### \u003cu\u003eSimple Parameters\u003c/u\u003e  \r\nOnly required input-parameters of the stored procedure/function need to be specified in your request body as JSON format _(Content-Type: application/json)_. Don't put parameter prefix ('@' or ':') in the JSON body.  \r\nFor example, a Stored Procedure _(in SQL Server)_:  \r\n``` SQL\r\nALTER PROCEDURE dbo.prj_GetRule\r\n    @inRuleDate  datetime,\r\n    @inRuleId    int,\r\n    @inWeight    float(6) = 0.1,\r\n    @outRuleDesc varchar(256) = NULL OUTPUT\r\nAS  ...\r\n```\r\nThe payload JSON should look like:  \r\n``` JSON\r\n{\r\n    \"inRuleDate\":\"2015-02-03T00:00:00Z\",\r\n    \"inRuleId\":108\r\n}\r\n```\r\nParameter names are case-insensitive.\r\n\r\nIf you use XML request _(content-Type: application/xml or text/xml)_, the message body should look like:\r\n``` XML\r\n\u003cAnyRootName\u003e\r\n  \u003cinRuleDate\u003e2015-02-03T00:00:00Z\u003c/inRuleDate\u003e\r\n  \u003cinRuleId\u003e108\u003c/inRuleId\u003e\r\n\u003c/AnyRootName\u003e\r\n```\r\nor\r\n``` XML\r\n\u003cAnyRootName xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:x=\"http://www.w3.org/2001/XMLSchema\"\u003e\r\n  \u003cinRuleDate i:type=\"x:dateTime\"\u003e2015-02-03T00:00:00Z\u003c/inRuleDate\u003e\r\n  \u003cinRuleId i:type=\"x:int\"\u003e108\u003c/inRuleId\u003e\r\n\u003c/AnyRootName\u003e\r\n```\r\nor\r\n``` XML\r\n\u003cAnyRootName xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:z=\"http://schemas.microsoft.com/2003/10/Serialization/\"\u003e\r\n  \u003cinRuleDate z:Type=\"System.DateTime\" z:Assembly=\"0\"\u003e2015-02-03T00:00:00Z\u003c/inRuleDate\u003e\r\n  \u003cinRuleId z:Type=\"System.Int32\" z:Assembly=\"0\"\u003e108\u003c/inRuleId\u003e\r\n\u003c/AnyRootName\u003e\r\n```\r\nor\r\n``` XML\r\n\u003cAnyName inRuleDate=\"2015-02-03T00:00:00Z\" inRuleId=\"108\" /\u003e\r\n```\r\nIf you want to use HTML Form _(although you are unlikely to do so)_, the form body can be:\r\n``` HTML\r\n\u003cform id=\"form1\" method=\"post\" action=\"api/Your.StoredProcedure.FullyQualifiedName/json\"\r\n    enctype=\"application/x-www-form-urlencoded\"\u003e\r\n    \u003cdiv\u003e\u003clabel for=\"inRuleDate\"\u003eInput Rule Date\u003c/label\u003e\u003c/div\u003e\r\n    \u003cdiv\u003e\u003cinput name=\"inRuleDate\" type=\"text\" /\u003e\u003c/div\u003e\r\n    \u003cdiv\u003e\u003clabel for=\"inRuleId\"\u003eInput Rule Id\u003c/label\u003e\u003c/div\u003e\r\n    \u003cdiv\u003e\u003cinput name=\"inRuleId\" type=\"text\" /\u003e\u003c/div\u003e\r\n    \u003cdiv\u003e\u003cinput type=\"submit\" value=\"Submit\" /\u003e\u003c/div\u003e\r\n\u003c/form\u003e\r\n```\r\nIf you need to upload some files into database, Multipart Form Data is a really simple way:\r\n``` HTML\r\n\u003cform id=\"form1\" method=\"post\" action=\"api/Your.StoredProcedure.FullyQualifiedName/json\"\r\n    enctype=\"multipart/form-data\"\u003e\r\n    \u003cdiv\u003e\u003clabel for=\"inRuleDate\"\u003eInput Rule Date\u003c/label\u003e\u003c/div\u003e\r\n    \u003cdiv\u003e\u003cinput name=\"inRuleDate\" type=\"text\" /\u003e\u003c/div\u003e\r\n    \u003cdiv\u003e\u003clabel for=\"inRuleId\"\u003eInput Rule Id\u003c/label\u003e\u003c/div\u003e\r\n    \u003cdiv\u003e\u003cinput name=\"inRuleId\" type=\"text\" /\u003e\u003c/div\u003e\r\n    \u003cdiv\u003e\u003clabel for=\"inImageData\"\u003eImage File\u003c/label\u003e\u003cinput name=\"inImageData\" type=\"file\" /\u003e\u003c/div\u003e\r\n    \u003cdiv\u003e\u003clabel for=\"inTextData\"\u003eText File\u003c/label\u003e\u003cinput name=\"inTextData\" type=\"file\" /\u003e\u003c/div\u003e\r\n    \u003cdiv\u003e\u003cinput type=\"submit\" value=\"Submit\" /\u003e\u003c/div\u003e\r\n\u003c/form\u003e\r\n```\r\nNotes:\r\n- For a binary type parameter of stored procedure, the uploaded binary data will be passed in straightforward without any transformation; _(for above instance, \"inImageData\" parameter)_  \r\n- For a string(text) type parameter of stored procedure, the uploaded stream will be treated as UTF-8 encoding to be decoded back to a string, unless your file contains BOM (Byte Order Marks) _- it will detect encoding from the BOM_. Then pass the string into the stores procedure. _(for above instance, \"inTextData\" parameter)_\r\n\r\n\u0026nbsp;\r\n\r\n###### \u003cu\u003eArray of Parameter Sets\u003c/u\u003e  \r\nTo pass bulk of same structure data back to database, you can just encapsulate all sets of parameters into an array like:\r\n``` JSON\r\n[\r\n  {\r\n    \"inRuleDate\":\"2015-02-03T00:00:00Z\",\r\n    \"inRuleId\":108\r\n  },\r\n  {\r\n    \"inRuleDate\":\"2015-02-04T00:00:00Z\",\r\n    \"inRuleId\":109\r\n  },\r\n  {\r\n    \"inRuleDate\":\"2015-02-05T00:00:00Z\",\r\n    \"inRuleId\":110\r\n  },\r\n  {\r\n    \"inRuleDate\":\"2015-02-06T00:00:00Z\",\r\n    \"inRuleId\":111\r\n  }\r\n]\r\n```\r\nFor above example, the Web API server side will iteratively invoking database stored procedure dbo.prj_GetRule 4 times and the response body will be an array that contains the corresponding results of 4 times executions.  \r\n*Notes:  \r\nBulkExecute reads bulk sets of parameters from the request message body only, it means only HTTP POST and PUT can be used to send BulkExecute request, and only JSON and XML are acceptable media types for bulk response. If this limitation does counteract its conveniences you gain, please consider using following alternatives.*\r\n\r\n* \u003ca name=\"associative-array-parameters\"\u003e\u003c/a\u003e[PL/SQL Associative Array Parameters](http://docs.oracle.com/cd/E51173_01/win.122/e17732/featOraCommand.htm#BABBDHBB) (Oracle):  \r\nIn Oracle database, you can use PL/SQL Associative Array Parameters (Bulk Binds) to reduce loop overhead for performance sake _(avoid too many context switches between the PL/SQL and SQL engines)_. For example, in database side:\r\n    ``` PLSQL\r\n    NUMBER_EMPTY_ARRAY  DBMS_UTILITY.NUMBER_ARRAY;\r\n\r\n    PROCEDURE WRITE_BULK_DATA\r\n    (\r\n        inGroupID       PLS_INTEGER,\r\n        inItemValues    DBMS_UTILITY.NUMBER_ARRAY := NUMBER_EMPTY_ARRAY,\r\n        RC1             OUT SYS_REFCURSOR\r\n    );\r\n    ```\r\n    The payload JSON should look like:\r\n    ``` JSON\r\n    {\r\n        \"inGroupID\": 108,\r\n        \"inItemValues\": [\r\n                            0,\r\n                            1,\r\n                            0.618,\r\n                            1001,\r\n                            -3.1415926585\r\n                        ]\r\n    }\r\n    ```\r\n    _Tips:  \r\nOracle ODP.NET does not support binding an **empty array** to a [PL/SQL Associative Array Parameter](https://docs.oracle.com/database/121/ODPNT/OracleParameterClass.htm#ODPNT1890). To work around this limitation, simply declaring an empty associative array as the default value for the parameter. Because the underlying DataBooster library does NOT pass the empty array to database for that particular parameter at all, the database engine will then use the DEFAULT value (which is an empty associative array declared in your stored procedures package) for that parameter, as shown above._  \r\n \r\n* \u003ca name=\"table-valued-parameters\"\u003e\u003c/a\u003e[Table-Valued Parameters](https://msdn.microsoft.com/en-us/library/bb675163.aspx) (SQL Server 2008+):  \r\nIn SQL Server 2008 or later, [Table-Valued Parameter](https://msdn.microsoft.com/en-us/library/bb510489.aspx) provides an equifinality of Associative Array Bulk Binds, but the implementation styles have different looks. For example, in database side:\r\n    ``` SQL\r\n    CREATE TYPE dbo.CategoryTableType AS TABLE\r\n        ( CategoryID int, Weight float(6), CategoryName nvarchar(50) )\r\n\r\n    CREATE PROCEDURE dbo.usp_UpdateCategories \r\n        (@inGroupID int, @inTvpCategories dbo.CategoryTableType READONLY)\r\n    ```\r\n    The payload JSON should look like:  \r\n    ``` JSON\r\n    {\r\n        \"inGroupID\": 108,\r\n        \"inTvpCategories\": [\r\n                               {\r\n                                   \"CategoryID\": 1,\r\n                                   \"Weight\": 0.15,\r\n                                   \"CategoryName\": \"Peach Blossom\"\r\n                               },\r\n                               {\r\n                                   \"CategoryID\": 2,\r\n                                   \"Weight\": 0.38,\r\n                                   \"CategoryName\": \"Peony\"\r\n                               },\r\n                               {\r\n                                   \"CategoryID\": 3,\r\n                                   \"Weight\": 0.26,\r\n                                   \"CategoryName\": \"Tulip\"\r\n                               },\r\n                               {\r\n                                   \"CategoryID\": 4,\r\n                                   \"Weight\": 0.06,\r\n                                   \"CategoryName\": \"Cymbidium Orchis\"\r\n                               },\r\n                               {\r\n                                   \"CategoryID\": 5,\r\n                                   \"Weight\": 0.18,\r\n                                   \"CategoryName\": \"Water Lily\"\r\n                               }\r\n                           ]\r\n    }\r\n    ```\r\n    *Tips:  \r\n    Unlike outer parameters bind-by-name (as above exampled \"inGroupID\" and \"inTvpCategories\"), Inside of the Table-Valued Parameter SQL Server actually behaves bind-by-position. No matter what you name those internal columns (as above exampled \"CategoryID\", \"Weight\", \"CategoryName\"), it made no difference to SQL Server. Below JSON input would get the same results as above.*\r\n    ``` JSON\r\n    {\r\n        \"inGroupID\": 108,\r\n        \"inTvpCategories\": [\r\n                               {\r\n                                   \"C01\": 1,\r\n                                   \"C02\": 0.15,\r\n                                   \"C03\": \"Peach Blossom\"\r\n                               },\r\n                               {\r\n                                   \"C01\": 2,\r\n                                   \"C02\": 0.38,\r\n                                   \"C03\": \"Peony\"\r\n                               },\r\n                               {\r\n                                   \"C01\": 3,\r\n                                   \"C02\": 0.26,\r\n                                   \"C03\": \"Tulip\"\r\n                               },\r\n                               {\r\n                                   \"C01\": 4,\r\n                                   \"C02\": 0.06,\r\n                                   \"C03\": \"Cymbidium Orchis\"\r\n                               },\r\n                               {\r\n                                   \"C01\": 5,\r\n                                   \"C02\": 0.18,\r\n                                   \"C03\": \"Water Lily\"\r\n                               }\r\n                           ]\r\n    }\r\n    ```\r\n    *According to this, you can control the order of properties in JSON Serialization by this sort of lazy way.*   \r\n    Note:  \r\n    If you don't have any item in the \"inTvpCategories\", but you still want to execute the stored procedure dbo.usp_UpdateCategories with an empty table-value, please remove the whole \"inTvpCategories\" parameter from the JSON payload as below:\r\n    ``` JSON\r\n    {\r\n        \"inGroupID\": 108\r\n    }\r\n    ```\r\n\u0026nbsp;\r\n\r\n###### Summary of Input Parameters and Execution Modes\r\nThe service execution mode is determined by the input payload from client's HTTP request body.\r\n- When a HTTP client sends a GET request (without message body),  \r\nDbWebApi works in single-execution mode only, and all input parameters are extracted from the URL query string;\r\n- When a HTTP client sends a POST (or PUT, DELETE) request,  \r\nDbWebApi follows these rules:\r\n\r\n\u003ctable\u003e\r\n  \u003ctr\u003e\r\n    \u003cth\u003eInput Payload\u003cbr/\u003e(Sample JSON)\u003c/th\u003e\r\n    \u003cth\u003eExecution Mode\u003c/th\u003e\r\n    \u003cth\u003eStored Procedure\u003cbr/\u003e(Examples)\u003c/th\u003e\r\n  \u003c/tr\u003e\r\n  \u003ctr\u003e\r\n    \u003ctd\u003e\u003cpre\u003e\r\n{\r\n  \"inPartitionId\": 108,\r\n  \"inItemId\": 101,\r\n  \"inItemName\": \"Test String 1\",\r\n  \"inItemDate\": \"2015-02-03T00:00:00Z\"\r\n}\r\n\u003c/pre\u003e\u003c/td\u003e\r\n    \u003ctd align=\"center\"\u003eSingle Execution\u003c/td\u003e\r\n    \u003ctd rowspan=\"2\"\u003e\u003cu\u003eSQL Server\u003c/u\u003e:\r\n\u003cpre\u003e\r\nALTER PROCEDURE dbo.my_pck_Singly_Ins_Upd\r\n(\r\n    @inPartitionId  int,\r\n    @inItemId       int,\r\n    @inItemName     varchar(256),\r\n    @inItemDate     datetime\r\n)   AS  ...\r\n\u003c/pre\u003e\r\nOr\u003cbr/\u003e\u003cbr/\u003e\u003cu\u003eOracle\u003c/u\u003e:\r\n\u003cpre\u003e\r\nCREATE OR REPLACE PACKAGE SCHEMA.MY_PCK IS\r\n\r\nPROCEDURE SINGLY_INS_UPD\r\n(\r\n    inPartitionId  PLS_INTEGER,\r\n    inItemId       PLS_INTEGER,\r\n    inItemName     VARCHAR2,\r\n    inItemDate     DATE\r\n);\r\n\r\nEND MY_PCK;\r\n\u003c/pre\u003e\u003c/td\u003e\r\n  \u003c/tr\u003e\r\n  \u003ctr\u003e\r\n    \u003ctd\u003e\u003cpre\u003e\r\n[\r\n  {\r\n    \"inPartitionId\": 108,\r\n    \"inItemId\": 101,\r\n    \"inItemName\": \"Test String 1\",\r\n    \"inItemDate\": \"2016-05-01T00:00:00Z\"\r\n  },\r\n  {\r\n    \"inPartitionId\": 108,\r\n    \"inItemId\": 102,\r\n    \"inItemName\": \"Test String 2\",\r\n    \"inItemDate\": \"2016-05-02T00:00:00Z\"\r\n  },\r\n  {\r\n    \"inPartitionId\": 108,\r\n    \"inItemId\": 103,\r\n    \"inItemName\": \"Test String 3\",\r\n    \"inItemDate\": \"2016-05-03T00:00:00Z\"\r\n  }\r\n]\r\n\u003c/pre\u003e\u003c/td\u003e\r\n    \u003ctd align=\"center\"\u003eBulk Execution\u003c/td\u003e\r\n  \u003c/tr\u003e\r\n  \u003ctr\u003e\r\n    \u003ctd\u003e\u003cpre\u003e\r\n{\r\n  \"inPartitionId\": 108,\r\n  \"inItemIds\": [\r\n    101,\r\n    102,\r\n    103\r\n  ],\r\n  \"inItemNames\": [\r\n    \"Test String 1\",\r\n    \"Test String 2\",\r\n    \"Test String 3\"\r\n  ],\r\n  \"inItemDates\": [\r\n    \"2016-05-01T00:00:00Z\",\r\n    \"2016-05-02T00:00:00Z\",\r\n    \"2016-05-03T00:00:00Z\"\r\n  ]\r\n}\r\n\u003c/pre\u003e\u003c/td\u003e\r\n    \u003ctd align=\"center\"\u003ePL/SQL Associative Array\u003cbr /\u003e(Oracle Bulk Binding)\u003c/td\u003e\r\n    \u003ctd\u003e\u003cu\u003eOracle\u003c/u\u003e:\r\n\u003cpre\u003e\r\nCREATE OR REPLACE PACKAGE SCHEMA.MY_PCK IS\r\n\r\nTYPE NUMBER_ARRAY IS TABLE OF NUMBER INDEX BY PLS_INTEGER;\r\nTYPE STRING_ARRAY IS TABLE OF VARCHAR2(256) INDEX BY PLS_INTEGER;\r\nTYPE DATE_ARRAY IS TABLE OF DATE INDEX BY PLS_INTEGER;\r\n\r\nPROCEDURE BULK_INS_UPD\r\n(\r\n    inPartitionId  PLS_INTEGER,\r\n    inItemIds      NUMBER_ARRAY,\r\n    inItemNames    STRING_ARRAY,\r\n    inItemDates    DATE_ARRAY\r\n);\r\n\r\nEND MY_PCK;\r\n\u003c/pre\u003e\u003c/td\u003e\r\n  \u003c/tr\u003e\r\n  \u003ctr\u003e\r\n    \u003ctd\u003e\u003cpre\u003e\r\n{\r\n  \"inPartitionId\": 108,\r\n  \"inTvpItems\": [\r\n    {\r\n      \"inItemId\": 101,\r\n      \"inItemName\": \"Test String 1\",\r\n      \"inItemDate\": \"2016-05-01T00:00:00Z\"\r\n    },\r\n    {\r\n      \"inItemId\": 102,\r\n      \"inItemName\": \"Test String 2\",\r\n      \"inItemDate\": \"2016-05-02T00:00:00Z\"\r\n    },\r\n    {\r\n      \"inItemId\": 103,\r\n      \"inItemName\": \"Test String 3\",\r\n      \"inItemDate\": \"2016-05-03T00:00:00Z\"\r\n    }\r\n  ]\r\n}\r\n\u003c/pre\u003e\u003c/td\u003e\r\n    \u003ctd align=\"center\"\u003eTable-Valued Parameter\u003cbr /\u003e(SQL Server 2008+)\u003c/td\u003e\r\n    \u003ctd\u003e\u003cu\u003eSQL Server 2008 or later\u003c/u\u003e:\r\n\u003cpre\u003e\r\nCREATE TYPE dbo.ItemTableType AS TABLE\r\n(\r\n    ItemId      int,\r\n    ItemName    nvarchar(256),\r\n    ItemDate    datetime\r\n);\r\n\r\nCREATE PROCEDURE dbo.my_pck_Bulk_Ins_Upd\r\n(\r\n    @inPartitionId  int,\r\n    @inTvpItems     dbo.ItemTableType READONLY\r\n)   AS ...\r\n\u003c/pre\u003e\u003c/td\u003e\r\n  \u003c/tr\u003e\r\n\u003c/table\u003e\r\n\r\n\u0026nbsp;\r\n\r\n##### Accept Response MediaType:  \r\n1. \u003ca name=\"accept-json\"\u003e\u003c/a\u003eJSON _(default)_  \r\n    Specify in request header:  \r\n    Accept: application/json  \r\n    or  \r\n    Accept: text/json  \r\n    or specify in query string: ?format=json  \r\n       _(e.g. http://BaseUrl/YourDatabase.dbo.prj_GetRule?format=json)_  \r\n    or specify in UriPathExtension which depends on your url routing  \r\n       _(e.g. http://BaseUrl/YourDatabase.dbo.prj_GetRule/json)_  \r\n\r\n2. \u003ca name=\"accept-bson\"\u003e\u003c/a\u003eBSON _(only available to targetFramework=\"4.5\" or higher - ASP.NET Web API 2)_  \r\n    Specify in request header:  \r\n    Accept: application/bson  \r\n    or specify in query string: ?format=bson  \r\n       _(e.g. http://BaseUrl/YourDatabase.dbo.prj_GetRule?format=bson)_  \r\n    or specify in UriPathExtension which depends on your url routing  \r\n       _(e.g. http://BaseUrl/YourDatabase.dbo.prj_GetRule/bson)_  \r\n\r\n3. \u003ca name=\"accept-jsonp\"\u003e\u003c/a\u003eJSONP  \r\n    QueryString must contain **callback** parameter _(the name can be configured)_  \r\n    and (  \r\n    Specify in request header:  \r\n    Accept: text/javascript  \r\n    or  \r\n    Accept: application/javascript  \r\n    or  \r\n    Accept: application/json-p  \r\n    or specify in query string: ?format=jsonp  \r\n       _(e.g. http://BaseUrl/YourDatabase.dbo.prj_GetRule?format=jsonp)_  \r\n    or specify in UriPathExtension which depends on your url routing  \r\n       _(e.g. http://BaseUrl/YourDatabase.dbo.prj_GetRule/jsonp)_  \r\n    )  \r\n\r\n4. \u003ca name=\"accept-xml\"\u003e\u003c/a\u003eXML  \r\n    Specify in request header:  \r\n    Accept: application/xml  \r\n    or  \r\n    Accept: text/xml  \r\n    or specify in query string: ?format=xml  \r\n       _(e.g. http://BaseUrl/YourDatabase.dbo.prj_GetRule?format=xml)_  \r\n    or specify in UriPathExtension which depends on your url routing  \r\n       _(e.g. http://BaseUrl/YourDatabase.dbo.prj_GetRule/xml)_  \r\n\r\n5. \u003ca name=\"accept-xlsx\"\u003e\u003c/a\u003exlsx _(Excel 2007 and later)_  \r\n    Specify in request header:  \r\n    Accept: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet  \r\n    or  \r\n    Accept: application/ms-excel  \r\n    or  \r\n    Accept: application/xlsx  \r\n    or specify in query string: ?format=xlsx  \r\n       _(e.g. http://BaseUrl/YourDatabase.dbo.prj_GetRule?format=xlsx)_  \r\n    or specify in UriPathExtension which depends on your url routing  \r\n       _(e.g. http://BaseUrl/YourDatabase.dbo.prj_GetRule/xlsx)_  \r\n    Notes: Since xlsx content presents as an attachment, so you can specify a filename for convenience by query string: FileName=\\[save_as\\] (default: \\[save_as\\].xlsx).  \r\n\r\n6. \u003ca name=\"accept-csv\"\u003e\u003c/a\u003eCSV  \r\n    Specify in request header:  \r\n    Accept: text/csv  \r\n    or specify in query string: ?format=csv  \r\n       _(e.g. http://BaseUrl/YourDatabase.dbo.prj_GetRule?format=csv)_  \r\n    or specify in UriPathExtension which depends on your url routing  \r\n       _(e.g. http://BaseUrl/YourDatabase.dbo.prj_GetRule/csv)_  \r\n    Notes: CSV response will only return the first (or one specified zero indexed result set in query string: ResultSet=i) result set if your stored procedure has multiple result sets. Since CSV content presents as an attachment, so you can specify a filename for convenience by query string: FileName=\\[save_as\\] (default: \\[save_as\\].csv).  \r\n\r\n7. \u003ca name=\"accept-razor-templating\"\u003e\u003c/a\u003eRazor Templating    \r\n    Specify in request header:  \r\n    Accept: text/razor  \r\n    or  \r\n    Accept: application/razor  \r\n    or specify in query string: ?format=razor  \r\n       _(e.g. http://BaseUrl/YourDatabase.dbo.prj_GetRule?format=razor)_  \r\n    or specify in UriPathExtension which depends on your url routing  \r\n       _(e.g. http://BaseUrl/YourDatabase.dbo.prj_GetRule/razor)_  \r\n    Notes: To send a Razor request, the template text must be provided in a conventionalized parameter {RazorTemplate=} in either json body of post request or query string of get request, if the template text is a output parameter name of the stored procedure, the string content of that output parameter will be used as the actual template text. Two optional parameters: {RazorEncoding=Raw|Html} _(default is Raw)_ and {RazorLanguage=CSharp|VisualBasic} _(default is CSharp)_.   \r\n    Model's Data: Inside Razor template, the **@Model** directive represents your strored procedure's result data. _(E.g.  @Model.OutputParameters.outSomeThing - is the value of output parameter outSomeThing, @Model.ResultSets[0][0].SomeProperty - is the value of Some_Property column of the first row of the first resultset)_  \r\n \r\n8. \u003ca name=\"accept-other-mediatypes\"\u003e\u003c/a\u003eOther MediaTypes  \r\n    To support new MediaType, you can:\r\n\r\n* \u003ca name=\"other-mediatype-low-lev\"\u003e\u003c/a\u003eControl in low level (DbDataReader and writeStream), you can create a new class that implements the interface **IFormatPlug**, and register it in your HttpConfiguration. Just like following CSV and xlsx did:\r\n``` CSharp\r\n    public static void RegisterDbWebApi(this HttpConfiguration config)\r\n    {\r\n        config.AddFormatPlug(new CsvFormatPlug());\r\n        config.AddFormatPlug(new XlsxFormatPlug());\r\n    }\r\n```\r\n* \u003ca name=\"other-mediatype-high-lev\"\u003e\u003c/a\u003eControl in high level (start point: StoredProcedureResponse), you can create a new XyzMediaTypeFormatter class as [classical tutorial](http://www.asp.net/web-api/overview/formats-and-model-binding/media-formatters) shows.\r\n  \r\n### HTTP Response\r\n#### Response data internal structure\r\n``` CSharp\r\n    public class StoredProcedureResponse\r\n    {\r\n        public IList\u003cIList\u003cBindableDynamicObject\u003e\u003e ResultSets { get; set; }\r\n        public BindableDynamicObject OutputParameters { get; set; }\r\n        public object ReturnValue { get; set; }\r\n    }\r\n```\r\n\r\n#### Response body formats  \r\n##### application/json, text/json  \r\n    Sample:  \r\n``` JSON\r\n{\r\n  \"ResultSets\":\r\n  [\r\n    [\r\n      {\"COL_1\":\"2015-02-03T00:00:00\",\"COL_2\":3.14159,\"COL_3\":\"Hello World1\",\"COL_4\":null, \"COL_5\":0},\r\n      {\"COL_1\":\"2015-02-02T00:00:00\",\"COL_2\":3.14159,\"COL_3\":null,\"COL_4\":1234567.800099, \"COL_5\":1},\r\n      {\"COL_1\":\"2015-02-01T00:00:00\",\"COL_2\":3.14159,\"COL_3\":\"Hello World3\",\"COL_4\":null, \"COL_5\":2},\r\n      {\"COL_1\":\"2015-01-31T00:00:00\",\"COL_2\":3.14159,\"COL_3\":null,\"COL_4\":9876541.230091, \"COL_5\":3}\r\n    ],\r\n    [\r\n      {\"COL_A\":100,\"COL_B\":\"fooA\",\"COL_C\":0},\r\n      {\"COL_A\":200,\"COL_B\":\"fooB\",\"COL_C\":null},\r\n      {\"COL_A\":300,\"COL_B\":\"fooC\",\"COL_C\":1}\r\n    ],\r\n    [\r\n       {\"NOTE\":\"Test1 for the third result set\"},\r\n       {\"NOTE\":\"Test2 for the third result set\"}\r\n    ]\r\n  ],\r\n  \"OutputParameters\":\r\n  {\r\n    \"outRuleDesc\":\"This is a test output parameter value.\",\r\n    \"outSumTotal\":888888.88,\r\n    \"outRC1\":null\r\n  },\r\n  \"ReturnValue\":0\r\n}\r\n```\r\nFor response to bulk execute request, each of such JSON object will be further encapsulated into an outer array.\r\n\r\n##### application/xml, text/xml  \r\n    Sample:\r\n``` XML\r\n\u003cStoredProcedureResponse xmlns:x=\"http://www.w3.org/2001/XMLSchema\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\" SerializePropertyAsAttribute=\"false\" EmitNullValue=\"true\" TypeSchema=\"Xsd\"\u003e\r\n  \u003cResultSets\u003e\r\n    \u003cResultSet\u003e\r\n      \u003cRecord\u003e\r\n        \u003cCOL_1 i:type=\"x:dateTime\"\u003e2015-02-03T00:00:00\u003c/COL_1\u003e\r\n        \u003cCOL_2 i:type=\"x:decimal\"\u003e3.14159\u003c/COL_2\u003e\r\n        \u003cCOL_3 i:type=\"x:string\"\u003eHello World1\u003c/COL_3\u003e\r\n        \u003cCOL_4 i:nil=\"true\"/\u003e\r\n        \u003cCOL_5 i:type=\"x:int\"\u003e0\u003c/COL_5\u003e\r\n      \u003c/Record\u003e\r\n      \u003cRecord\u003e\r\n        \u003cCOL_1 i:type=\"x:dateTime\"\u003e2015-02-02T00:00:00\u003c/COL_1\u003e\r\n        \u003cCOL_2 i:type=\"x:decimal\"\u003e3.14159\u003c/COL_2\u003e\r\n        \u003cCOL_3 i:nil=\"true\"/\u003e\r\n        \u003cCOL_4 i:type=\"x:decimal\"\u003e1234567.800099\u003c/COL_4\u003e\r\n        \u003cCOL_5 i:type=\"x:int\"\u003e1\u003c/COL_5\u003e\r\n      \u003c/Record\u003e\r\n      \u003cRecord\u003e\r\n        \u003cCOL_1 i:type=\"x:dateTime\"\u003e2015-02-01T00:00:00\u003c/COL_1\u003e\r\n        \u003cCOL_2 i:type=\"x:decimal\"\u003e3.14159\u003c/COL_2\u003e\r\n        \u003cCOL_3 i:type=\"x:string\"\u003eHello World3\u003c/COL_3\u003e\r\n        \u003cCOL_4 i:nil=\"true\"/\u003e\r\n        \u003cCOL_5 i:type=\"x:int\"\u003e2\u003c/COL_5\u003e\r\n      \u003c/Record\u003e\r\n      \u003cRecord\u003e\r\n        \u003cCOL_1 i:type=\"x:dateTime\"\u003e2015-01-31T00:00:00\u003c/COL_1\u003e\r\n        \u003cCOL_2 i:type=\"x:decimal\"\u003e3.14159\u003c/COL_2\u003e\r\n        \u003cCOL_3 i:nil=\"true\"/\u003e\r\n        \u003cCOL_4 i:type=\"x:decimal\"\u003e9876541.230091\u003c/COL_4\u003e\r\n        \u003cCOL_5 i:type=\"x:int\"\u003e3\u003c/COL_5\u003e\r\n      \u003c/Record\u003e\r\n    \u003c/ResultSet\u003e\r\n    \u003cResultSet\u003e\r\n      \u003cRecord\u003e\r\n        \u003cCOL_A i:type=\"x:int\"\u003e100\u003c/COL_A\u003e\r\n        \u003cCOL_B i:type=\"x:string\"\u003efooA\u003c/COL_B\u003e\r\n        \u003cCOL_C i:type=\"x:int\"\u003e0\u003c/COL_C\u003e\r\n      \u003c/Record\u003e\r\n      \u003cRecord\u003e\r\n        \u003cCOL_A i:type=\"x:int\"\u003e200\u003c/COL_A\u003e\r\n        \u003cCOL_B i:type=\"x:string\"\u003efooB\u003c/COL_B\u003e\r\n        \u003cCOL_C i:nil=\"true\"/\u003e\r\n      \u003c/Record\u003e\r\n      \u003cRecord\u003e\r\n        \u003cCOL_A i:type=\"x:int\"\u003e300\u003c/COL_A\u003e\r\n        \u003cCOL_B i:type=\"x:string\"\u003efooC\u003c/COL_B\u003e\r\n        \u003cCOL_C i:type=\"x:int\"\u003e1\u003c/COL_C\u003e\r\n      \u003c/Record\u003e\r\n    \u003c/ResultSet\u003e\r\n    \u003cResultSet\u003e\r\n      \u003cRecord\u003e\r\n        \u003cNOTE i:type=\"x:string\"\u003eTest1 for the third result set\u003c/NOTE\u003e\r\n      \u003c/Record\u003e\r\n      \u003cRecord\u003e\r\n        \u003cNOTE i:type=\"x:string\"\u003eTest2 for the third result set\u003c/NOTE\u003e\r\n      \u003c/Record\u003e\r\n    \u003c/ResultSet\u003e\r\n  \u003c/ResultSets\u003e\r\n  \u003cOutputParameters\u003e\r\n    \u003coutRuleDesc i:type=\"x:string\"\u003eThis is a test output parameter value.\u003c/outRuleDesc\u003e\r\n    \u003coutSumTotal i:type=\"x:decimal\"\u003e888888.88\u003c/outSumTotal\u003e\r\n    \u003coutRC1 i:nil=\"true\" /\u003e\r\n  \u003c/OutputParameters\u003e\r\n  \u003cReturnValue i:nil=\"true\" /\u003e\r\n\u003c/StoredProcedureResponse\u003e\r\n```\r\nFor response to bulk execute request, each of such XML object will be further encapsulated into an outer array.\r\n\r\nThere are a few options can be applied in Url **query string** to control the XML style:\r\n* XmlNullValue\r\n    - **true**: **(default)** Emit all null _(DBNull)_ value properties _(columns)_ into XML.\r\n    - false: Do not emit any null _(DBNull)_ value properties _(columns)_ into XML.\r\n\r\n* XmlAsAttribute\r\n    - **false**: **(default)** Serialize properties _(columns)_ as XML elements.\r\n    - true: Serialize properties _(columns)_ as XML attributes, null _(DBNull)_ value will be rendered as empty string if *XmlNullValue=true*.  \r\nFor above example stored procedure with *XmlAsAttribute=true*, the response becomes:\r\n``` XML\r\n\u003cStoredProcedureResponse xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\" SerializePropertyAsAttribute=\"true\" EmitNullValue=\"true\" TypeSchema=\"None\"\u003e\r\n  \u003cResultSets\u003e\r\n    \u003cResultSet\u003e\r\n      \u003cRecord COL_1=\"2015-02-03T00:00:00\" COL_2=\"3.14159\" COL_3=\"Hello World1\" COL_4=\"\" COL_5=\"0\" /\u003e\r\n      \u003cRecord COL_1=\"2015-02-02T00:00:00\" COL_2=\"3.14159\" COL_3=\"\" COL_4=\"1234567.800099\" COL_5=\"1\" /\u003e\r\n      \u003cRecord COL_1=\"2015-02-01T00:00:00\" COL_2=\"3.14159\" COL_3=\"Hello World3\" COL_4=\"\" COL_5=\"2\" /\u003e\r\n      \u003cRecord COL_1=\"2015-01-31T00:00:00\" COL_2=\"3.14159\" COL_3=\"\" COL_4=\"9876541.230091\" COL_5=\"3\" /\u003e\r\n    \u003c/ResultSet\u003e\r\n    \u003cResultSet\u003e\r\n      \u003cRecord COL_A=\"100\" COL_B=\"fooA\" COL_C=\"0\" /\u003e\r\n      \u003cRecord COL_A=\"200\" COL_B=\"fooB\" COL_C=\"\" /\u003e\r\n      \u003cRecord COL_A=\"300\" COL_B=\"fooC\" COL_C=\"1\" /\u003e\r\n    \u003c/ResultSet\u003e\r\n    \u003cResultSet\u003e\r\n      \u003cRecord NOTE=\"Test1 for the third result set\" /\u003e\r\n      \u003cRecord NOTE=\"Test2 for the third result set\" /\u003e\r\n    \u003c/ResultSet\u003e\r\n  \u003c/ResultSets\u003e\r\n  \u003cOutputParameters outRuleDesc=\"This is a test output parameter value.\" outSumTotal=\"888888.88\" outRC1=\"\" /\u003e\r\n  \u003cReturnValue i:nil=\"true\" /\u003e\r\n\u003c/StoredProcedureResponse\u003e\r\n```\r\n* XmlTypeSchema *(only available when XmlAsAttribute=false)*\r\n    - **Xsd**: **(default)** Emit XSD data type information *(E.g. i:type=\"x:dateTime\" ...)* for each property(column)'s XML element.\r\n    - Net: Emit .Net data type information *(E.g. z:Type=\"System.DateTime\" ...)* for each property(column)'s XML element.\r\n    - None: Do not emit any data type information in XML.\r\n\r\nAbove options only provide some simple controls on XML styles.  \r\nFor other XML format controls, you may still need to further apply [XDT](http://xdt.codeplex.com/) or raw XSLT transformations ... even hard coding in client side.\r\n\r\n\u0026nbsp;\r\n\r\n##### text/csv  \r\n    Sample:\r\n``` CSV\r\nCOL_1,COL_2,COL_3,COL_4,COL_5\r\n2015-02-03,3.14159,Hello World1,,0\r\n2015-02-02,3.14159,,1234567.800099,1\r\n2015-02-01,3.14159,Hello World3,,2\r\n2015-01-31,3.14159,,9876541.230091,3\r\n```\r\n\r\nNotes:  \r\n\u003e JSON, XML and xlsx respones are constructed completely in Web API server before sending to the client, so you might  encounter OutOfMemoryException if the client wants to receive huge amounts of data. However, JSON can be sufficient in most application scenarios with its simplicity. And after all, process data as close to where the data physically resides as possible, this is a basic principle of big data processing. (i.e. Simplifying the complexity as early as possible.)  \r\n\r\n\u003eFor most of Web applications, the final data are for human eyes to read.\r\n\r\n\u003eFor some systems integration, CSV format is also widely used for data filling. It's mostly waste of human resources to design such SSIS packages one by one, and to maintain such encumbrances for ever. It's time for machine to do such mechanical process, let DbWebApi serve as the machine. No more mechanical designs, no more packages, no more configurations, no more deployments and no more maintenances. Let artificial complexities, dust to dust, nothing to nothing!\r\n\r\n\u003eCSV respone emerges as text stream pushing to the client, it just use very little memory in Web API server to push a few text lines as long as their CSV rows have been constructed, so on and so forth, until all complete. So the server's memory is not a limitation of how many records can be handled.\r\n\r\n####  Property Naming Convention\r\nDatabase side may use a different naming convention other than .NET side or JavaScript side. For example, most Oracle works use underscores naming convention, like above output examples, from a .NET or JavaScript point of view, they could look really ugly. So DbWebApi provides 2 + _None_ built-in naming convention resolvers:\r\n- PropertyNamingConvention.None\r\n- PropertyNamingConvention.PascalCase\r\n- PropertyNamingConvention.CamelCase\r\n\r\nYou can set the DefaultPropertyNamingConvention globally in your WebApiConfig:\r\n``` CSharp\r\npublic static class WebApiConfig\r\n{\r\n    public static void Register(HttpConfiguration config)\r\n    {\r\n        ...\r\n        DbWebApiOptions.DefaultPropertyNamingConvention =\r\n            PropertyNamingConvention.PascalCase;\r\n    }\r\n}\r\n```\r\nYou can also specify the output Property Naming Convention in Uri Query String of each individual request:\r\n- NamingCase=N (or None) ---------- As it is in database \r\n- NamingCase=P (or Pascal) -------- PascalCase\r\n- NamingCase=C (or Camel) -------- CamelCase\r\n\r\nIf you don't specify the NamingCase in later request, the global set before will back into effect.  \r\nNotes: The automatic naming conversion only applies to result sets' column-names. Input and output parameters remain the same as they are in database.  \r\n\r\n\r\n### Exceptions\r\nFor JSON, XML and xlsx responses, detail exception will be encapsulated into HttpResponseMessage with HTTP 500 error status if the Web API service encounters any problems. For the verbosity of errors to show in client side, it depends on your IncludeErrorDetailPolicy in HttpConfiguration. However, because CSV respone uses a push stream, the client side will always receive a HTTP 200 OK header without Content-Length field. If the server side encounter any exception subsequently, it would simply interrupt the http connection and the client would get a Receive Failure without any detail exception message.\r\n\r\n### Permission Control\r\nAccess authorization is the only one thing you have to handle by yourself, and the approach depends on the granularity of control you want.  \r\nControl over the stored-procedure granularity is a simple and effective practice. The [example project](https://github.com/DataBooster/DbWebApi/blob/master/Server/Sample/MyDbWebApi/Controllers/DbWebApiController.cs) shows using an authorization filter [[DbWebApiAuthorize](https://github.com/DataBooster/DbWebApi/blob/master/Server/Sample/MyDbWebApi/Filters/MyDbWebApiAuthorization.cs)] to restrict which user can execute which stored procedure, that should integrate with your own implementation of authorization checking.\r\n``` CSharp\r\n    public class MyDbWebApiAuthorization : IDbWebApiAuthorization\r\n    {\r\n        public bool IsAuthorized(string userName, string storedProcedure, object state = null)\r\n        {\r\n            // TO DO, to integrate with your own authorization implementation\r\n            return true;\t// If allow permission\r\n            return false;\t// If deny permission\r\n        }\r\n    }\r\n```\r\n\r\n### UserName\r\nRecording current username is a common auditing requirement. Since the Web API never trust any self-identify username sent from client request data. So if a stored procedure requires the username as a parameter, the Web API should always replace that parameter sent from the client (or add that parameter if a client didn't send it) by the server side authentication. Any practical way as long as you think it's simple enough can be apply in your Web API implementation. For examples,\r\n* Make a naming convention for this special parameter in database within your enterprise, then the Web API always set (replace/add) this special parameter before pass the whole input parameters dictionary to ExecuteDbApi extension method. It won't hurt anything, because in its low level DataBooster will match stored procedure parameter names with the input parameters dictionary that you pass in, and discard non-matched parameters.\r\n* Or in a traditional way, create separate Controllers for those stored procedures individually, in their internal implementation set current username and then call the ExecuteDbApi extension method.\r\n* Or in a centralized table, register that which stored procedures which parameter require current UserName input, so that in the Web API can know when it need to replace/add which input parameter.\r\n* etc.\r\n\r\n### Performance  \r\n* Connection Pool Tuning  \r\nFacing with concurrent requests from different clients in different business contexts. DbWebApi server opens a new database connection per request. All requests are using the same connection string. So the Connection Pool Tuning is important to the performance of the whole responsiveness.\r\n\r\n* Load Balancing  \r\nAs a completely generic web service, DbWebApi makes the distributed deployment much simpler, every nodes in the distributed environment are equivalent. It is easier to apply any of today's existing web server load balance techniques.\r\n\r\n* For front-end applications and systems integration  \r\nAs a Web API, the target clients are still front-end applications mainly, plus some data formats transform for systems integration convenience.  \r\nThe performance overhead of each extra wrapper of network service _(wrap one web service on top of another web service, and another one ... fussily)_ is always very expensive. For efficient custom data services development, it is recommended to use [DataBooster Library - Extension to ADO.NET Data Provider](https://github.com/DataBooster/DataAccess) directly for high-performance database access.  \r\n  \r\n#### Bulk Manipulation  \r\n* \u003ca name=\"quasi-bulk\"\u003e\u003c/a\u003eThe BulkExecuteDbApi extension (BulkExecute action) is not a completely thorough bulk operation. It does performs the real bulk operation only between HTTP client and Web API server, it still performs a big loop calls to database from the Web API server. But it provides a convenient wrapper around every single call, and it is independent on specific database (Oracle or SQL Server).\r\n* \u003ca name=\"thorough-bulk\"\u003e\u003c/a\u003eIf there are thousands of data rows or more data sets need to be passed back to database, it's well worth considering using Table-Valued Parameters (specific for SQL Server 2008+) or PL/SQL Associative Array Parameters (specific for Oracle database) in a single ExecuteDbApi (Execute action) call as mentioned before, they are completely thorough bulk operations.\r\n\r\n## Clients\r\n#### Swagger UI  \r\n[Swagger UI](http://swagger.io/swagger-ui/) is a handy WebAPI documentation tool. Some examples of DbWebApi Swagger specifcation (swagger.json file) are provided in [DbWebApi/Client/Swagger-UI/](https://github.com/DataBooster/DbWebApi/tree/master/Client/Swagger-UI).  \r\n_Notes: the templating symbols `/*( ... )*/` are using in the example files to mark a section as replaceable._  \r\n[Swagger Editor](http://editor.swagger.io/#/) can be used to verify your customized Swagger specifcation file (_swagger.json_).  \r\n_If your have complex data model, some JSON schema-generating tool (such as [JSONSchema.Net](http://jsonschema.net/#/)) can be used as an aid._\r\n\r\nThen, your Swagger UI URL may look like `http://your_host/swagger-ui/index.html?url=specification_location/swagger.json` or `http://your_host/swagger-ui/?url=specification_location/swagger.json`.\r\n\r\n_If your swagger.json files are placed in some older IIS, you might need to add `.json` file extension in IIS MIME Types or add a mimeMap in the web.config as below._\r\n``` XML\r\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\r\n\u003cconfiguration\u003e\r\n  \u003csystem.webServer\u003e\r\n    \u003cstaticContent\u003e\r\n      \u003cremove fileExtension=\".json\" /\u003e\r\n      \u003cmimeMap fileExtension=\".json\" mimeType=\"application/json\" /\u003e\r\n    \u003c/staticContent\u003e\r\n    \u003cdefaultDocument\u003e\r\n      \u003cfiles\u003e\r\n        \u003cclear /\u003e\r\n        \u003cadd value=\"index.html\" /\u003e\r\n      \u003c/files\u003e\r\n    \u003c/defaultDocument\u003e\r\n  \u003c/system.webServer\u003e\r\n\u003c/configuration\u003e\r\n```\r\n\r\n#### .Net Client  \r\n[DbWebApi Client .Net Library](https://www.nuget.org/packages/DataBooster.DbWebApi.Client.Net) can be used to simplify the client call. See following sample:\r\n``` CSharp\r\nusing DataBooster.DbWebApi.Client;\r\n```\r\n``` CSharp\r\nDbWebApiClient client = new DbWebApiClient(\"http://dbwebapi.dev.com/oradev/\");\r\n//  client.HttpMethod = HttpMethod.Get;    // Default is POST\r\n\r\n// Synchronous call. If need asynchronous call, please use ExecAsync(..) instead.\r\nStoredProcedureResponse data = client.Exec(\"test_schema.prj_package.foo\",\r\n    new {\r\n        inDate = new DateTime(2015, 3, 16)\r\n        //, ... other input parameters, if any.\r\n    });\r\n```\r\nThe second argument of Exec(...) can be a dictionary that contains every parameters, or an anonymous type object that each property indicate a parameter name-value.  \r\nIf an array of input parameter sets is passed into the second argument of Exec(...), the return will be an array -- StoredProcedureResponse[].\r\n\r\n_If you expect a stored procedure would be time-consuming, please set the [HttpClient.Timeout](https://msdn.microsoft.com/en-us/library/system.net.http.httpclient.timeout(v=vs.110).aspx) to a sufficiently long time, such as:_\r\n``` CSharp\r\nclient.HttpClient.Timeout = TimeSpan.FromMinutes(10);\r\n```\r\n_All Exec... overloads will use HTTP POST method by default. You can change the default behavior to HTTP GET if need:_\r\n``` CSharp\r\nclient.HttpMethod = HttpMethod.Get;\r\n```\r\n\r\nIf you just need the response content stream (E.g. CSV, Excel xlsx or generated text) to be stored as a file or transfer forward to somewhere else on the network, see below example, replacing Exec() by ExecAsStream().\r\n``` CSharp\r\n...\r\nStream stream = client.ExecAsStream(\"test_schema.prj_package.foo\",\r\n    new {\r\n        inDate = new DateTime(2015, 3, 16)\r\n        //, ... other input parameters, if any.\r\n    });\r\nusing (FileStream file = File.Create(...))\r\n{\r\n    stream.CopyTo(file);\r\n}\r\n```\r\nFor more general purpose, ExecAsStream _(or ExecAsStreamAsync)_, ExecAsJson _(or ExecAsJsonAsync)_, ExecAsXml _(or ExecAsXmlAsync)_ and ExecAsString _(or ExecAsStringAsync)_ overloads can be used to invoke any REST API, not limited to DbWebApi.\r\n\r\nBy default, the DbWebApiClient uses Windows authentication for the convenience of intranet usage scenarios. Please see its constructor overrides for other options.\r\n\r\n#### JavaScript Client  \r\nYou can use jQuery.ajax easily to call the Web API, or you can use [DbWebApi Client JavaScript Library](https://www.nuget.org/packages/DataBooster.DbWebApi.Client.JS) to reduce repetitive coding.  \r\n    Sample:\r\n``` JavaScript\r\n\u003cscript src=\"Scripts/jquery-2.1.3.js\" type=\"text/javascript\"\u003e\u003c/script\u003e\r\n\u003cscript src=\"Scripts/dbwebapi_client-1.0.8-alpha.js\" type=\"text/javascript\"\u003e\u003c/script\u003e\r\n```\r\n``` JavaScript\r\n\u003cscript type=\"text/javascript\"\u003e\r\n    ...\r\n    $.postDb('http://dbwebapi.dev.com/oradev/test_schema.prj_package.foo',\r\n             '{\"inDate\":\"2015-03-10T00:00:00.000Z\"}',\r\n             function (data) {\r\n                 // Bind data.ResultSets[0] with some contorls,\r\n                 // or iterate through each JSON object in data.\r\n             });\r\n    ...\r\n\u003c/script\u003e\r\n```\r\nThe second argument of $.postDb - _inputJson_ can be either a JSON string or a plain object. If it's a plain object, it will be converted by JSON.stringify before sending to the server. Below sample is equivalent to above sample.\r\n``` JavaScript\r\n    ...\r\n    var input = {\r\n        inDate: $.utcDate(2015,03,10)\r\n    };\r\n    $.postDb('http://dbwebapi.dev.com/oradev/test_schema.prj_package.foo',\r\n             input,\r\n             function (data) {\r\n                 ...\r\n             });\r\n    ...\r\n```\r\nIf there is no input parameter to pass to the server, please put _**null**_ in the second argument.  \r\nIf an array of input parameter sets is passed into the second argument, the return data will be an array that contains the corresponding results of every iterative executions.  \r\nBy default, the $.postDb sets the withCredentials property of the internal xhrFields object to true so it will pass the user credentials with cross-domain requests.  \r\nAs the name implies, $.postDb uses HTTP POST to send a request;  \r\nAlternatively, $.getDb can be used for HTTP GET if need be. All input parameters are encapsulated into a special query string, and appended to the url for GET-requests.\r\n\r\n\r\n##### Cross-domain  \r\n###### CORS  \r\nThe sample server projects _(.Net4.5/WebApi2 versions)_ in this repository have built-in support for [CORS _(Cross-Origin Resource Sharing)_](http://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api). You can change the \"CorsOrigins\" item of appSettings in the Web.config if you want to specify particular Origins.\r\n``` XML\r\n\u003cconfiguration\u003e\r\n  \u003cappSettings\u003e\r\n    \u003cadd key=\"CorsOrigins\" value=\"*\" /\u003e\r\n    \u003cadd key=\"CorsSupportsCredentials\" value=\"true\" /\u003e\r\n    \u003cadd key=\"CorsPreflightMaxAge\" value=\"3600\" /\u003e\r\n  \u003c/appSettings\u003e\r\n\u003c/configuration\u003e\r\n```\r\n_\u003cu\u003ePreflight Request 401 Issue with Windows Authentication\u003c/u\u003e_\r\n\r\nToday (until 2015) a very common usage scenario:\r\n- Only Integrated Windows Authentication is enabled on IIS _(without anonymous authentication)_, and\r\n- The request body is JSON or XML media types - _the Content-Type header is application/json, text/json, application/xml or text/xml._\r\n\r\nUsually the CORS preflight will fail by a 401 unauthorized error _(Access is denied due to invalid credentials)_ in this scenario. The root of this problem came from an awkward preflight rule in W3C specifications.\r\n- Since above case doesn't meet the exemption conditions for browser to skip the preflight request, so browser will start the additional preflight request;\r\n- Browser would never include user credentials in preflight request; _(Even though both server side's **SupportsCredentials** and browser side's **withCredentials** have been set to be true, they only apply to subsequent actual cross-origin requests instead of the preflight request.)_\r\n- IIS would return a 401 to any anonymous request, within the 401 response would have some Windows Authentication headers that expect the client to start an authentication handshake. However the browser still stubbornly believe that preflight request shouldn't include any user credential, so just gives up;\r\n- If the preflight request fails, the browser would never send the actual cross-origin request at all.\r\n\r\n\u003cu\u003eThere are several ways to get around this uncomfortable issue\u003c/u\u003e:\r\n- Enable anonymous authentication at the IIS level, and disable anonymous authentication at the `web.config` level and the web API authorization filter level.\r\n- Use HTTP GET method to avoid sending application/json (or xml) Content-Type header - _Browser never send any body in GET request._  \r\n$.getDb(...) can encode input JSON object into a special parameter in query string.  \r\n_But there is a limitation on length of the URL, large data still requires the use of POST method, see the next ways then:_\r\n- Attach cross-origin POST request in any one very lightweight GET request's callback function, as in the following example:\r\n``` JavaScript\r\n    ...\r\n    var input = {\r\n        inDate: $.utcDate(2015,03,10)\r\n    };\r\n    $.getDb('http://dbwebapi.dev.com/oradev/api/Misc/WhoAmI', null, function (me) {\r\n        $.postDb('http://dbwebapi.dev.com/oradev/test_schema.prj_package.foo',\r\n             input,\r\n             function (data) {\r\n                 ...\r\n             });\r\n    });\r\n    ...\r\n```\r\nHere $.getDb('.../WhoAmI') acts as a bootstrapper, it makes the browser to start an authentication handshake in advance, once IIS authenticates the request, [the default behavior of IIS](https://msdn.microsoft.com/en-us/library/aa347548.aspx) will cache a token/ticket on the server for the connection, then the immediate preflight request on the same connection is not required to be authenticated again, so the preflight request will succeed, then the browser can continue the actual CORS request.\r\n- HTML Form request _(Content-Type: application/x-www-form-urlencoded)_ and Multipart MIME request _(Content-Type: multipart/form-data)_ can also be used to send a fair amount of data to DbWebApi without preflight request.  \r\n_The limitation of these two media types' requests is that, they can only carry input parameters for a single execution of DbWebApi on each call. If you need a bulk execution of DbWebApi on one call, only the former way (all sets of input parameters are further encapsulated in an outer JSON or XML array) can satisfy the use._\r\n\r\n\u0026nbsp;\r\n\r\n###### JSONP (Added: Server Lib v1.2.4, Client JS v1.0.8-alpha) \r\nJSONP is a practicable way _(although it seems a little rascal)_ to solve the cross-domain access puzzle before CORS is supported by all popular browsers.  \r\nBelow example is a JSONP approach of above example,\r\n``` JavaScript\r\n    ...\r\n    var input = {\r\n        inDate: $.utcDate(2015,03,10)\r\n    };\r\n    $.jsonpDb('http://dbwebapi.dev.com/oradev/test_schema.prj_package.foo',\r\n             input,\r\n             function (data) {\r\n                 ...\r\n             });\r\n    ...\r\n```\r\nNotes: since JSONP sends request by HTTP GET method, BulkExecute can not be used by JSONP.  \r\n\r\nThe server side [WebApiConfig.cs](https://github.com/DataBooster/DbWebApi/blob/master/Server/Sample/MyDbWebApi/App_Start/WebApiConfig.cs):\r\n``` CSharp\r\n    config.RegisterDbWebApi();\r\n```\r\nwhich will include JSONP support by default. If you don't want to support JSONP, please specify supportJsonp to be false:\r\n``` CSharp\r\n    config.RegisterDbWebApi(supportJsonp: false);\r\n```\r\n\r\n\r\n#### AngularJS Client\r\nUsing the built-in [$http service](https://docs.angularjs.org/api/ng/service/$http) is a straightforward way for AngularJS client to invoke the Web API. For example,\r\n``` JavaScript\r\n    ...\r\n    return $http.post(spUrl, inputData, {withCredentials: true})\r\n                .then(function(response){\r\n                    return response.data.ResultSets; \r\n                });\r\n```\r\n\r\n#### Angular 6+ Client\r\nIf your project is Angular 6.0 or higher, the npm package [**dbwebapi-client**](https://www.npmjs.com/package/dbwebapi-client) can be used to simplify your http client coding.\r\n```\r\n\u003e npm i dbwebapi-client\r\n```\r\napp.module.ts\r\n``` typescript\r\nimport { DbwebapiClientModule } from 'dbwebapi-client';\r\n\r\n@NgModule({\r\n  declarations: [ // ...\r\n  ],\r\n  imports: [ // ...\r\n    DbwebapiClientModule.forRoot()\r\n  ],\r\n  providers: [ // ...\r\n  ],\r\n  bootstrap: [AppComponent]\r\n})\r\nexport class AppModule { }\r\n```\r\nThen your Angular service can inherit from DbWebApiClient class, as shown in the following example:\r\n``` typescript\r\nimport { Injectable } from \"@angular/core\";\r\nimport { HttpClient } from \"@angular/common/http\";\r\nimport { Observable } from \"rxjs\";\r\nimport { map } from 'rxjs/operators';\r\n\r\nimport { DbWebApiClient } from 'dbwebapi-client';\r\n\r\n@Injectable({ providedIn: 'root' })\r\nexport class MyDbWebApiService extends DbWebApiClient {\r\n    constructor(_http: HttpClient) { super(_http, 'http://my-base-url-path.'); }\r\n\r\n    invokeMyStoredProcedure(inParams: object): Observable\u003cMyTypescriptModel\u003e {\r\n        return super.post('my_stored_procedure', inParams).pipe(map(data =\u003e new MyTypescriptModel(data.ResultSets)));\r\n    }\r\n}\r\n```\r\n_The example method will return an Observable MyTypescriptModel instance if the **constructor** of MyTypescriptModel class transforms flat result sets to local hierarchical data model._\r\n\r\nIf you need to control the details of http options (such as: credentials, headers), you can use the property `httpOptions` to set it up.\r\n\r\n#### Python(3+) Client\r\nSpecifically for `JSON-request=\u003eJSON-response` with Windows single sign-on authentication, the PyPi package [**simple-rest-call**](https://pypi.org/project/simple-rest-call/) can be leveraged to simplify your Python client.\r\n\r\n#### PowerShell Client  \r\nIn Windows PowerShell 3.0 or higher, [Invoke-RestMethod](https://technet.microsoft.com/en-us/library/hh849971.aspx) cmdlet is readily available. See following sample:\r\n``` PowerShell\r\n$inpms = @{inDate = [DateTime]\"2015-03-16\"};\r\n$response = Invoke-RestMethod -UseDefaultCredentials -Method Post -Uri \"http://dbwebapi.dev.com/oradev/test_schema.prj_package.foo\" -Body (ConvertTo-Json $inpms) -ContentType \"application/json\"\r\n```\r\n$response contains all the result data. In Powershell ISE, the IntelliSense can show you all its member properties.  \r\nIf an array of input parameter sets is passed into the body content, the return $response will be an array that contains the corresponding results of every iterative executions.  \r\n\r\nIf you want to save the response body stream _(such as CSV or Excel xlsx)_ into a specified output file, please use -OutFile parameter,\r\n``` PowerShell\r\nInvoke-RestMethod -UseDefaultCredentials -Method Post -Uri \"http://dbwebapi.dev.com/oradev/test_schema.prj_package.foo/xlsx\" -Body (ConvertTo-Json $inpms) -ContentType \"application/json\" -OutFile \"\\\\somewhere\\somepath\\filename.xlsx\"\r\n```\r\n\r\n##### Bulk Data Post Back\r\n- \u003ca name=\"hundreds-or-less\"\u003e\u003c/a\u003eIf you only have hundreds of records or less to post back to database, you can just encapsulate all records into an array, let the Web API server side implicitly perform the BulkExecute action. Following example shows how to load data from a local CSV file and post back into database:\r\n``` PowerShell\r\n$impData = Import-Csv -Path \"D:\\Test\\bulk-100s.csv\";\r\nInvoke-RestMethod -UseDefaultCredentials -Method Post -Uri \"http://dbwebapi.test.net/oradev/test_schema.prj_package.write_row\" -Body (ConvertTo-Json $impData) -ContentType \"application/json\";\r\n```\r\nStraightforwardly, any CSV columns that match the names in input parameters will be passed into the stored procedure.  \r\nBy using PowerShell pipeline, if need, you can easily apply some data transformations, to do such as: column-parameter mapping or simple calculating by [select cmdlet](https://technet.microsoft.com/en-us/library/hh849895.aspx), data filtering by [where cmdlet](https://technet.microsoft.com/en-us/library/hh849715.aspx), some simple aggregation by [group cmdlet](https://technet.microsoft.com/en-us/library/hh849907.aspx), and data sorting by [sort cmdlet](https://technet.microsoft.com/en-us/library/hh849912.aspx), ... etc.\r\n- \u003ca name=\"thousands-or-more\"\u003e\u003c/a\u003eIf you have thousands of records or more to post back to database, taking advantage of [Table-Valued Parameters](https://msdn.microsoft.com/en-us/library/bb510489.aspx) (SQL Server 2008+) or [PL/SQL Associative Array Parameters](http://docs.oracle.com/cd/A91202_01/901_doc/appdev.901/a89856/05_colls.htm#23723) (Oracle) will give you significantly better performance.\r\n    - \u003ca name=\"ps-table-valued-parameters\"\u003e\u003c/a\u003e[Table-Valued Parameters](https://msdn.microsoft.com/en-us/library/bb510489.aspx) (SQL Server 2008+)  \r\nBasically just further wrap the array of records into a single parameter, like the second line in following example *(note that dbo.pck_bulk_write stored procedure is different from single row operation)*:\r\n``` PowerShell\r\n$csv = Import-Csv -Path \"D:\\Test\\bulk-100s.csv\";\r\n$inpms = @{ tvpParam = $csv };\r\nInvoke-RestMethod -UseDefaultCredentials -Method Post -Uri \"http://dbwebapi.test.net/sqldev/dbo.pck_bulk_write\" -Body (ConvertTo-Json $inpms) -ContentType \"application/json\";\r\n```\r\n- \r\n    - \u003ca name=\"ps-associative-array-parameters\"\u003e\u003c/a\u003e[PL/SQL Associative Array Parameters](http://docs.oracle.com/cd/E51173_01/win.122/e17732/featOraCommand.htm#ODPNT250) (Oracle)  \r\nOracle uses another style,  each parameter must be separated as an array of primitive data type. See following example,\r\n``` PowerShell\r\n$csv = Import-Csv -Path \"D:\\Test\\bulk-100s.csv\";\r\n$inpms = @{inItemIds=[int[]]@(0) * $csv.Length; inItemNames=[string[]]@(\"\") * $import.Length; inItemValues=[decimal[]]@(0) * $import.Length; inBatchComment=\"This is a test load.\"};\r\n[int]$i = 0;\r\nforeach ($item in $csv) {\r\n    $inpms.inItemIds[$i] = $item.ItemId;\r\n    $inpms.inItemNames[$i] = $item.ItemName;\r\n    $inpms.inItemValues[$i] = $item.ItemValue;\r\n    $i++;\r\n}\r\nInvoke-RestMethod -UseDefaultCredentials -Method Post -Uri \"http://dbwebapi.test.net/oradev/test_schema.tst_package.bulk_write\" -Body (ConvertTo-Json $inpms) -ContentType \"application/json\";\r\n```\r\n*Tips:*  \r\n*Using PowerShell array for large dataset, better to initialize an array with explicit size (instead of dynamic array with subsequent appending elements), otherwise most of performance will be lost in highly frequent memory reallocation, data copying over and over again.*  \r\n*You may notice that [Invoke-RestMethod](https://technet.microsoft.com/en-us/library/hh849971.aspx) takes many fixed arguments, to be lazier to type them all the time, you can import a convenient function [Invoke-DbWebApi](https://github.com/DataBooster/DbWebApi/blob/master/Client/PowerShell/ClientModule/Invoke-DbWebApi.Sample.ps1) from [DbWebApi-Client.psm1](https://github.com/DataBooster/DbWebApi/blob/master/Client/PowerShell/ClientModule/DbWebApi-Client.psm1) to further clean your PowerShell scripts. As a shell, PowerShell is much better at describing what to do, rather than how to do. Each Cmdlet or external service focuses on how to do. So keep PowerShell scripts as clean as possible will benefit the whole process flow in a clear thread.*\r\n\r\nIf you like to automatically match all Associative Array Parameters to the CSV Columns (by names), above example can be refactored into a generic function as follows:\r\n``` PowerShell\r\nFunction LoadCsv-IntoOra {\r\n    [CmdletBinding(SupportsShouldProcess)]\r\n    Param (\r\n        [Parameter(Mandatory, ValueFromPipeline)]\r\n        [string]$CsvPath,\r\n        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]\r\n        [Uri]$SpUri\r\n    )\r\n\r\n    $csv = Import-Csv -Path $CsvPath;\r\n\r\n    If ($csv.Length -gt 0) {\r\n        $plAAParams = [PSCustomObject]@{};\r\n\r\n        Get-Member -InputObject $csv[0] -MemberType Properties | ForEach-Object {\r\n            $inArray = @($null) * $csv.Length;\r\n\r\n            for ([int]$i = 0; $i -lt $csv.Length; $i++) {\r\n                $inArray[$i] = $csv[$i].($_.Name);\r\n            }\r\n            Add-Member -InputObject $plAAParams -MemberType NoteProperty -Name $_.Name -Value $inArray;\r\n        }\r\n        return Invoke-RestMethod -UseDefaultCredentials -Method Post -Uri $SpUri -Body (ConvertTo-Json $plAAParams) -ContentType \"application/json\";\r\n    }\r\n}\r\n```\r\n\r\nPowerShell is true powerful to do more solid work with less coding if being rationally utilized. Especially for back office system-integration applications, heterogeneous techniques across different systems can be leveraged by PowerShell's interoperability with consistent pipeline mechanism. It's also extremely handy to use PowerShell as a test/debug tool. In PowerShell, all data become visualized and extremely flexible to be quickly modified interactively.  \r\n\r\n#### Windows Command Line Client  \r\nOn some occasions which running-performance are not critical, using a generic batch file to call the DbWebApi PowerShell Client in a single command line may still be an efficient development. Refer to [Invoke-DbWebApi.bat](https://github.com/DataBooster/DbWebApi/blob/master/Client/PowerShell/ClientModule/Invoke-DbWebApi.bat).  \r\nFor usage example,\r\n```bat\r\nInvoke-DbWebApi.bat -Uri \"http://dbwebapi.dev.com/oradev/test_schema.prj_package.your_sp/xlsx\" -Body \"{'inId':108,'inDate':'2016-01-20T00:00:00Z'}\" -OutFile \"\\\\NFS\\Shared Folder\\Working Data\\Rpt2015.xlsx\"\r\n```\r\n_\u003cu\u003eRemarks\u003c/u\u003e_  \r\n_The Invoke-DbWebApi.bat requires Windows PowerShell 3.0 or higher._  \r\nTo check what exact parameters will be sent to DbWebApi without really executing the stored procedure, you can append the _**-WhatIf -Verbose**_ switches to above command-line.\r\n\r\nTo prevent a JSON string argument from being split into multiple broken arguments by Command Prompt, double quotes (\") must be used to encapsulate the whole string, and leaving inside single quotes (') to JSON parser. For example:\r\n```\r\n-Body \"{'inId': 108, 'inDate': '2016-01-20T00:00:00Z', 'inComment': 'Some Comment'}\"\r\n```\r\nBecause in Json.Net, single quotes around keys and values work in the same way as double quotes _(although it is not a standard of JSON.org)_. But in Windows Command Prompt, only double-quoted string _(contains spaces)_ can be recognized as a whole argument.\r\n\r\nAlternatively, escaping every inside double quote character (\") needs to be considered. For example:\r\n```\r\n-Body \"{\\\"inId\\\": 108, \\\"inDate\\\": \\\"2016-01-20T00:00:00Z\\\", \\\"inComment\\\": \\\"Some Comment\\\"}\"\r\n```\r\n_Using -WhatIf -Verbose switches is an easy check._\r\n\r\n#### Power Query Client  \r\n[Power BI](https://powerbi.microsoft.com/en-us/guided-learning/) can use [Power Query](https://msdn.microsoft.com/en-us/library/mt260892.aspx) to invoke DbWebApi as simply as below example:\r\n```\r\nlet\r\n    Source = Json.Document(Web.Contents(\"http://dbwebapi.dev.com/oradev/test_schema.prj_package.your_sp/json?inDate=2016-04-22\")),\r\n    ResultSet1 = Table.FromRecords(Source[ResultSets]{0})\r\nin\r\n    ResultSet1\r\n```\r\n_(In practical applications, above regular query would be made into a function with parameters.)_\r\n\r\nIn many cases, some in-situ processes are programmed in stored procedures; Power BI needs to get the resultsets on demand. DbWebApi brings a convenient and secure way for Power Query to access stored procedures.\r\n\r\n_Especially for Oracle stored procedures, Power Query can not handle SYS_REFCURSOR. Without the DbWebApi, we mostly had to schedule the stored procedures to run and dump the resultsets into some physical tables at regular intervals. Then let Power Query get the result data from those tables. This might require assistance from the DBAs at your organization, for extra jobs, and grant appropriate database privilege on every individual tables. That’s too cumbersome!_\r\n\r\nSince Power Query doesn't currently support POST web request with windows authentication. However, windows authentication is a necessity in most intranet-enterprise scenarios, so we have to use GET web request with windows authentication for now. Fortunately, DbWebApi accepts input parameters from either POST body or URL query string. This provides an easy workaround in many situations.\r\n\r\n\u0026nbsp;\r\n\r\n### Restrictions  \r\n* Only primitive database data types are supported -- can be mapped to .NET Framework simple data types which implement the [IConvertible](https://msdn.microsoft.com/en-us/library/system.iconvertible.aspx) interface.\r\n* Database User-Defined Types, Oracle composite data types (such as Collection Types, Varrays, Nested Tables, etc.) are currently not supported in result set columns nor in sp/func parameters.  \r\n[Table-Valued Parameters (SQL Server 2008)](https://msdn.microsoft.com/en-us/library/bb675163.aspx) and [PL/SQL Associative Array Parameters](http://docs.oracle.com/cd/E51173_01/win.122/e17732/featOraCommand.htm#BABBDHBB) are supported only in sp/func input parameters.    \r\n* All database procedure-names, function-names, column-names and parameter-names are regarded as case-insensitive.\r\n* [Overloading of Oracle stored procedure or function](https://docs.oracle.com/database/121/LNPLS/subprograms.htm#i12352) is not supported.\r\n\r\n\r\n\r\n## NuGet\r\n#### Server side\r\nThere are 4 NuGet packages for 4 differenct versions of ADO.NET providers:\r\n- [DbWebApi for SQL Server](https://www.nuget.org/packages/DataBooster.DbWebApi.SqlServer)\r\n- [DbWebApi for Oracle (use ODP.NET Managed Driver)](https://www.nuget.org/packages/DataBooster.DbWebApi.Oracle.Managed)\r\n- [DbWebApi for Oracle (use ODP.NET Provider)](https://www.nuget.org/packages/DataBooster.DbWebApi.Oracle.ODP)\r\n- [DbWebApi for Oracle (use DataDirect Provider)](https://www.nuget.org/packages/DataBooster.DbWebApi.Oracle.DataDirect)\r\n\r\nFor-Oracle versions always contain the support for SQL Server. To switch from Oracle to SQL Server, simply change the providerName and connectionString of connectionStrings \"DataBooster.DbWebApi.MainConnection\" in your web.config.  \r\nTo switch above from one NuGet package to another NuGet Package, simply uninstall one and install another from NuGet Package Manager.\r\n#### Clients\r\n- [DbWebApi Client .Net Library](https://www.nuget.org/packages/DataBooster.DbWebApi.Client.Net)\r\n- [DbWebApi Client JavaScript Library](https://www.nuget.org/packages/DataBooster.DbWebApi.Client.JS)\r\n- [DbWebApi Client Command-line Utility](https://www.nuget.org/packages/DataBooster.DbWebApi.Client.CmdUtility)\r\n- PowerShell:\r\n- [DbWebApi Client PowerShell Module _on PowerShell Gallery_](http://www.powershellgallery.com/packages/DbWebApi-Client)\r\n- [DbWebApi Client PowerShell Utility _on NuGet Gallery_](https://www.nuget.org/packages/DataBooster.DbWebApi.Client.PowerShell)\r\n\r\n\r\n### SymbolSource\r\nAssociated Symbols Packages with above NuGet Packages are also available in http://srv.symbolsource.org/pdb/Public.  \r\nTo source step during debugging into the code of libraries, please see [SymbolSource.org](http://www.symbolsource.org/Public/Home/VisualStudio) for detailed instructions.\r\n\r\n## Examples\r\n\r\nPlease refer to example projects - MyDbWebApi in https://github.com/DataBooster/DbWebApi/tree/master/Server/Sample  \r\n- DbWebApi.SampleServers.sln for Visual Studio 2012 or later\r\n- DbWebApi.SampleServers.Net40.sln for Visual Studio 2010 with ASP.NET MVC 4 installed\r\n\r\nInside the solutions, both .Net45 branch and .Net40 branch are further divided into 4 projects for - SQL Server, Oracle (ODP.NET Managed, ODP.NET Unmanaged and DataDirect provider). You can keep one of them as needed and removed all the rest. Hopefully, base on the examples, it's easier to customize it as your own DbWebApi server.\r\n\r\nIf you are only interested in having your trial server setup quickly, you can download the released server side samples from https://github.com/DataBooster/DbWebApi/releases simplicity.\r\n\r\nBy default, the example server is configured for intranet environment:\r\n\r\n[Web.config](https://github.com/DataBooster/DbWebApi/blob/master/Server/Sample/MyDbWebApi/Web.MyDbWebApi.config)\r\n``` XML\r\n\u003cconfiguration\u003e\r\n  \u003csystem.web\u003e\r\n    \u003cauthentication mode=\"Windows\" /\u003e\r\n  \u003c/system.web\u003e\r\n  \u003cconnectionStrings\u003e\r\n    \u003cadd name=\"DataBooster.DbWebApi.MainConnection\" providerName=\"System.Data.SqlClient\" connectionString=\"Data Source=.\\SQLEXPRESS;Initial Catalog=SAMPLEDB;Integrated Security=SSPI;Min Pool Size=8\" /\u003e\r\n  \u003c/connectionStrings\u003e\r\n\u003c/configuration\u003e\r\n```\r\n\r\n[DbWebApiController.cs](https://github.com/DataBooster/DbWebApi/blob/master/Server/Sample/MyDbWebApi/Controllers/DbWebApiController.cs)\r\n``` CSharp\r\nnamespace MyDbWebApi.Controllers\r\n{\r\n    [DbWebApiAuthorize]\r\n    public class DbWebApiController : ApiController\r\n    {\r\n        ...\r\n    }\r\n}\r\n```\r\n\r\n[MyDbWebApiAuthorization.cs](https://github.com/DataBooster/DbWebApi/blob/master/Server/Sample/MyDbWebApi/Filters/MyDbWebApiAuthorization.cs)\r\n``` CSharp\r\nnamespace MyDbWebApi\r\n{\r\n    public class MyDbWebApiAuthorization : IDbWebApiAuthorization\r\n    {\r\n        public bool IsAuthorized(string userName, string storedProcedure, object state = null)\r\n        {\r\n            // TO DO, to implementate your own authorization logic\r\n            return true;\t// If allow permission\r\n            return false;\t// If deny permission\r\n        }\r\n    }\r\n}\r\n```\r\n\r\nAccording to your own circumstances, above should be modified as needed, just like the most basic settings should be applied on an Empty ASP.NET Web API project.\r\n\r\n_Notes: the example web sites are just for hosting the DbWebApi, there is no default page in them, so you would see HTTP Error 403.14 when you open the nonexistent home page, that's normal._\r\n\r\nWelcome all feedback through the [Issues](https://github.com/DataBooster/DbWebApi/issues).\r\n","funding_links":[],"categories":["C\\#"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FDataBooster%2FDbWebApi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FDataBooster%2FDbWebApi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FDataBooster%2FDbWebApi/lists"}