Skip to content

Commit 22557c0

Browse files
authored
Merge pull request #233807 from dzsquared/sqlbinding-april2023-csharp
adding tutorial article for output binding
2 parents acbbb2f + d6558a4 commit 22557c0

12 files changed

+659
-90
lines changed

articles/azure-functions/TOC.yml

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -161,14 +161,29 @@
161161
- name: Connect to a database
162162
items:
163163
- name: C#
164-
displayName: Connect to a database, Azure Cosmos DB
165-
href: functions-add-output-binding-cosmos-db-vs-code.md?pivots=programming-language-csharp
164+
items:
165+
- name: Azure Cosmos DB
166+
displayName: Connect to a database, Azure Cosmos DB
167+
href: functions-add-output-binding-cosmos-db-vs-code.md?pivots=programming-language-csharp
168+
- name: Azure SQL
169+
displayName: Connect to a database, Azure SQL
170+
href: functions-add-output-binding-azure-sql-vs-code.md?pivots=programming-language-csharp
166171
- name: JavaScript
167-
displayName: Connect to a database, Azure Cosmos DB
168-
href: functions-add-output-binding-cosmos-db-vs-code.md?pivots=programming-language-javascript
172+
items:
173+
- name: Azure Cosmos DB
174+
displayName: Connect to a database, Azure Cosmos DB
175+
href: functions-add-output-binding-cosmos-db-vs-code.md?pivots=programming-language-javascript
176+
- name: Azure SQL
177+
displayName: Connect to a database, Azure SQL
178+
href: functions-add-output-binding-azure-sql-vs-code.md?pivots=programming-language-javascript
169179
- name: Python
170-
displayName: Connect to a database, Azure Cosmos DB
171-
href: functions-add-output-binding-cosmos-db-vs-code.md?pivots=programming-language-python
180+
items:
181+
- name: Azure Cosmos DB
182+
displayName: Connect to a database, Azure Cosmos DB
183+
href: functions-add-output-binding-cosmos-db-vs-code.md?pivots=programming-language-python
184+
- name: Azure SQL
185+
displayName: Connect to a database, Azure SQL
186+
href: functions-add-output-binding-azure-sql-vs-code.md?pivots=programming-language-python
172187
- name: Tutorials
173188
items:
174189
- name: Functions with Logic Apps

articles/azure-functions/create-first-function-vs-code-csharp.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ The next article depends on your chosen process model.
7979
> [!div class="nextstepaction"]
8080
> [Connect to Azure Cosmos DB](functions-add-output-binding-cosmos-db-vs-code.md?pivots=programming-language-csharp&tabs=in-process)
8181
> [Connect to Azure Queue Storage](functions-add-output-binding-storage-queue-vs-code.md?pivots=programming-language-csharp&tabs=in-process)
82+
> [Connect to Azure SQL](functions-add-output-binding-azure-sql-vs-code.md?pivots=programming-language-csharp&tabs=in-process)
8283
8384
# [Isolated process](#tab/isolated-process)
8485

articles/azure-functions/create-first-function-vs-code-node.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ You have used [Visual Studio Code](functions-develop-vs-code.md?tabs=javascript)
186186
> [!div class="nextstepaction"]
187187
> [Connect to Azure Cosmos DB](functions-add-output-binding-cosmos-db-vs-code.md?pivots=programming-language-javascript)
188188
> [Connect to Azure Queue Storage](functions-add-output-binding-storage-queue-vs-code.md?pivots=programming-language-javascript)
189+
> [Connect to Azure SQL](functions-add-output-binding-azure-sql-vs-code.md?pivots=programming-language-javascript)
189190

190191
[Azure Functions Core Tools]: functions-run-local.md
191192
[Azure Functions extension for Visual Studio Code]: https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azurefunctions

articles/azure-functions/create-first-function-vs-code-python.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ You have used [Visual Studio Code](functions-develop-vs-code.md?tabs=python) to
208208

209209
> [!div class="nextstepaction"]
210210
> [Connect to an Azure Storage queue](functions-add-output-binding-storage-queue-vs-code.md?pivots=programming-language-python)
211+
> [Connect to Azure SQL](functions-add-output-binding-azure-sql-vs-code.md?pivots=programming-language-python)
211212

212213
[Having issues? Let us know.](https://aka.ms/python-functions-qs-survey)
213214

articles/azure-functions/functions-add-output-binding-azure-sql-vs-code.md

Lines changed: 543 additions & 0 deletions
Large diffs are not rendered by default.

articles/azure-functions/functions-bindings-azure-sql-input.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ description: Learn to use the Azure SQL input binding in Azure Functions.
44
author: dzsquared
55
ms.topic: reference
66
ms.custom: event-tier1-build-2022
7-
ms.date: 11/10/2022
7+
ms.date: 4/7/2023
88
ms.author: drskwier
99
ms.reviewer: glenga
1010
zone_pivot_groups: programming-languages-set-functions-lang-workers
@@ -65,7 +65,7 @@ namespace AzureSQLSamples
6565
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "gettodoitem")]
6666
HttpRequest req,
6767
[Sql(commandText: "select [Id], [order], [title], [url], [completed] from dbo.ToDo where Id = @Id",
68-
commandText: System.Data.CommandType.Text,
68+
commandType: System.Data.CommandType.Text,
6969
parameters: "@Id={Query.id}",
7070
connectionStringSetting: "SqlConnectionString")]
7171
IEnumerable<ToDoItem> toDoItem)
@@ -1081,6 +1081,8 @@ The attribute's constructor takes the SQL command text, the command type, parame
10811081

10821082
Queries executed by the input binding are [parameterized](/dotnet/api/microsoft.data.sqlclient.sqlparameter) in Microsoft.Data.SqlClient to reduce the risk of [SQL injection](/sql/relational-databases/security/sql-injection) from the parameter values passed into the binding.
10831083

1084+
If an exception occurs when a SQL input binding is executed then the function code will not execute. This may result in an error code being returned, such as an HTTP trigger returning a 500 error code.
1085+
10841086

10851087
::: zone-end
10861088

articles/azure-functions/functions-bindings-azure-sql-output.md

Lines changed: 56 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ description: Learn to use the Azure SQL output binding in Azure Functions.
44
author: dzsquared
55
ms.topic: reference
66
ms.custom: event-tier1-build-2022
7-
ms.date: 11/10/2022
7+
ms.date: 4/7/2023
88
ms.author: drskwier
99
ms.reviewer: glenga
1010
zone_pivot_groups: programming-languages-set-functions-lang-workers
@@ -162,20 +162,29 @@ This section contains the following examples:
162162

163163
* [HTTP trigger, write one record](#http-trigger-write-one-record-c-oop)
164164
* [HTTP trigger, write to two tables](#http-trigger-write-to-two-tables-c-oop)
165-
* [HTTP trigger, write records using IAsyncCollector](#http-trigger-write-records-using-iasynccollector-c-oop)
166165

167166
The examples refer to a `ToDoItem` class and a corresponding database table:
168167

169168
:::code language="csharp" source="~/functions-sql-todo-sample/ToDoModel.cs" range="6-16":::
170169

171170
:::code language="sql" source="~/functions-sql-todo-sample/sql/create.sql" range="1-7":::
172171

172+
To return [multiple output bindings](./dotnet-isolated-process-guide.md#multiple-output-bindings) in our samples, we will create a custom return type:
173+
174+
```cs
175+
public static class OutputType
176+
{
177+
[SqlOutput("dbo.ToDo", connectionStringSetting: "SqlConnectionString")]
178+
public ToDoItem ToDoItem { get; set; }
179+
public HttpResponseData HttpResponse { get; set; }
180+
}
181+
```
173182

174183
<a id="http-trigger-write-one-record-c-oop"></a>
175184

176185
### HTTP trigger, write one record
177186

178-
The following example shows a [C# function](functions-dotnet-class-library.md) that adds a record to a database, using data provided in an HTTP POST request as a JSON body.
187+
The following example shows a [C# function](functions-dotnet-class-library.md) that adds a record to a database, using data provided in an HTTP POST request as a JSON body. The return object is the `OutputType` class we created to handle both an HTTP response and the SQL output binding.
179188

180189
```cs
181190
using System;
@@ -195,11 +204,13 @@ namespace AzureSQL.ToDo
195204
// create a new ToDoItem from body object
196205
// uses output binding to insert new item into ToDo table
197206
[FunctionName("PostToDo")]
198-
public static async Task<IActionResult> Run(
199-
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "PostFunction")] HttpRequest req,
200-
ILogger log,
201-
[SqlOutput(commandText: "dbo.ToDo", connectionStringSetting: "SqlConnectionString")] IAsyncCollector<ToDoItem> toDoItems)
207+
public static async Task<OutputType> Run(
208+
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "PostFunction")] HttpRequestData req,
209+
FunctionContext executionContext)
202210
{
211+
var logger = executionContext.GetLogger("HttpExample");
212+
logger.LogInformation("C# HTTP trigger function processed a request.");
213+
203214
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
204215
ToDoItem toDoItem = JsonConvert.DeserializeObject<ToDoItem>(requestBody);
205216

@@ -215,13 +226,21 @@ namespace AzureSQL.ToDo
215226
toDoItem.completed = false;
216227
}
217228

218-
await toDoItems.AddAsync(toDoItem);
219-
await toDoItems.FlushAsync();
220-
List<ToDoItem> toDoItemList = new List<ToDoItem> { toDoItem };
221-
222-
return new OkObjectResult(toDoItemList);
229+
return new OutputType()
230+
{
231+
ToDoItem = toDoItem,
232+
HttpResponse = req.CreateResponse(System.Net.HttpStatusCode.Created)
233+
}
223234
}
224235
}
236+
237+
public static class OutputType
238+
{
239+
[SqlOutput("dbo.ToDo", connectionStringSetting: "SqlConnectionString")]
240+
public ToDoItem ToDoItem { get; set; }
241+
242+
public HttpResponseData HttpResponse { get; set; }
243+
}
225244
}
226245
```
227246

@@ -239,6 +258,7 @@ CREATE TABLE dbo.RequestLog (
239258
)
240259
```
241260

261+
To use an additional output binding, we add a class for `RequestLog` and modify our `OutputType` class:
242262

243263
```cs
244264
using System;
@@ -258,11 +278,9 @@ namespace AzureSQL.ToDo
258278
// create a new ToDoItem from body object
259279
// uses output binding to insert new item into ToDo table
260280
[FunctionName("PostToDo")]
261-
public static async Task<IActionResult> Run(
262-
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "PostFunction")] HttpRequest req,
263-
ILogger log,
264-
[SqlOutput(commandText: "dbo.ToDo", connectionStringSetting: "SqlConnectionString")] IAsyncCollector<ToDoItem> toDoItems,
265-
[SqlOutput(commandText: "dbo.RequestLog", connectionStringSetting: "SqlConnectionString")] IAsyncCollector<RequestLog> requestLogs)
281+
public static async Task<OutputType> Run(
282+
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "PostFunction")] HttpRequestData req,
283+
FunctionContext executionContext)
266284
{
267285
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
268286
ToDoItem toDoItem = JsonConvert.DeserializeObject<ToDoItem>(requestBody);
@@ -279,69 +297,41 @@ namespace AzureSQL.ToDo
279297
toDoItem.completed = false;
280298
}
281299

282-
await toDoItems.AddAsync(toDoItem);
283-
await toDoItems.FlushAsync();
284-
List<ToDoItem> toDoItemList = new List<ToDoItem> { toDoItem };
285-
286300
requestLog = new RequestLog();
287301
requestLog.RequestTimeStamp = DateTime.Now;
288302
requestLog.ItemCount = 1;
289-
await requestLogs.AddAsync(requestLog);
290-
await requestLogs.FlushAsync();
291303

292-
return new OkObjectResult(toDoItemList);
304+
return new OutputType()
305+
{
306+
ToDoItem = toDoItem,
307+
RequestLog = requestLog,
308+
HttpResponse = req.CreateResponse(System.Net.HttpStatusCode.Created)
309+
}
293310
}
294311
}
295312

296313
public class RequestLog {
297314
public DateTime RequestTimeStamp { get; set; }
298315
public int ItemCount { get; set; }
299316
}
300-
}
301-
```
302-
303-
<a id="http-trigger-write-records-using-iasynccollector-c-oop"></a>
304-
305-
### HTTP trigger, write records using IAsyncCollector
306-
307-
The following example shows a [C# function](functions-dotnet-class-library.md) that adds a collection of records to a database, using data provided in an HTTP POST body JSON array.
308-
309-
```cs
310-
using Microsoft.AspNetCore.Http;
311-
using Microsoft.AspNetCore.Mvc;
312-
using Microsoft.Azure.Functions.Worker;
313-
using Microsoft.Azure.Functions.Worker.Extensions.Sql;
314-
using Microsoft.Azure.Functions.Worker.Http;
315-
using Newtonsoft.Json;
316-
using System.IO;
317-
using System.Threading.Tasks;
318-
319-
namespace AzureSQLSamples
320-
{
321-
public static class WriteRecordsAsync
317+
318+
public static class OutputType
322319
{
323-
[FunctionName("WriteRecordsAsync")]
324-
public static async Task<IActionResult> Run(
325-
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "addtodo-asynccollector")]
326-
HttpRequest req,
327-
[SqlOutput(commandText: "dbo.ToDo", connectionStringSetting: "SqlConnectionString")] IAsyncCollector<ToDoItem> newItems)
328-
{
329-
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
330-
var incomingItems = JsonConvert.DeserializeObject<ToDoItem[]>(requestBody);
331-
foreach (ToDoItem newItem in incomingItems)
332-
{
333-
await newItems.AddAsync(newItem);
334-
}
335-
// Rows are upserted here
336-
await newItems.FlushAsync();
320+
[SqlOutput("dbo.ToDo", connectionStringSetting: "SqlConnectionString")]
321+
public ToDoItem ToDoItem { get; set; }
337322

338-
return new CreatedResult($"/api/addtodo-asynccollector", "done");
339-
}
323+
[SqlOutput("dbo.RequestLog", connectionStringSetting: "SqlConnectionString")]
324+
public RequestLog RequestLog { get; set; }
325+
326+
public HttpResponseData HttpResponse { get; set; }
340327
}
328+
341329
}
342330
```
343331

344332

333+
334+
345335
# [C# Script](#tab/csharp-script)
346336

347337

@@ -1197,7 +1187,10 @@ The following table explains the binding configuration properties that you set i
11971187
::: zone pivot="programming-language-csharp,programming-language-javascript,programming-language-powershell,programming-language-python,programming-language-java"
11981188
The `CommandText` property is the name of the table where the data is to be stored. The connection string setting name corresponds to the application setting that contains the [connection string](/dotnet/api/microsoft.data.sqlclient.sqlconnection.connectionstring?view=sqlclient-dotnet-core-5.0&preserve-view=true#Microsoft_Data_SqlClient_SqlConnection_ConnectionString) to the Azure SQL or SQL Server instance.
11991189

1200-
The output bindings use the T-SQL [MERGE](/sql/t-sql/statements/merge-transact-sql) statement which requires [SELECT](/sql/t-sql/statements/merge-transact-sql#permissions) permissions on the target database.
1190+
The output bindings use the T-SQL [MERGE](/sql/t-sql/statements/merge-transact-sql) statement which requires [SELECT](/sql/t-sql/statements/merge-transact-sql#permissions) permissions on the target database.
1191+
1192+
If an exception occurs when a SQL output binding is executed then the function code stop executing. This may result in an error code being returned, such as an HTTP trigger returning a 500 error code. If the `IAsyncCollector` is used in a .NET function then the function code can handle exceptions throw by the call to `FlushAsync()`.
1193+
12011194

12021195
::: zone-end
12031196

0 commit comments

Comments
 (0)