├── .github ├── CODE_OF_CONDUCT.md ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── CHANGELOG.md ├── CONTRIBUTING.md ├── DeleteToDo.cs ├── GetToDo.cs ├── LICENSE.md ├── PatchToDo.cs ├── PostToDo.cs ├── README.md ├── ToDo.cs ├── ToDoModel.cs ├── azure-sql-binding-func-dotnet-todo.csproj ├── default.local.settings.json ├── host.json ├── images ├── testing.png └── validation.png ├── sql └── create.sql └── testing.http /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 4 | > Please provide us with the following information: 5 | > --------------------------------------------------------------- 6 | 7 | ### This issue is for a: (mark with an `x`) 8 | ``` 9 | - [ ] bug report -> please search issues before submitting 10 | - [ ] feature request 11 | - [ ] documentation issue or request 12 | - [ ] regression (a behavior that used to work and stopped in a new release) 13 | ``` 14 | 15 | ### Minimal steps to reproduce 16 | > 17 | 18 | ### Any log messages given by the failure 19 | > 20 | 21 | ### Expected/desired behavior 22 | > 23 | 24 | ### OS and Version? 25 | > Windows 7, 8 or 10. Linux (which distribution). macOS (Yosemite? El Capitan? Sierra?) 26 | 27 | ### Versions 28 | > 29 | 30 | ### Mention any other details that might be useful 31 | 32 | > --------------------------------------------------------------- 33 | > Thanks! We'll be in touch soon. 34 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Purpose 2 | 3 | * ... 4 | 5 | ## Does this introduce a breaking change? 6 | 7 | ``` 8 | [ ] Yes 9 | [ ] No 10 | ``` 11 | 12 | ## Pull Request Type 13 | What kind of change does this Pull Request introduce? 14 | 15 | 16 | ``` 17 | [ ] Bugfix 18 | [ ] Feature 19 | [ ] Code style update (formatting, local variables) 20 | [ ] Refactoring (no functional changes, no api changes) 21 | [ ] Documentation content changes 22 | [ ] Other... Please describe: 23 | ``` 24 | 25 | ## How to Test 26 | * Get the code 27 | 28 | ``` 29 | git clone [repo-address] 30 | cd [repo-name] 31 | git checkout [branch-name] 32 | npm install 33 | ``` 34 | 35 | * Test the code 36 | 37 | ``` 38 | ``` 39 | 40 | ## What to Check 41 | Verify that the following are valid 42 | * ... 43 | 44 | ## Other Information 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | # Visual Studio 2017 auto generated files 40 | Generated\ Files/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | # NUnit 47 | *.VisualState.xml 48 | TestResult.xml 49 | nunit-*.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # Benchmark Results 57 | BenchmarkDotNet.Artifacts/ 58 | 59 | # .NET Core 60 | project.lock.json 61 | project.fragment.lock.json 62 | artifacts/ 63 | 64 | # StyleCop 65 | StyleCopReport.xml 66 | 67 | # Files built by Visual Studio 68 | *_i.c 69 | *_p.c 70 | *_h.h 71 | *.ilk 72 | *.meta 73 | *.obj 74 | *.iobj 75 | *.pch 76 | *.pdb 77 | *.ipdb 78 | *.pgc 79 | *.pgd 80 | *.rsp 81 | *.sbr 82 | *.tlb 83 | *.tli 84 | *.tlh 85 | *.tmp 86 | *.tmp_proj 87 | *_wpftmp.csproj 88 | *.log 89 | *.vspscc 90 | *.vssscc 91 | .builds 92 | *.pidb 93 | *.svclog 94 | *.scc 95 | 96 | # Chutzpah Test files 97 | _Chutzpah* 98 | 99 | # Visual C++ cache files 100 | ipch/ 101 | *.aps 102 | *.ncb 103 | *.opendb 104 | *.opensdf 105 | *.sdf 106 | *.cachefile 107 | *.VC.db 108 | *.VC.VC.opendb 109 | 110 | # Visual Studio profiler 111 | *.psess 112 | *.vsp 113 | *.vspx 114 | *.sap 115 | 116 | # Visual Studio Trace Files 117 | *.e2e 118 | 119 | # TFS 2012 Local Workspace 120 | $tf/ 121 | 122 | # Guidance Automation Toolkit 123 | *.gpState 124 | 125 | # ReSharper is a .NET coding add-in 126 | _ReSharper*/ 127 | *.[Rr]e[Ss]harper 128 | *.DotSettings.user 129 | 130 | # TeamCity is a build add-in 131 | _TeamCity* 132 | 133 | # DotCover is a Code Coverage Tool 134 | *.dotCover 135 | 136 | # AxoCover is a Code Coverage Tool 137 | .axoCover/* 138 | !.axoCover/settings.json 139 | 140 | # Visual Studio code coverage results 141 | *.coverage 142 | *.coveragexml 143 | 144 | # NCrunch 145 | _NCrunch_* 146 | .*crunch*.local.xml 147 | nCrunchTemp_* 148 | 149 | # MightyMoose 150 | *.mm.* 151 | AutoTest.Net/ 152 | 153 | # Web workbench (sass) 154 | .sass-cache/ 155 | 156 | # Installshield output folder 157 | [Ee]xpress/ 158 | 159 | # DocProject is a documentation generator add-in 160 | DocProject/buildhelp/ 161 | DocProject/Help/*.HxT 162 | DocProject/Help/*.HxC 163 | DocProject/Help/*.hhc 164 | DocProject/Help/*.hhk 165 | DocProject/Help/*.hhp 166 | DocProject/Help/Html2 167 | DocProject/Help/html 168 | 169 | # Click-Once directory 170 | publish/ 171 | 172 | # Publish Web Output 173 | *.[Pp]ublish.xml 174 | *.azurePubxml 175 | # Note: Comment the next line if you want to checkin your web deploy settings, 176 | # but database connection strings (with potential passwords) will be unencrypted 177 | *.pubxml 178 | *.publishproj 179 | 180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 181 | # checkin your Azure Web App publish settings, but sensitive information contained 182 | # in these scripts will be unencrypted 183 | PublishScripts/ 184 | 185 | # NuGet Packages 186 | *.nupkg 187 | # NuGet Symbol Packages 188 | *.snupkg 189 | # The packages folder can be ignored because of Package Restore 190 | **/[Pp]ackages/* 191 | # except build/, which is used as an MSBuild target. 192 | !**/[Pp]ackages/build/ 193 | # Uncomment if necessary however generally it will be regenerated when needed 194 | #!**/[Pp]ackages/repositories.config 195 | # NuGet v3's project.json files produces more ignorable files 196 | *.nuget.props 197 | *.nuget.targets 198 | 199 | # Microsoft Azure Build Output 200 | csx/ 201 | *.build.csdef 202 | 203 | # Microsoft Azure Emulator 204 | ecf/ 205 | rcf/ 206 | 207 | # Windows Store app package directories and files 208 | AppPackages/ 209 | BundleArtifacts/ 210 | Package.StoreAssociation.xml 211 | _pkginfo.txt 212 | *.appx 213 | *.appxbundle 214 | *.appxupload 215 | 216 | # Visual Studio cache files 217 | # files ending in .cache can be ignored 218 | *.[Cc]ache 219 | # but keep track of directories ending in .cache 220 | !?*.[Cc]ache/ 221 | 222 | # Others 223 | ClientBin/ 224 | ~$* 225 | *~ 226 | *.dbmdl 227 | *.dbproj.schemaview 228 | *.jfm 229 | *.pfx 230 | *.publishsettings 231 | orleans.codegen.cs 232 | 233 | # Including strong name files can present a security risk 234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 235 | #*.snk 236 | 237 | # Since there are multiple workflows, uncomment next line to ignore bower_components 238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 239 | #bower_components/ 240 | 241 | # RIA/Silverlight projects 242 | Generated_Code/ 243 | 244 | # Backup & report files from converting an old project file 245 | # to a newer Visual Studio version. Backup files are not needed, 246 | # because we have git ;-) 247 | _UpgradeReport_Files/ 248 | Backup*/ 249 | UpgradeLog*.XML 250 | UpgradeLog*.htm 251 | ServiceFabricBackup/ 252 | *.rptproj.bak 253 | 254 | # SQL Server files 255 | *.mdf 256 | *.ldf 257 | *.ndf 258 | 259 | # Business Intelligence projects 260 | *.rdl.data 261 | *.bim.layout 262 | *.bim_*.settings 263 | *.rptproj.rsuser 264 | *- [Bb]ackup.rdl 265 | *- [Bb]ackup ([0-9]).rdl 266 | *- [Bb]ackup ([0-9][0-9]).rdl 267 | 268 | # Microsoft Fakes 269 | FakesAssemblies/ 270 | 271 | # GhostDoc plugin setting file 272 | *.GhostDoc.xml 273 | 274 | # Node.js Tools for Visual Studio 275 | .ntvs_analysis.dat 276 | node_modules/ 277 | 278 | # Visual Studio 6 build log 279 | *.plg 280 | 281 | # Visual Studio 6 workspace options file 282 | *.opt 283 | 284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 285 | *.vbw 286 | 287 | # Visual Studio LightSwitch build output 288 | **/*.HTMLClient/GeneratedArtifacts 289 | **/*.DesktopClient/GeneratedArtifacts 290 | **/*.DesktopClient/ModelManifest.xml 291 | **/*.Server/GeneratedArtifacts 292 | **/*.Server/ModelManifest.xml 293 | _Pvt_Extensions 294 | 295 | # Paket dependency manager 296 | .paket/paket.exe 297 | paket-files/ 298 | 299 | # FAKE - F# Make 300 | .fake/ 301 | 302 | # CodeRush personal settings 303 | .cr/personal 304 | 305 | # Python Tools for Visual Studio (PTVS) 306 | __pycache__/ 307 | *.pyc 308 | 309 | # Cake - Uncomment if you are using it 310 | # tools/** 311 | # !tools/packages.config 312 | 313 | # Tabs Studio 314 | *.tss 315 | 316 | # Telerik's JustMock configuration file 317 | *.jmconfig 318 | 319 | # BizTalk build output 320 | *.btp.cs 321 | *.btm.cs 322 | *.odx.cs 323 | *.xsd.cs 324 | 325 | # OpenCover UI analysis results 326 | OpenCover/ 327 | 328 | # Azure Stream Analytics local run output 329 | ASALocalRun/ 330 | 331 | # MSBuild Binary and Structured Log 332 | *.binlog 333 | 334 | # NVidia Nsight GPU debugger configuration file 335 | *.nvuser 336 | 337 | # MFractors (Xamarin productivity tool) working folder 338 | .mfractor/ 339 | 340 | # Local History for Visual Studio 341 | .localhistory/ 342 | 343 | # BeatPulse healthcheck temp database 344 | healthchecksdb 345 | 346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 347 | MigrationBackup/ 348 | 349 | # Ionide (cross platform F# VS Code tools) working folder 350 | .ionide/ 351 | 352 | 353 | local.settings.json 354 | *.log -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "ms-azuretools.vscode-azurefunctions", 4 | "ms-dotnettools.csharp", 5 | "ms-mssql.mssql", 6 | "humao.rest-client" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Attach to .NET Functions", 6 | "type": "coreclr", 7 | "request": "attach", 8 | "processId": "${command:azureFunctions.pickProcess}" 9 | } 10 | ] 11 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "azureFunctions.deploySubpath": "bin/Release/net6.0/publish", 3 | "azureFunctions.projectLanguage": "C#", 4 | "azureFunctions.projectRuntime": "~4", 5 | "debug.internalConsoleOptions": "neverOpen", 6 | "azureFunctions.preDeployTask": "publish (functions)" 7 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "clean (functions)", 6 | "command": "dotnet", 7 | "args": [ 8 | "clean", 9 | "/property:GenerateFullPaths=true", 10 | "/consoleloggerparameters:NoSummary" 11 | ], 12 | "type": "process", 13 | "problemMatcher": "$msCompile" 14 | }, 15 | { 16 | "label": "build (functions)", 17 | "command": "dotnet", 18 | "args": [ 19 | "build", 20 | "/property:GenerateFullPaths=true", 21 | "/consoleloggerparameters:NoSummary" 22 | ], 23 | "type": "process", 24 | "dependsOn": "clean (functions)", 25 | "group": { 26 | "kind": "build", 27 | "isDefault": true 28 | }, 29 | "problemMatcher": "$msCompile" 30 | }, 31 | { 32 | "label": "clean release (functions)", 33 | "command": "dotnet", 34 | "args": [ 35 | "clean", 36 | "--configuration", 37 | "Release", 38 | "/property:GenerateFullPaths=true", 39 | "/consoleloggerparameters:NoSummary" 40 | ], 41 | "type": "process", 42 | "problemMatcher": "$msCompile" 43 | }, 44 | { 45 | "label": "publish (functions)", 46 | "command": "dotnet", 47 | "args": [ 48 | "publish", 49 | "--configuration", 50 | "Release", 51 | "/property:GenerateFullPaths=true", 52 | "/consoleloggerparameters:NoSummary" 53 | ], 54 | "type": "process", 55 | "dependsOn": "clean release (functions)", 56 | "problemMatcher": "$msCompile" 57 | }, 58 | { 59 | "type": "func", 60 | "dependsOn": "build (functions)", 61 | "options": { 62 | "cwd": "${workspaceFolder}/bin/Debug/net6.0" 63 | }, 64 | "command": "host start", 65 | "isBackground": true, 66 | "problemMatcher": "$func-dotnet-watch" 67 | } 68 | ] 69 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [project-title] Changelog 2 | 3 | 4 | # x.y.z (yyyy-mm-dd) 5 | 6 | *Features* 7 | * ... 8 | 9 | *Bug Fixes* 10 | * ... 11 | 12 | *Breaking Changes* 13 | * ... 14 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to [project-title] 2 | 3 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 4 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 5 | the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. 6 | 7 | When you submit a pull request, a CLA bot will automatically determine whether you need to provide 8 | a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions 9 | provided by the bot. You will only need to do this once across all repos using our CLA. 10 | 11 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 12 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 13 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 14 | 15 | - [Code of Conduct](#coc) 16 | - [Issues and Bugs](#issue) 17 | - [Feature Requests](#feature) 18 | - [Submission Guidelines](#submit) 19 | 20 | ## Code of Conduct 21 | Help us keep this project open and inclusive. Please read and follow our [Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 22 | 23 | ## Found an Issue? 24 | If you find a bug in the source code or a mistake in the documentation, you can help us by 25 | [submitting an issue](#submit-issue) to the GitHub Repository. Even better, you can 26 | [submit a Pull Request](#submit-pr) with a fix. 27 | 28 | ## Want a Feature? 29 | You can *request* a new feature by [submitting an issue](#submit-issue) to the GitHub 30 | Repository. If you would like to *implement* a new feature, please submit an issue with 31 | a proposal for your work first, to be sure that we can use it. 32 | 33 | * **Small Features** can be crafted and directly [submitted as a Pull Request](#submit-pr). 34 | 35 | ## Submission Guidelines 36 | 37 | ### Submitting an Issue 38 | Before you submit an issue, search the archive, maybe your question was already answered. 39 | 40 | If your issue appears to be a bug, and hasn't been reported, open a new issue. 41 | Help us to maximize the effort we can spend fixing issues and adding new 42 | features, by not reporting duplicate issues. Providing the following information will increase the 43 | chances of your issue being dealt with quickly: 44 | 45 | * **Overview of the Issue** - if an error is being thrown a non-minified stack trace helps 46 | * **Version** - what version is affected (e.g. 0.1.2) 47 | * **Motivation for or Use Case** - explain what are you trying to do and why the current behavior is a bug for you 48 | * **Browsers and Operating System** - is this a problem with all browsers? 49 | * **Reproduce the Error** - provide a live example or a unambiguous set of steps 50 | * **Related Issues** - has a similar issue been reported before? 51 | * **Suggest a Fix** - if you can't fix the bug yourself, perhaps you can point to what might be 52 | causing the problem (line of code or commit) 53 | 54 | You can file new issues by providing the above information at the corresponding repository's issues link: https://github.com/[organization-name]/[repository-name]/issues/new]. 55 | 56 | ### Submitting a Pull Request (PR) 57 | Before you submit your Pull Request (PR) consider the following guidelines: 58 | 59 | * Search the repository (https://github.com/[organization-name]/[repository-name]/pulls) for an open or closed PR 60 | that relates to your submission. You don't want to duplicate effort. 61 | 62 | * Make your changes in a new git fork: 63 | 64 | * Commit your changes using a descriptive commit message 65 | * Push your fork to GitHub: 66 | * In GitHub, create a pull request 67 | * If we suggest changes then: 68 | * Make the required updates. 69 | * Rebase your fork and force push to your GitHub repository (this will update your Pull Request): 70 | 71 | ```shell 72 | git rebase master -i 73 | git push -f 74 | ``` 75 | 76 | That's it! Thank you for your contribution! 77 | -------------------------------------------------------------------------------- /DeleteToDo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. See LICENSE.md in the project root for license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using Microsoft.AspNetCore.Mvc; 7 | using Microsoft.Azure.WebJobs; 8 | using Microsoft.Azure.WebJobs.Extensions.Http; 9 | using Microsoft.AspNetCore.Http; 10 | using Microsoft.Extensions.Logging; 11 | 12 | namespace AzureSQL.ToDo 13 | { 14 | public static class DeleteToDo 15 | { 16 | // delete all items or a specific item from querystring 17 | // returns remaining items 18 | // uses input binding with a stored procedure DeleteToDo to delete items and return remaining items 19 | [FunctionName("DeleteToDo")] 20 | public static IActionResult Run( 21 | [HttpTrigger(AuthorizationLevel.Anonymous, "delete", Route = "DeleteFunction")] HttpRequest req, 22 | ILogger log, 23 | [Sql(commandText: "DeleteToDo", commandType: System.Data.CommandType.StoredProcedure, 24 | parameters: "@Id={Query.id}", connectionStringSetting: "SqlConnectionString")] 25 | IEnumerable toDoItems) 26 | { 27 | return new OkObjectResult(toDoItems); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /GetToDo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. See LICENSE.md in the project root for license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Threading.Tasks; 7 | using Microsoft.AspNetCore.Http; 8 | using Microsoft.AspNetCore.Mvc; 9 | using Microsoft.Azure.WebJobs; 10 | using Microsoft.Azure.WebJobs.Extensions.Http; 11 | using Microsoft.Extensions.Logging; 12 | 13 | namespace AzureSQL.ToDo 14 | { 15 | public static class GetToDos 16 | { 17 | // return items from ToDo table 18 | // id querystring in the query text to filter if specified 19 | // uses input binding to run the query and return the results 20 | [FunctionName("GetToDos")] 21 | public static async Task Run( 22 | [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "GetFunction")] HttpRequest req, 23 | ILogger log, 24 | [Sql(commandText: "if @Id = '' select Id, [order], title, url, completed from dbo.ToDo else select Id, [order], title, url, completed from dbo.ToDo where @Id = Id", connectionStringSetting: "SqlConnectionString", commandType: System.Data.CommandType.Text, parameters: "@Id={Query.id}")] IAsyncEnumerable toDos) 25 | { 26 | return new OkObjectResult(toDos); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE -------------------------------------------------------------------------------- /PatchToDo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. See LICENSE.md in the project root for license information. 3 | 4 | using System; 5 | using System.IO; 6 | using System.Collections.Generic; 7 | using System.Threading.Tasks; 8 | using Microsoft.AspNetCore.Http; 9 | using Microsoft.AspNetCore.Mvc; 10 | using Microsoft.Azure.WebJobs; 11 | using Microsoft.Azure.WebJobs.Extensions.Http; 12 | using Microsoft.Extensions.Logging; 13 | using Newtonsoft.Json; 14 | 15 | namespace AzureSQL.ToDo 16 | { 17 | public static class PatchToDo 18 | { 19 | // update an item from new data in body object 20 | // receives a list in the body with the existing data in at first position, and updates in at second position 21 | // uses output binding to update the row in ToDo table 22 | [FunctionName("PatchToDo")] 23 | public static async Task Run( 24 | [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "PatchFunction")] HttpRequest req, 25 | ILogger log, 26 | [Sql(commandText: "dbo.ToDo", connectionStringSetting: "SqlConnectionString")] IAsyncCollector toDoItems) 27 | { 28 | string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); 29 | List incomingToDoItems = JsonConvert.DeserializeObject>(requestBody); 30 | 31 | // existing at first position, new at second position 32 | ToDoItem toDoItem = incomingToDoItems[0]; 33 | ToDoItem newToDoItem = incomingToDoItems[1]; 34 | 35 | // compare the two items attributes 36 | if (newToDoItem.title != null) 37 | { 38 | toDoItem.title = newToDoItem.title; 39 | } 40 | if (newToDoItem.order != null) 41 | { 42 | toDoItem.order = newToDoItem.order; 43 | } 44 | if (newToDoItem.completed != null) 45 | { 46 | toDoItem.completed = newToDoItem.completed; 47 | } 48 | 49 | await toDoItems.AddAsync(toDoItem); 50 | await toDoItems.FlushAsync(); 51 | 52 | return new OkObjectResult(toDoItem); 53 | 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /PostToDo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. See LICENSE.md in the project root for license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Threading.Tasks; 8 | using Microsoft.AspNetCore.Http; 9 | using Microsoft.AspNetCore.Mvc; 10 | using Microsoft.Azure.WebJobs; 11 | using Microsoft.Azure.WebJobs.Extensions.Http; 12 | using Microsoft.Extensions.Logging; 13 | using Newtonsoft.Json; 14 | 15 | namespace AzureSQL.ToDo 16 | { 17 | public static class PostToDo 18 | { 19 | // create a new ToDoItem from body object 20 | // uses output binding to insert new item into ToDo table 21 | [FunctionName("PostToDo")] 22 | public static async Task Run( 23 | [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "PostFunction")] HttpRequest req, 24 | ILogger log, 25 | [Sql(commandText: "dbo.ToDo", connectionStringSetting: "SqlConnectionString")] IAsyncCollector toDoItems) 26 | { 27 | string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); 28 | ToDoItem toDoItem = JsonConvert.DeserializeObject(requestBody); 29 | 30 | // generate a new id for the todo item 31 | toDoItem.Id = Guid.NewGuid(); 32 | 33 | // set Url from env variable ToDoUri 34 | toDoItem.url = Environment.GetEnvironmentVariable("ToDoUri")+"?id="+toDoItem.Id.ToString(); 35 | 36 | // if completed is not provided, default to false 37 | if (toDoItem.completed == null) 38 | { 39 | toDoItem.completed = false; 40 | } 41 | 42 | await toDoItems.AddAsync(toDoItem); 43 | await toDoItems.FlushAsync(); 44 | List toDoItemList = new List { toDoItem }; 45 | 46 | return new OkObjectResult(toDoItemList); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_type: sample 3 | languages: 4 | - csharp 5 | - sql 6 | - tsql 7 | products: 8 | - azure-functions 9 | - azure-sql-database 10 | - sql-server 11 | - azure-sql-managed-instance 12 | - azure-sqlserver-vm 13 | - dotnet 14 | name: ToDo API backend with Azure SQL bindings 15 | description: Implement the ToDo Backend in .NET with Azure SQL bindings for Azure Functions 16 | urlFragment: todo-backend-dotnet-azure-sql-bindings-azure-functions 17 | --- 18 | 19 | # ToDo Backend Implementation in .NET with Azure SQL bindings for Azure Functions 20 | 21 | ![License](https://img.shields.io/badge/license-MIT-green.svg) 22 | 23 | This project implements the [Todo Backend API](http://www.todobackend.com/index.html) using Azure Functions (.NET) and Azure SQL through HTTP triggers and Azure SQL input/output bindings. 24 | 25 | ## ToDo Backend 26 | 27 | This project implements a ToDo endpoint with the following actions: 28 | 29 | * GET all ToDos 30 | * GET a ToDo by Id 31 | * POST a new ToDo 32 | * PATCH an update to a ToDo 33 | * DELETE a ToDo by Id 34 | * DELETE all ToDos 35 | 36 | 37 | ## Getting Started 38 | 39 | ### Quickstart 40 | 41 | 1. Configure your development environment for creating Azure Functions with .NET. This [VS Code example](https://docs.microsoft.com/azure/azure-functions/create-first-function-vs-code-csharp?tabs=in-process#configure-your-environment) includes the .NET SDK, Azure Functions Core Tools, and the C# and Azure Functions extensions for VS Code. 42 | 2. Clone the repository to your environment 43 | 3. Create an [Azure SQL Database](https://docs.microsoft.com/azure/azure-sql/database/single-database-create-quickstart) and execute the script in [/sql/create.sql](/sql/create.sql) to create the table and stored procedure used by this example. Optionally the local development can be done against an local database, such as in a [Docker container](https://docs.microsoft.com/sql/linux/sql-server-linux-docker-container-deployment). 44 | 45 | 46 | ### Application Settings 47 | 48 | This example uses 2 application settings to control either secret or deployment-specific information. During local development, these values are stored in `local.settings.json` at the project root. 49 | 50 | * **Connection string to SQL database (General format)** "SqlConnectionString": "Server=tcp:SERVERNAME,PORT;Initial Catalog=DATABASENAME;Persist Security Info=False;User ID=USERNAME;Password=USERPASSWORD;" 51 | * **Primary endpoint URL** "ToDoUri": "http://localhost:7071/api/ToDo" 52 | 53 | 54 | ### Testing 55 | Using the [REST Client](https://marketplace.visualstudio.com/items?itemName=humao.rest-client) extension for VS Code, the [testing.http](testing.http) file provides example requests for the functionality. 56 | 57 | 58 | ![Testing in VS Code](./images/testing.png) 59 | 60 | ## Demo/Validation 61 | 62 | The ToDo backend specification has an accompanying runner for tests that validate the functionality. To run these tests on the demo endpoint, go to [http://todobackend.com/specs/index.html?https://azuresqltodo.azurewebsites.net/api/ToDo](http://todobackend.com/specs/index.html?https://azuresqltodo.azurewebsites.net/api/ToDo). 63 | 64 | The functions runtime and SQL database may have gone idle when you test the link, if you find tests fail retry after a few seconds. 65 | 66 | ![Validation](./images/validation.png) 67 | 68 | ## Resources 69 | 70 | 71 | - [Azure SQL binding for Azure Functions docs](https://docs.microsoft.com/azure/azure-functions/functions-bindings-azure-sql) 72 | - [Azure SQL binding for Azure Functions code and samples](https://github.com/Azure/azure-functions-sql-extension) 73 | -------------------------------------------------------------------------------- /ToDo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. See LICENSE.md in the project root for license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Net.Http; 8 | using System.Net.Http.Json; 9 | using System.Threading.Tasks; 10 | using Microsoft.AspNetCore.Mvc; 11 | using Microsoft.Azure.WebJobs; 12 | using Microsoft.Azure.WebJobs.Extensions.Http; 13 | using Microsoft.AspNetCore.Http; 14 | using Microsoft.Extensions.Logging; 15 | using Newtonsoft.Json; 16 | 17 | namespace AzureSQL.ToDo 18 | { 19 | public static class ToDo 20 | { 21 | // primary endpoint for the ToDo API 22 | // all other functions are dependencies from this function 23 | [FunctionName("ToDo")] 24 | public static async Task Run( 25 | [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", "patch", "delete", Route = null)] HttpRequest req, 26 | ILogger log) 27 | { 28 | // verify the id querystring is a Guid type 29 | string QId = req.Query["id"]; 30 | Guid GId = Guid.Empty; 31 | if (!Guid.TryParse(QId, out GId) && !string.IsNullOrEmpty(QId)) 32 | { 33 | return new BadRequestObjectResult($"Invalid id: {QId}"); 34 | } 35 | 36 | // parse the request body 37 | string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); 38 | var bodyObject = JsonConvert.DeserializeObject(requestBody); 39 | 40 | log.LogInformation("ToDo API"); 41 | log.LogInformation("Request Body: " + requestBody); 42 | log.LogInformation("Method: " + req.Method); 43 | 44 | List toDoResponseList = new List(); 45 | using (HttpClient client = new HttpClient()) { 46 | // get uri from app settings 47 | client.BaseAddress = new Uri(Environment.GetEnvironmentVariable("ToDoUri")); 48 | // setup the request query parameter for item id 49 | string queryParams = "?id=" + (QId ?? ""); 50 | 51 | // switch based on request method, pass query param and/or body object to the corresponding function 52 | switch (req.Method) 53 | { 54 | case "GET": 55 | var getResponse = await client.GetAsync("GetFunction"+queryParams); 56 | 57 | // return a list of ToDoItems if no querystring 58 | if (string.IsNullOrEmpty(QId)) 59 | { 60 | toDoResponseList = JsonConvert.DeserializeObject>(await getResponse.Content.ReadAsStringAsync()); 61 | return new OkObjectResult(toDoResponseList); 62 | } 63 | // return a single ToDoItem if querystring 64 | else 65 | { 66 | toDoResponseList = JsonConvert.DeserializeObject>(await getResponse.Content.ReadAsStringAsync()); 67 | if (toDoResponseList.Count > 0) 68 | { 69 | // return the first item in the list (should be only item) 70 | return new OkObjectResult(toDoResponseList[0]); 71 | } else { 72 | // return a 404 for no item found 73 | return new NotFoundObjectResult(toDoResponseList); 74 | } 75 | } 76 | case "POST": 77 | // create a new ToDoItem from body object 78 | var postResponse = await client.PostAsJsonAsync("PostFunction"+queryParams, bodyObject); 79 | toDoResponseList = JsonConvert.DeserializeObject>(await postResponse.Content.ReadAsStringAsync()); 80 | return new OkObjectResult(toDoResponseList[0]); 81 | 82 | case "PATCH": 83 | // update an item from new data in body object 84 | // need both the existing data and the new data to update 85 | // create a list and put the existing data in at first position 86 | var getExistingResponse = await client.GetAsync("GetFunction"+queryParams); 87 | List toDoPatchList = JsonConvert.DeserializeObject>(await getExistingResponse.Content.ReadAsStringAsync()); 88 | 89 | // put the patch body in at second position 90 | toDoPatchList.Add(JsonConvert.DeserializeObject(requestBody)); 91 | 92 | // call patch endpoint with list of existing and new data 93 | var patchResponse = await client.PostAsJsonAsync("PatchFunction"+queryParams, toDoPatchList); 94 | toDoResponseList.Add(JsonConvert.DeserializeObject(await patchResponse.Content.ReadAsStringAsync())); 95 | return new OkObjectResult(toDoResponseList[0]); 96 | 97 | case "DELETE": 98 | // delete all items or a specific item from querystring 99 | // returns remaining items 100 | var deleteResponse = await client.DeleteAsync("DeleteFunction"+queryParams); 101 | toDoResponseList = JsonConvert.DeserializeObject>(await deleteResponse.Content.ReadAsStringAsync()); 102 | return new OkObjectResult(toDoResponseList); 103 | default: 104 | //do nothing 105 | break; 106 | } 107 | } 108 | 109 | // return badrequest 110 | return new BadRequestObjectResult("Bad Request"); 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /ToDoModel.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. See LICENSE.md in the project root for license information. 3 | 4 | using System; 5 | 6 | namespace AzureSQL.ToDo 7 | { 8 | public class ToDoItem 9 | { 10 | public Guid Id { get; set; } 11 | public int? order { get; set; } 12 | public string title { get; set; } 13 | public string url { get; set; } 14 | public bool? completed { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /azure-sql-binding-func-dotnet-todo.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net6.0 4 | v4 5 | azure_sql_binding_func_dotnet_todo 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | PreserveNewest 14 | 15 | 16 | PreserveNewest 17 | Never 18 | 19 | 20 | -------------------------------------------------------------------------------- /default.local.settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "IsEncrypted": false, 3 | "Values": { 4 | "AzureWebJobsStorage": "", 5 | "FUNCTIONS_WORKER_RUNTIME": "dotnet", 6 | "SqlConnectionString": "Server={SERVERNAME};Initial Catalog={DATABASENAME};Persist Security Info=False;User ID={USERNAME};Password={PASSWORD};", 7 | "ToDoUri": "http://localhost:7071/api/ToDo" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0", 3 | "logging": { 4 | "applicationInsights": { 5 | "samplingSettings": { 6 | "isEnabled": true, 7 | "excludedTypes": "Request" 8 | } 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /images/testing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/azure-sql-binding-func-dotnet-todo/6cad5f58ddfd76208d6865ff52dc1eb2c13e5385/images/testing.png -------------------------------------------------------------------------------- /images/validation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/azure-sql-binding-func-dotnet-todo/6cad5f58ddfd76208d6865ff52dc1eb2c13e5385/images/validation.png -------------------------------------------------------------------------------- /sql/create.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE dbo.ToDo ( 2 | [Id] UNIQUEIDENTIFIER PRIMARY KEY, 3 | [order] INT NULL, 4 | [title] NVARCHAR(200) NOT NULL, 5 | [url] NVARCHAR(200) NOT NULL, 6 | [completed] BIT NOT NULL 7 | ); 8 | GO 9 | 10 | 11 | CREATE PROCEDURE [dbo].[DeleteToDo] 12 | @Id NVARCHAR(100) 13 | AS 14 | DECLARE @UID UNIQUEIDENTIFIER = TRY_CAST(@ID AS UNIQUEIDENTIFIER) 15 | IF @UId IS NOT NULL AND @Id != '' 16 | BEGIN 17 | DELETE FROM dbo.ToDo WHERE Id = @UID 18 | END 19 | ELSE 20 | BEGIN 21 | DELETE FROM dbo.ToDo WHERE @ID = '' 22 | END 23 | 24 | SELECT [Id], [order], [title], [url], [completed] FROM dbo.ToDo 25 | GO 26 | -------------------------------------------------------------------------------- /testing.http: -------------------------------------------------------------------------------- 1 | // retrieves all todo items from database 2 | GET http://localhost:7071/api/ToDo?id= HTTP/1.1 3 | 4 | ### 5 | // retrieves a specific todo item from database 6 | GET http://localhost:7071/api/ToDo?id=98230fc4-2ce7-4b1f-aad1-dabaf372b76f HTTP/1.1 7 | 8 | ### 9 | // retrieves an invalid todo item from database 10 | GET http://localhost:7071/api/ToDo?id=gdfsgsf HTTP/1.1 11 | 12 | ### 13 | // adds a new todo to the database 14 | POST http://localhost:7071/api/ToDo HTTP/1.1 15 | Content-Type: application/json 16 | 17 | {"title":"test this sample"} 18 | 19 | ### 20 | // updates a specific todo item from database 21 | PATCH http://localhost:7071/api/ToDo?id=94319c5c-4a11-47f0-a38b-45dc4807c8bd HTTP/1.1 22 | Content-Type: application/json 23 | 24 | {"title":"walk the dog"} 25 | 26 | ### 27 | // deletes a specific todo item from database 28 | DELETE http://localhost:7071/api/ToDo?id=98230fc4-2ce7-4b1f-aad1-dabaf372b76f HTTP/1.1 29 | 30 | ### 31 | // deletes all todo items from database 32 | DELETE http://localhost:7071/api/ToDo?id= HTTP/1.1 33 | --------------------------------------------------------------------------------