MFF UK - NSWI152 - Cloud Application Development (2018-2021)
⚠️ WARNING This repository is archived. ✅ New courseware: https://github.com/havit/CloudApplicationsDevelopment
Labs for Cloud Applications Development (NSWI152) course @ MFF UK.
Create a super-simple web application in Visual Studio. Follow the instructions below or pick a web project template of your preference:
<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>
Page_Load
method - Set Text
property of the MyLabel
control to some string:
protected void Page_Load(object sender, EventArgs e)
{
MyLabel.Text = "Hello World from Azure";
}
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:
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)
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).
CREATE LOGIN CloudDevLogin WITH PASSWORD = '***new password***'
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
and AppSettings
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="[email protected]" />
</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.
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.)
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.
Default.aspx
page
<asp:FileUpload ID="MyFileUpload" runat="server" />
<asp:Button ID="GoButton" Text="GO!" OnClick="GoButton_Click" runat="server" />
Default.aspx.cs
file
protected 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);
}
}
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.In yout Web Application, add a simple list of files stored in your blob container + simple download action.
<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>
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);
}
https://docs.microsoft.com/en-us/azure/storage/queues/storage-dotnet-how-to-use-queues
Microsoft.Azure.Storage.Queue
NuGet package to both WebApplication project in your solution.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);
}
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...";
}
}
In Microsoft Azure portal create new Service Bus namespace. Recommended pricing tier is Standard (allows to work with Topics)
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.
During LAB 8 you will create your own Logic App workflow. See presentation for more information.
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
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).