Tuesday, February 22, 2022

Azure API Management with Developer Pricing Tier: 504 Gateway Timeout

If you are using Azure API Management with Developer Pricing Tier and it's getting deployed/managed using Azure Resource Manager (ARM) APIs (https://management.azure.com) and your APIs has large schema, you are very likely to get 504 Gateway Timeout errors upon resource management/deployments. 

This is because ARM APIs impose a 2-minute timeout on all operations, and if the API is large, the operation can take more than 2 minutes. Most of the time that's because of SQL bottleneck, as the APIM Developer Pricing Tier uses the Basic SQL Tier which has very limited processing and storage capacity.

Solutions/workarounds,
  • Use your APIM instances' Management API (https://apimname.management.azure-api.net) instead of ARM APIs (https://management.azure.com) to manage your APIM. Direct Management API calls run for as long as needed and are not subject to timeouts.
  • Upgrade APIM instances to larger SKUs. With larger SKU’s we would not have the same issue as those have larger processing and storage capacity.
  • Watch out for what you are returning in your APIs. You might be returning more properties than you really need to. Removing them will reduce the schema size.
As far as I know, Microsoft has plans to make all these ARM operations asynchronous in the near future (no ETA currently), so we are not limited by ARM timeouts.

Hope this will save someone some research time.

Happy Coding.

Regards,
Jaliya

Thursday, February 10, 2022

System.Text.Json.JsonSerializer: Serialize Properties of Derived Classes

Did you know by default JsonSerializer doesn't serialize Properties of Derived Classes?

Say we have the following classes.

public class Person
{
    public string Name { getset; }
}
 
public class Student : Person
{
    public int StudentId { getset; }
}

I am creating a Student, but I am assigning it to a Person.

Person person = new Student
{
    Name = "John Doe",
    StudentId = 1
};

Now if I serialize this using Json.NET,

var options = new Newtonsoft.Json.JsonSerializerSettings
{
    Formatting = Newtonsoft.Json.Formatting.Indented,
};
Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(person, options));

I am getting the following output. The correct underline type is serialized.

{
  "StudentId": 1,
  "Name""John Doe"
}

Now if I serialize the same using JsonSerializer,

var options = new System.Text.Json.JsonSerializerOptions { WriteIndented = true };
Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(person, options));

I am getting the following output. You can see it has only serialized the properties that are in the Parent class.

{
  "Name""John Doe"
}

Now we can change this behavior using one of two ways.

  1. Call an overload of Serialize and ask to use type at run time
  2. Declare the object to be serialized as an object

var options = new System.Text.Json.JsonSerializerOptions { WriteIndented = true };

// Call an overload of Serialize that lets you specify the type at run time:
Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(person, person.GetType(), options));

// Declare the object to be serialized as object
Console.WriteLine(System.Text.Json.JsonSerializer.Serialize<object>(person, options));

And now we can see the correct underline type is getting serialized.

{
  "StudentId": 1,
  "Name""John Doe"
}

Hope this helps.

Happy Coding.

Regards,
Jaliya

Friday, February 4, 2022

Visual Studio 17.1 Preview 5: Streamlined File Scoped Namespacing

This is a quick post on a very nice feature that got introduced with Visual Studio Version 17.1.0 Preview 5.0 (released on February 1, 2022).

I am using .NET 6/C# 10.0 in most of my projects and I have been updating the source files to use File Scoped Namespaces. I have been adding a semicolon at the end of the namespace name and then I had to manually delete unnecessary curly braces. And that was tiring. 

Converting to File Scoped Namespaces

But I no longer have to do that, Hooray! With the Visual Studio Version 17.1.0 Preview 5.0, I can just add the semicolon at the end of the namespace name, and Visual Studio takes care of the rest automatically. I just love this!

If you are planning on migrating your code to use File Scoped Namespaces, I would highly recommend doing so with the latest Visual Studio 2022 Preview, it's going to save you a lot of time.

Happy Coding.

Regards,
Jaliya

Wednesday, February 2, 2022

Azure Automation Runbook: Execute SQL Scripts and Capture Print Statements

In my last post, I wrote about how we can execute SQL Scripts on an Azure SQL Database with Managed Identities using an Azure Automation Runbook. In this post, let's see how we can Capture the Print Statements in the SQL script, so we can output those.

Consider the following Stored Procedure.

CREATE OR ALTER PROC dbo.TestSelectStoredProcedure 
(
    @Param1 INT,
    @Param2 VARCHAR(100) 
)
AS
BEGIN
PRINT CONCAT('Param1: ', @Param1)
PRINT CONCAT('Param2: ', @Param2)
END

I need to execute this script and capture the Print messages, so I can output those. In this post, I am not going to explain how we can execute a SQL script from an Azure Automation Runbook, (for that you can read this post: Azure Automation Runbook: Execute SQL Scripts with Managed Identities).

My Runbook is PowerShell 7.1 (preview). This approach would still work if you are using PowerShell 5.1 Runtime.

Now to capture the messages the SP is printing, we need to register SqlInfoMessageEventHandler to the SqlConnection as follows.

# Reading from variables
$AzureSqlServer = Get-AutomationVariable -Name "AzureSqlServer"
$ApplicationDatabase = Get-AutomationVariable -Name "ApplicationDatabase"
$ClientId = Get-AutomationVariable -Name "AzureSqlManagedIdentityId"
 
# Getting AccessToken for User assigned Managed Identity
# Note: Query Parameter now includes the client_Id
$Resource = "https://database.windows.net/"
$QueryParameter = "?resource=$Resource&client_Id=$ClientId"
$Url = $env:IDENTITY_ENDPOINT + $QueryParameter
$Url = $env:IDENTITY_ENDPOINT + $QueryParameter
$Headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" 
$Headers.Add("X-IDENTITY-HEADER"$env:IDENTITY_HEADER) 
$Headers.Add("Metadata""True") 
$Content =[System.Text.Encoding]::Default.GetString((Invoke-WebRequest `
	-UseBasicParsing `
	-Uri $Url `
	-Method 'GET' `
	-Headers $Headers).RawContentStream.ToArray()) | ConvertFrom-Json 
$AccessToken = $Content.access_token 
 
# PowerShell/ADO.NET
$SqlConnection = New-Object System.Data.SqlClient.SQLConnection  
$SqlConnection.ConnectionString = "Server=$AzureSqlServer;Initial Catalog=$ApplicationDatabase;Connect Timeout=30" 
$SqlConnection.AccessToken = $AccessToken 
$Events = new-object System.Collections.Generic.List[Object]
$Handler = [System.Data.SqlClient.SqlInfoMessageEventHandler] { param($sender, $event) $Events.Add($event) }
$SqlConnection.add_InfoMessage($Handler); 
$SqlConnection.FireInfoMessageEventOnUserErrors = $true;
$SqlConnection.Open();
$SqlCommand = $SqlConnection.CreateCommand()
$SqlCommand.CommandText = "exec dbo.TestStoredProcedure @Param1=100, @Param2='Hello World'"
$SqlCommand.ExecuteNonQuery() | Out-Null
ForEach($event in $Events)
{
    Write-Output $event.Message
}
$SqlConnection.Close();
And now when I test this Runbook, I am seeing the following output as expected.
SQL Script Print Messages Being Outputted
Hope this helps.

Happy Coding.

Regards,
Jaliya