⚠️ WARNING This repository is archived.
âś… New courseware: https://github.com/havit/CloudApplicationsDevelopment
Labs for Cloud Applications Development (NSWI152) course @ MFF UK.
- Robert Haken & Jiřà Kanda = this repo
- Ondřej Václavek - https://github.com/vaclavek/CloudDevelopment
-
Create a super-simple web application in Visual Studio. Follow the instructions below or pick a web project template of your preference:
- Create new Visual Studio project using ASP.NET Web Application (.NET Framework) template (as Empty web application with Web Forms core references).
- Add a new item to the root folder of the project - Web Form - Default.aspx
- In Default.aspx - Add
<asp:Label ID="MyLabel" runat="server" />
inside the<form>
element.<body> <form id="form1" runat="server"> <div> <asp:Label ID="MyLabel" runat="server" /> </div> </form> </body>
- In Default.aspx.cs
Page_Load
method - SetText
property of theMyLabel
control to some string:protected void Page_Load(object sender, EventArgs e) { MyLabel.Text = "Hello World from Azure"; }
- (Start the web app locally to check the result -
Ctrl
+F5
)
-
Publish the WebSite to Azure AppService
-
Change anything and re-publish the result to Azure.
-
Push the solution source code to your GitHub account.
You might want to simplify your website by removing unnecessary configuration which can cause issues when building the app. See my GitHub commit.
-
Setup an automatic deployment from GitHub to Azure AppService:
- Create new Azure AppService using Azure Portal (it's called Web App in Create New Resource panel). Use the App Service Plan created in LAB1.
- Go to Deployment Center blade and set up your GitHub repository as deployment source (note other deployment sources available - VSTS, OneDrive, Dropbox, git, ...)
- Check the result - the application should be deployed within few seconds.
- Change anything in your website and push the commit to GitHub - the application should be redeployed within few seconds.
In this lab we will create and deploy a WebJob (background task) which connects to Azure SQL database. The job will run in scheduled time intervals, read data from a EmailQueue table and send e-mail via SendGrid mail service (in future lab we might move the queue from Azure SQL to Azure Storage Queues).
-
Create new Azure SQL server and database:
-
Go to Azure Portal and use Create Resource button in left panel to create a new SQL Database.
-
Fill in the name and other properties of the DB + create a new SQL server to host the DB
-
-
In Visual Studio open new SQL Query window for the new DB
Note: In practice you usualy use Microsoft SQL Server Management Studio or Microsoft SQL Server Operations Studio to perform DB-related development/management tasks. In this lab we will use plain Visual Studio to demonstrate an alternate option.
-
In Visual Studio open Cloud Explorer panel (you can use Quick Launch) and navigate to your database - SQL Databases / Open SQL Server Object Explorer (r-click)
- Connect to your Azure SQL server
- Confirm adding of your current IP address to Azure SQL Server firewall (You can check firewall rules using Azure Portal - Go to you SQL Server and see Firewall / Network Settings section)
-
In SQL Server Object Explorer navigate to your DB and open New query... (r-click)
-
-
Create the EmailQueue table in your DB:
CREATE TABLE dbo.EmailQueue ( ID int PRIMARY KEY NOT NULL IDENTITY (1, 1), Recipient nvarchar(MAX) NOT NULL, Subject nvarchar(400) NOT NULL, Body nvarchar(MAX) NOT NULL, Created datetime NOT NULL, Sent datetime NULL )
-
[OPTIONAL] Create new SQL login account for the application not to use system administrator account
Note: In real scenario you don't want your applications to use the system administrator account to access the DB. Dedicated login account for each client should be created (with restricted access rights).
- In master DB run the following SQL query
CREATE LOGIN CloudDevLogin WITH PASSWORD = '***new password***'
- In your application DB run following SQL query
CREATE USER CloudDevUser FOR LOGIN CloudDevLogin WITH DEFAULT_SCHEMA = dbo GO EXEC sp_addrolemember N'db_owner', N'CloudDevUser' GO
-
Add new Console Application (.NET Framework) project to the solution.
-
Add following code to Program.cs
Main
method (application entry-point):static void Main(string[] args) { while (true) { Console.WriteLine("Checking for new e-mails to be sent..."); using (var conn = new SqlConnection(ConfigurationManager.ConnectionStrings["MainDatabase"].ConnectionString)) { conn.Open(); var cmd = new SqlCommand("SELECT * FROM EmailQueue WHERE Sent IS NULL", conn); using (var reader = cmd.ExecuteReader()) { while (reader.Read()) { Console.WriteLine($"Email ID:{reader["ID"]} found..."); using (var smtpClient = new SmtpClient()) { smtpClient.Host = ConfigurationManager.AppSettings["SmtpHost"]; smtpClient.Credentials = new NetworkCredential(ConfigurationManager.AppSettings["SmtpUsername"], ConfigurationManager.AppSettings["SmtpPassword"]); smtpClient.Send( from: ConfigurationManager.AppSettings["SmtpFrom"], recipients: reader["Recipient"].ToString(), subject: reader["Subject"].ToString(), body: reader["Body"].ToString()); // TODO: Write EmqilQueue.Sent to DB Console.WriteLine($"Email ID:{reader["ID"]} sent..."); } } } } Thread.Sleep(30_000); // 30sec } }
-
Add corresponding
ConnectionStrings
andAppSettings
sections to App.Config file.Note: In real scenario you would set the values to development settings. These will be overriden by Azure Portal Application Settings (see bellow).
<connectionStrings> <!-- Will be replaced with Azure Portal Application Settings --> <add name="MainDatabase" connectionString="Data Source=sql.development.local;Initial Catalog=CloudDev;User Id=development;Password=development;Application Name=CloudDevWebJob"/> </connectionStrings> <appSettings> <!-- Will be replaced with Azure Portal Application Settings --> <add key="SmtpHost" value="mail.development.local" /> <add key="SmtpUsername" value="" /> <add key="SmtpPassword" value="" /> <add key="SmtpFrom" value="haken@devmail.havit.cz" /> </appSettings>
-
Publish the result as Azure Web Job...
Note: The job will be failing now. We have to set the production Application Settings.
-
[OPTIONAL] Create a new SendGrid account in Azure and use it's credentials to send mails.
-
Go to Azure Portal, locate the App Service hosting the WebJob and set appropriate Application Settings (ConnectionStrings and AppSettings):
-
Try the job by adding a row to EmailQueue table.
- In Azure Portal create a new Application Insights service associated to the App Service created in LAB1.
-
You can easily create one through navigating to your App Service - Application Insights blade:
-
Check Application Settings section - there is a new
APPINSIGHTS_INSTRUMENTATIONKEY
setting added automatically (If not, create one on your own - you can find the instrumentation key in Properties section of the Application Insights service.)
-
- Install Application Install
Microsoft.ApplicationInsights.Web
NuGet Package to the Web Application project created in LAB1 and publish the project to Azure.
https://docs.microsoft.com/en-us/azure/storage/blobs/storage-quickstart-blobs-dotnet?tabs=windows
-
In Azure Portal create a new Azure Storage Account - StorageV2 (general purpose v2). Familiarize yourself with the configuration options available:
-
Add new container to the Blob service section of the storage account created:
-
Install
Microsoft.Azure.Storage.Blob
NuGet package to both WebApplication and WebJob projects in your solution. -
In your Web Application, add a simple form which uploads a file to the blob container you created earlier.
- Add FileUpload control and a Button to your
Default.aspx
page<asp:FileUpload ID="MyFileUpload" runat="server" /> <asp:Button ID="GoButton" Text="GO!" OnClick="GoButton_Click" runat="server" />
- Add button-click handler to the
Default.aspx.cs
fileprotected void GoButton_Click(object sender, EventArgs e) { if (MyFileUpload.HasFile) { var storageAccount = CloudStorageAccount.Parse(ConfigurationManager.ConnectionStrings["StorageAccountConnectionString"].ConnectionString); var blobClient = storageAccount.CreateCloudBlobClient(); var containerReference = blobClient.GetContainerReference("test"); // name of your container var blobReference = containerReference.GetBlockBlobReference(MyFileUpload.FileName); blobReference.UploadFromStream(MyFileUpload.FileContent); } }
- Add
StorageAccountConnectionString
to your web.config file (you will find the connection string on Azure Portal in Access Keys section of the Storage Account blade). For production deployment you can use the Application Settings section of App Service to set the value. - You can test the upload now. Try to use Azure Storage Explorer to verify file existence.
- Add FileUpload control and a Button to your
-
In yout Web Application, add a simple list of files stored in your blob container + simple download action.
- Add following code snippet to your Default.aspx file:
<h1>Files</h1> <asp:Repeater ID="FilesRepeater" ItemType="Microsoft.Azure.Storage.Blob.IListBlobItem" runat="server"> <ItemTemplate> <asp:LinkButton ID="FileLink" CommandArgument="<%# Item.Uri %>" Text="<%# Item.Uri %>" OnCommand="FileLink_Command" runat="server" /><br /> </ItemTemplate> </asp:Repeater>
- Add following code snippet to your Default.aspx.cs file:
protected override void OnPreRender(EventArgs e) { var storageAccount = CloudStorageAccount.Parse(ConfigurationManager.ConnectionStrings["StorageAccountConnectionString"].ConnectionString); var blobClient = storageAccount.CreateCloudBlobClient(); var containerReference = blobClient.GetContainerReference("test"); var blobs = containerReference.ListBlobs(); FilesRepeater.DataSource = blobs; FilesRepeater.DataBind(); } protected void FileLink_Command(object sender, CommandEventArgs e) { var storageAccount = CloudStorageAccount.Parse(ConfigurationManager.ConnectionStrings["StorageAccountConnectionString"].ConnectionString); var blobClient = storageAccount.CreateCloudBlobClient(); var blobReference = new CloudBlockBlob(new Uri((string)e.CommandArgument), blobClient); blobReference.DownloadToStream(Response.OutputStream); }
- Add following code snippet to your Default.aspx file:
https://docs.microsoft.com/en-us/azure/storage/queues/storage-dotnet-how-to-use-queues
- Create a new queue within the storage account created in LAB4.
- Install
Microsoft.Azure.Storage.Queue
NuGet package to both WebApplication project in your solution. - Send Message
Default.aspx
<asp:TextBox ID="QueueMessageTB" runat="server" /> <asp:Button ID="SendToQueueButton" Text="Send to Queue" OnClick="SendToQueueButton_Click" runat="server" />
Default.aspx.cs
protected void SendToQueueButton_Click(object sender, EventArgs e) { var storageAccount = CloudStorageAccount.Parse(ConfigurationManager.ConnectionStrings["StorageAccountConnectionString"].ConnectionString); var queueClient = storageAccount.CreateCloudQueueClient(); var queueReference = queueClient.GetQueueReference("test"); // your queue name var message = new CloudQueueMessage(QueueMessageTB.Text); queueReference.AddMessage(message); }
- Retrieve Message
Default.aspx
<asp:Label ID="QueueMessageLb" runat="server" /> <asp:Button ID="GetMessageButton" Text="Get from Queue" OnClick="GetMessageButton_Click" runat="server" />
Default.aspx.cs
protected void GetMessageButton_Click(object sender, EventArgs e) { var storageAccount = CloudStorageAccount.Parse(ConfigurationManager.ConnectionStrings["StorageAccountConnectionString"].ConnectionString); var queueClient = storageAccount.CreateCloudQueueClient(); var queueReference = queueClient.GetQueueReference("test"); // your queue name var message = queueReference.GetMessage(); if (message != null) { QueueMessageLb.Text = message.AsString; queueReference.DeleteMessage(message); } else { QueueMessageLb.Text = "No message returned..."; } }
- Official documentation: https://azure.microsoft.com/en-us/services/service-bus/
- Presentation (PDF)
In Microsoft Azure portal create new Service Bus namespace. Recommended pricing tier is Standard (allows to work with Topics)
- How to create Queue and Send/Receive messages in .NET
- How to create Topic and Publish/Subscribe messages in .NET
- Official documentation: https://docs.microsoft.com/en-us/azure/redis-cache/
- Recommended reading and video: https://www.miroslavholec.cz/blog/uvod-do-redis-a-prakticke-navrhove-vzory--wug-praha-2017
- Presentation (PDF)
In Microsoft Azure portal create new Redis service. Recommended pricing tier is Basic and for education reasons I would recommend to allow port 6379 (non SSL).
Download Redis Server installer from https://github.com/MicrosoftArchive/redis/releases and install with default settings.
Pull changes from this repository to your computer. You should see new Redis solution folder with RedisCache project. It is a simple console application.
- Implement Redis client libraries for .NET
- Implement caching (get and set operations)
- Implement counter for each user request
- Official documentation (Azure Functions): Azure Functions documentation
- Official documentation (Azure Logic Apps): Azure Logic Apps documentation
- Recommended reading: Serverless Computing video + resources in CZ
- Presentation (PDF)
During LAB 8 you will create your own Logic App workflow. See presentation for more information.
- Official documentation (Azure Search): Azure Search documentation
- Presentation (PDF)
In Microsoft Azure portal create new Azure Search service. You can choose Free pricing tier for our purposes. Provisioning of your service can take up to 15 minutes.
You should see new solution folder AzureSearch. There is a new WinForm project in this solution folder. Try to build it.
Step 1: Add new NuGet package Microsoft.Azure.Search to your project
Step 2: Implement ButtonIndex_Click event
using (var serviceClient = new SearchServiceClient("name", new SearchCredentials("key")))
{
var actions = new IndexAction<Article>[]
{
IndexAction.MergeOrUpload(article)
};
var batch = IndexBatch.New(actions);
ISearchIndexClient indexClient = serviceClient.Indexes.GetClient("articles");
indexClient.Documents.Index(batch);
}
Step 3: Create new Index in Azure Search
- visit portal.azure.com and find your Azure Search service
- choose Indexes item and click on the Add index button
- define your Index (do not forget use all fields from Article class)
Step 4: Implement ButtonSearch_Click event
using (var indexClient = new SearchIndexClient("name", "index", new SearchCredentials("key")))
{
var parameters = new SearchParameters()
{
// Select = new[] { "" }
};
var results = indexClient.Documents.Search<Article>(Keyword.Text, parameters);
ResultGrid.DataSource = results.Results.Select(x => new
{
Score = x.Score,
Title = x.Document.Title,
Category = x.Document.Category,
Text = x.Document.Text
}).OrderByDescending(x => x.Score).ToList();
}
Scoring profiles: Create new default scoring profile so that Title will have higher priority in search results than other fields.
Scoring functions: Extend your scoring profile with scoring functions. Lets say we want to prioritize newest items (use Created field).