From b67a2e1ffbd07351c5a7b9c2dc5b228df23360e8 Mon Sep 17 00:00:00 2001 From: Jovan Popovic Date: Mon, 20 Mar 2017 21:49:19 +0100 Subject: [PATCH] Multiple changes due to SqlClient update --- .../belgrade-product-catalog-demo/.gitignore | 3 +- .../belgrade-product-catalog-demo/Startup.cs | 2 +- .../logs/linedelimited.fmt | 3 + .../project.json | 2 +- .../sql-scripts/bcp.sql.tt | 8 ++ .../Controllers/PeopleController.cs | 26 ++++-- .../demos/ivs-people-register/project.json | 6 +- .../json/azure-function-odata/README.md | 84 +++++++++++++++++++ .../azure-function/project.json | 10 +++ .../azure-function/run.csx | 20 +++++ .../LoadFromAzureBlobStorage.sql | 61 +++++++++----- 11 files changed, 191 insertions(+), 34 deletions(-) create mode 100644 samples/demos/belgrade-product-catalog-demo/logs/linedelimited.fmt create mode 100644 samples/demos/belgrade-product-catalog-demo/sql-scripts/bcp.sql.tt create mode 100644 samples/features/json/azure-function-odata/README.md create mode 100644 samples/features/json/azure-function-odata/azure-function/project.json create mode 100644 samples/features/json/azure-function-odata/azure-function/run.csx diff --git a/samples/demos/belgrade-product-catalog-demo/.gitignore b/samples/demos/belgrade-product-catalog-demo/.gitignore index e1ffc1417f..03331010ec 100644 --- a/samples/demos/belgrade-product-catalog-demo/.gitignore +++ b/samples/demos/belgrade-product-catalog-demo/.gitignore @@ -9,4 +9,5 @@ obj/* Properties/PublishProfiles/* appsettings.Development.json appsettings.Production.json -*.ndjson \ No newline at end of file +*.ndjson +sql-scripts/bcp.sql.sql \ No newline at end of file diff --git a/samples/demos/belgrade-product-catalog-demo/Startup.cs b/samples/demos/belgrade-product-catalog-demo/Startup.cs index f115293e87..4e9448236a 100644 --- a/samples/demos/belgrade-product-catalog-demo/Startup.cs +++ b/samples/demos/belgrade-product-catalog-demo/Startup.cs @@ -31,7 +31,7 @@ public Startup(IHostingEnvironment env) Configuration = builder.Build(); #if NETCOREAPP1_0 Log.Logger = new LoggerConfiguration() - .WriteTo.RollingFile(new Serilog.Formatting.Json.JsonFormatter(), System.IO.Path.Combine(env.ContentRootPath, "log-{Date}.ndjson")) + .WriteTo.RollingFile(new Serilog.Formatting.Json.JsonFormatter(), System.IO.Path.Combine(env.ContentRootPath, "logs\\log-{Date}.ndjson")) .CreateLogger(); #endif #if NET46 diff --git a/samples/demos/belgrade-product-catalog-demo/logs/linedelimited.fmt b/samples/demos/belgrade-product-catalog-demo/logs/linedelimited.fmt new file mode 100644 index 0000000000..91a7660e14 --- /dev/null +++ b/samples/demos/belgrade-product-catalog-demo/logs/linedelimited.fmt @@ -0,0 +1,3 @@ +12.0 +1 +1 SQLCHAR 0 80000 "\r\n" 1 JSON SQL_Latin1_General_CP1_CI_AS diff --git a/samples/demos/belgrade-product-catalog-demo/project.json b/samples/demos/belgrade-product-catalog-demo/project.json index c3a083b4bf..4f3b71e357 100644 --- a/samples/demos/belgrade-product-catalog-demo/project.json +++ b/samples/demos/belgrade-product-catalog-demo/project.json @@ -1,6 +1,6 @@ { "dependencies": { - "Belgrade.Sql.Client": "0.6.5", + "Belgrade.Sql.Client": "0.7", "Microsoft.AspNetCore.Mvc": "1.0.0", "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0", "Microsoft.AspNetCore.Server.Kestrel": "1.0.0", diff --git a/samples/demos/belgrade-product-catalog-demo/sql-scripts/bcp.sql.tt b/samples/demos/belgrade-product-catalog-demo/sql-scripts/bcp.sql.tt new file mode 100644 index 0000000000..c7526b6b1e --- /dev/null +++ b/samples/demos/belgrade-product-catalog-demo/sql-scripts/bcp.sql.tt @@ -0,0 +1,8 @@ +<#@ output extension=".sql" #> +<#@ template language="C#" hostspecific="True" #> + + +SELECT * +FROM OPENROWSET(BULK '<#=this.Host.ResolvePath("..\\logs") #>\log-20170203.ndjson', + FORMATFILE = '<#=this.Host.ResolvePath("..\\logs") #>\linedelimited.fmt' ); + diff --git a/samples/demos/ivs-people-register/Controllers/PeopleController.cs b/samples/demos/ivs-people-register/Controllers/PeopleController.cs index 367d50ffba..4d8bee17cb 100644 --- a/samples/demos/ivs-people-register/Controllers/PeopleController.cs +++ b/samples/demos/ivs-people-register/Controllers/PeopleController.cs @@ -1,7 +1,6 @@ using Belgrade.SqlClient; using Microsoft.AspNetCore.Mvc; -using SqlServerRestApi.Controller; -using SqlServerRestApi.SQL; +using SqlServerRestApi; using System.Threading.Tasks; // For more information on enabling Web API for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860 @@ -10,12 +9,12 @@ namespace Register.Controllers [Route("api/[controller]")] public class PeopleController : Controller { - IQueryPipe sqlQuery = null; + IQueryPipe pipe = null; TableSpec tableSpec = new TableSpec("dbo.People", "name,surname,address,town"); public PeopleController(IQueryPipe sqlQueryService) { - this.sqlQuery = sqlQueryService; + this.pipe = sqlQueryService; } /// @@ -26,7 +25,20 @@ public PeopleController(IQueryPipe sqlQueryService) [HttpGet("All")] public async Task GetAll() { - await sqlQuery.Stream("select name, surname, address, town from people for json path, root('data')", Response.Body, @"{""data"":[]"); + await pipe.Stream("select name, surname, address, town from people for json path, root('data')", Response.Body, @"{""data"":[]"); + } + + /// + /// Endpoint that exposes People information using OData protocol. + /// + /// OData response. + // GET api/People/odata + [HttpGet("odata")] + public async Task OData() + { + await this + .ODataHandler(tableSpec, pipe) + .Process(); } /// @@ -38,7 +50,9 @@ public async Task GetAll() [HttpGet] public async Task Get() { - await this.ProcessJQueryDataTablesRequest(tableSpec, sqlQuery); + await this + .JQueryDataTablesHandler(tableSpec, pipe) + .Process(); } } } \ No newline at end of file diff --git a/samples/demos/ivs-people-register/project.json b/samples/demos/ivs-people-register/project.json index 542a0a1908..78f4467029 100644 --- a/samples/demos/ivs-people-register/project.json +++ b/samples/demos/ivs-people-register/project.json @@ -4,8 +4,8 @@ "version": "1.0.0", "type": "platform" }, - "Belgrade.Sql.Client": "0.6.1", - "Microsoft.AspNetCore.Mvc": "1.0.0", + "Belgrade.Sql.Client": "0.7", + "Microsoft.AspNetCore.Mvc": "1.0.1", "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0", "Microsoft.AspNetCore.Server.Kestrel": "1.0.0", "Microsoft.AspNetCore.StaticFiles": "1.1.0", @@ -16,7 +16,7 @@ "Microsoft.Extensions.Logging.Console": "1.0.0", "Microsoft.Extensions.Logging.Debug": "1.0.0", "Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0", - "Sql-Server-Rest-Api": "0.1.4" + "Sql-Server-Rest-Api": "0.2.7" }, "tools": { diff --git a/samples/features/json/azure-function-odata/README.md b/samples/features/json/azure-function-odata/README.md new file mode 100644 index 0000000000..48fe31dcfd --- /dev/null +++ b/samples/features/json/azure-function-odata/README.md @@ -0,0 +1,84 @@ +# Rest API with Azure Functions and Azure SQL Database + +This sample shows how to create REST API using Azure Function that read data from Azure SQL Database using FOR JSON clause. + +## Contents + +[About this sample](#about-this-sample)
+[Before you begin](#before-you-begin)
+[Run this sample](#run-this-sample)
+[Sample details](#sample-details)
+[Related links](#related-links)
+ + + +## About this sample + +- **Applies to:** Azure SQL Database, SQL Server 2016 (or higher) +- **Key features:** FOR JSON clause in SQL Server 2016/Azure SQL Database +- **Programming Language:** C# +- **Authors:** Jovan Popovic + + + +## Before you begin + +To run this sample, you need the be able to create Azure SQL Database and Azure Function. + + + +## Run this sample + +To run this sample, you need to download source code from SQL Server GitHub account, or copy the content of files directly from GitHub using browser. + +### Setup Azure SQL Database + +1. Create Azure SQL Database using Azure Portal, SQL Server Management Studio, or other tools. + +### Setup Azure Function + +1. Create Azure Function using Azure Portal. In the list of templates choose C#/Http Webhook as a type. + +2. Add data-access NuGet package. Click on the **Files** link on the righ-hand side, and upload [project.json[(azure-function/project.json) file into your Azure Function. This file contains a reference to the Data Access library that will be used to get the data from Azure SQL Database. + +3. Setup connection to your database. Click on manage link in Azure Function, and open settings of your Azure Function application. Scroll down to the connection string section, add a key **azure-db-connection** and put the connection string to your dataase as a value. + +4. Modify C# code in your Azure Function (Run.csx file). Put the code in the [run.csx](azure-function/run.csx) file in your Azure Function. + - Modify query in the code to create different REST API. + + + +## Sample + +In this sample is created one Azure Function that is called via URL, calls Azure SQL Database, and returns query result formatted as JSON. This is can be used to implement of REST API using Azure Function on Azure SQL Database. +Azure Function returns response to the caller using HttpResponseMessage class. + +``` +var httpStatus = HttpStatusCode.OK; +string body = + await (new QueryMapper(ConnectionString) + .OnError(ex => { httpStatus = HttpStatusCode.InternalServerError; })) + .GetStringAsync("select * from sys.objects for json path"); + +return new HttpResponseMessage() { Content = new StringContent(body), StatusCode = httpStatus }; +``` + +**QueryMapper** is a class that maps results of SQL Query to some result. In this example, **QueryMapper** uses **GetStringAsync** method to asynchrously execute SQL query and map results to string that will be returned as a result of REST API call. On the **QueryMapper** object is added **OnError** handler that will set *Internal Server Error* code in the response if some error happens during the query execution (this is optional setting). + + + + +## Related Links + +You can find more information about the technologies that are used in this sample on these locations: +- [JSON support in Azure SQL Database](https://docs.microsoft.com/en-us/azure/sql-database/sql-database-json-features). +- [Webhooks in Azure Functions](https://docs.microsoft.com/en-us/azure/azure-functions/functions-create-a-web-hook-or-api-function). + +## Code of Conduct +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. + +## License +These samples and templates are all licensed under the MIT license. See the license.txt file in the root. + +## Questions +Email questions to: [sqlserversamples@microsoft.com](mailto: sqlserversamples@microsoft.com). diff --git a/samples/features/json/azure-function-odata/azure-function/project.json b/samples/features/json/azure-function-odata/azure-function/project.json new file mode 100644 index 0000000000..ec274ba09d --- /dev/null +++ b/samples/features/json/azure-function-odata/azure-function/project.json @@ -0,0 +1,10 @@ +{ + "frameworks": { + "net46":{ + "dependencies": { + "Antlr4.Runtime": "4.5.3", + "Sql-Server-Rest-Api": "0.2.6" + } + } + } +} \ No newline at end of file diff --git a/samples/features/json/azure-function-odata/azure-function/run.csx b/samples/features/json/azure-function-odata/azure-function/run.csx new file mode 100644 index 0000000000..4e46a9b029 --- /dev/null +++ b/samples/features/json/azure-function-odata/azure-function/run.csx @@ -0,0 +1,20 @@ +using Belgrade.SqlClient.SqlDb; +using System.Net; +using System.Configuration; +using SqlServerRestApi; + +public static async Task Run(HttpRequestMessage req, TraceWriter log) +{ + log.Info("Started execution..."); + + try{ + string ConnectionString = ConfigurationManager.ConnectionStrings["azure-db-connection"].ConnectionString; + var sqlQuery = new QueryPipe(ConnectionString); + var tableSpec = new SqlServerRestApi.SQL.TableSpec("sys.objects", "object_id,name,type,schema_id,create_date"); + return await req.CreateODataResponse(tableSpec, sqlQuery); + + } catch (Exception ex) { + log.Error($"C# Http trigger function exception: {ex.Message}"); + return new HttpResponseMessage() { Content = new StringContent(ex.Message), StatusCode = HttpStatusCode.InternalServerError }; + } +} \ No newline at end of file diff --git a/samples/features/sql-bulk-load/load-from-azure-blob-storage/LoadFromAzureBlobStorage.sql b/samples/features/sql-bulk-load/load-from-azure-blob-storage/LoadFromAzureBlobStorage.sql index 4cdc64025a..979743770a 100644 --- a/samples/features/sql-bulk-load/load-from-azure-blob-storage/LoadFromAzureBlobStorage.sql +++ b/samples/features/sql-bulk-load/load-from-azure-blob-storage/LoadFromAzureBlobStorage.sql @@ -6,25 +6,42 @@ ********************************************************************************/ /******************************************************************************** -* SETUP * +* 1. SETUP * ********************************************************************************/ --- Create master key that will encrypt credentials -CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'some strong password'; --- Create credential with Azure Blob SAS -CREATE DATABASE SCOPED CREDENTIAL MyAzureBlobStorageCredential -WITH IDENTITY = 'SHARED ACCESS SIGNATURE', -SECRET = 'sv=2015-12-11&ss=b&srt=sco&sp=rwac&se=2017-02-01T00:55:34Z&st=2016-12-29T16:55:34Z&spr=https&sig=copyFromAzurePortal'; +/******************************************************************************** +* 1.1. OPTIONAL CREDENTIAL SETUP * +* (if data source is not public) * +********************************************************************************/ +-- 1.1.1. (optional) Create master key that will encrypt credentials +-- +-- Required only if you need to setup CREDENTIAL in 1.1.2. +-- CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'some strong password'; + +-- 1.1.2. (optional) Create credential with Azure Blob SAS +-- +-- CREATE DATABASE SCOPED CREDENTIAL MyAzureBlobStorageCredential +-- WITH IDENTITY = 'SHARED ACCESS SIGNATURE', +-- SECRET = 'sv=2015-12-11&ss=b&srt=sco&sp=rwac&se=2017-02-01T00:55:34Z&st=2016-12-29T16:55:34Z&spr=https&sig=copyFromAzurePortal'; +-- NOTE: DO NOT PUT FIRST CHARACTER '?'' IN SECRET!!! + + --- Create external data source with with the roow URL of the Blob storage Account and associated credential. +/******************************************************************************** +* 1.2. REQUIRE DATA SOURCE SETUP * +* (optionally add credential) * +********************************************************************************/ + +-- Create external data source with with the roow URL of the Blob storage Account and associated credential (if it is not public). CREATE EXTERNAL DATA SOURCE MyAzureBlobStorage WITH ( TYPE = BLOB_STORAGE, - LOCATION = 'https://myazureblobstorage.blob.core.windows.net', - CREDENTIAL= MyAzureBlobStorageCredential); + LOCATION = 'https://sqlchoice.blob.core.windows.net/sqlchoice/samples/load-from-azure-blob-storage', +-- CREDENTIAL= MyAzureBlobStorageCredential --> CREDENTIAL is not required if a blob storage is public! +); /******************************************************************************** -* CREATE DESTINATION TABLE (if not exists) * +* 1.3. CREATE DESTINATION TABLE (if not exists) * *********************************************************************************/ DROP TABLE IF EXISTS Product; @@ -43,36 +60,36 @@ CREATE TABLE dbo.Product( GO /******************************************************************************** -* LOAD * +* 2. LOAD * *********************************************************************************/ --- INSERT CSV file into Product table +-- 2.1. INSERT CSV file into Product table BULK INSERT Product -FROM 'data/product.csv' +FROM 'product.csv' WITH ( DATA_SOURCE = 'MyAzureBlobStorage', FORMAT='CSV', CODEPAGE = 65001, --UTF-8 encoding FIRSTROW=2, TABLOCK); --- INSERT file exported using bcp.exe into Product table +-- 2.2. INSERT file exported using bcp.exe into Product table BULK INSERT Product -FROM 'data/product.bcp' +FROM 'product.bcp' WITH ( DATA_SOURCE = 'MyAzureBlobStorage', - FORMATFILE='data/product.fmt', + FORMATFILE='product.fmt', FORMATFILE_DATA_SOURCE = 'MyAzureBlobStorage', TABLOCK); --- Read rows from product.dat file using format file and insert it into Product table +-- 2.3. Read rows from product.dat file using format file and insert it into Product table INSERT INTO Product WITH (TABLOCK) (Name, Color, Price, Size, Quantity, Data, Tags) SELECT Name, Color, Price, Size, Quantity, Data, Tags -FROM OPENROWSET(BULK 'data/product.bcp', +FROM OPENROWSET(BULK 'product.bcp', DATA_SOURCE = 'MyAzureBlobStorage', - FORMATFILE='data/product.fmt', + FORMATFILE='product.fmt', FORMATFILE_DATA_SOURCE = 'MyAzureBlobStorage') as products; --- Query remote file +-- 2.4. Query remote file SELECT Color, count(*) -FROM OPENROWSET(BULK 'data/product.bcp', +FROM OPENROWSET(BULK 'product.bcp', DATA_SOURCE = 'MyAzureBlobStorage', FORMATFILE='data/product.fmt', FORMATFILE_DATA_SOURCE = 'MyAzureBlobStorage') as data