Category Archives: VS Code

How to share SQL query result to C# on Polyglot Notebook?

Polyglot Notebook has an interesting feature that can share SQL query results directly from SQL to C#, this makes it useful as a scratchpad. The example code is below.

Cell 1 – Add NuGet packages for Polyglot, TabularDataResource, and DataFrame

#r "nuget: Microsoft.DotNet.Interactive.SqlServer, *-*"
#r "nuget: Microsoft.Data.Analysis"

Cell 2 – Connect to SQL Server and WideWorldImporters sample DB

#!connect mssql --kernel-name myDb1 "Server=(localdb)\LocalDB2025;Database=WideWorldImporters;Trusted_Connection=True;TrustServerCertificate=True;"

Cell 3 – Run the queries and set to customerData variable

#!sql-myDb1 --name customersData
SELECT TOP 3 *
FROM Sales.Customers WHERE CustomerName LIKE '%Toys%'

SELECT TOP 3 *
FROM Sales.Customers WHERE CustomerName NOT LIKE '%Toys%'

Cell 4 – Share to C# customerData

#!share --from sql-myDb1 customersData

foreach (var data in customersData)
{
    "Table".Display();
    foreach (IEnumerable<KeyValuePair<string, object>> row in data.Data)
    {
        // Row is a list instead of dictionary, need to search if want to display by column ID, e.g.
        // row.FirstOrDefault(x => x.Key == "CustomerID").Value.Display();
        
        foreach (KeyValuePair<string, object> field in row)
        {
            Console.Write($"{field.Key}: {field.Value} | ");
        }
        Console.WriteLine();
    }
}

Cell 5 – Another example of sharing customersData2 variable from SQL to C#

#!sql-myDb1 --name customersData2
SELECT TOP 3 CustomerID, CustomerName, BillToCustomerID, CustomerCategoryID, BuyingGroupID, PrimaryContactPersonID, AlternateContactPersonID, DeliveryMethodID, DeliveryCityID, PostalCityID, CreditLimit, CAST(AccountOpenedDate AS NVARCHAR(20)) AccountOpenedDate
FROM Sales.Customers WHERE CustomerName LIKE '%Toys%'

#!C#
#!share --from sql-myDb1 customersData2
var df = customersData2[0].ToDataFrame();
df.Display();

// If want to convert to DataTable
// df.ToTable().Display();

foreach (Microsoft.Data.Analysis.DataFrameRow row in df.Rows)
{
    // Access by column Name
    // row["CustomerID"].Display();
    
    for (int i = 0; i < df.Columns.Count; i++)
    {
        Console.Write($"{df.Columns[i].Name}: {row[i]} | ");
    }
    Console.WriteLine();
}

Note: The AccountOpenedDate column needs to be cast to nvarchar due to an error (Error: System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values. at Microsoft.DotNet.Interactive.Formatting.TabularData.TabularDataResourceExtensions.ToDataFrame(TabularDataResource tabularDataResource)) in ToDataFrame(), so a workaround has been used.

C# Jupyter Notebook on VS Code Polyglot Notebook Not Support Debugging – A Workaround

Visual Studio Code Polyglot Notebook is a C# version of Python Jupyter Notebook that provides interactive environment to let developers write, run code and markdown notes with visualization. While Python Jupyter Notebook can do debugging, but C# Polyglot Notebook is unable to do the debugging; this hinders some people from using the notebook. Some already logged the debugging feature request on the .NET Interactive GitHub issue https://github.com/dotnet/interactive/issues/2099.

Are there any workarounds for the debugging if we really need to debug some problem occasionally? If you are on Windows and have installed Visual Studio, you might want to consider temporarily using Visual Studio as the C# Polyglot Notebook debugger. It uses the JIT debugger on Windows and technically should work (not proven though) on Linux too since it requires more configurations and without Visual Studio, so Linux JIT debugger is not covered here.

To debug C# Polyglot Notebook, you need to add the System.Diagnostics.Debugger.Launch(); to launch the JIT debugger, and if you have more than one cell you want to debug, add System.Diagnostics.Debugger.Break();.

Click Run All button or run the cell; when you hit the Launch method, JIT Debugger Dialog will be pop-up, you need to choose Visual Studio, then F5 again, it will go to first breakpoint, and F5 once more to go to second one. Refer to the screenshots below, and Happy Debugging!

Ubuntu Hang if running Visual Studio Code with Heavy Workload

Are you running some debugging or unit testing on VS Code inside Ubuntu but found Ubuntu hanging for a long time without response?
If you are running VS Code on Ubuntu with remote tunnel or SSH, the remote session even disconnected and is unable to connect back?
If one of above is your scenario, you might try to enable the swap file on Ubuntu if it hasn’t been turned on. Your Ubuntu system might not have enough memory to run your heavy memory workload. To check whether your Ubuntu has turned on the swap file or not, run the command below:
sudo swapon --show
If it returns nothing, your system is not yet enabled for the swap file.
To enable the swap file, just follow the steps on the link below for Ubuntu 22.04.
https://www.digitalocean.com/community/tutorials/how-to-add-swap-space-on-ubuntu-22-04.

How to debug .NET app in VS Code that required to set environment path?

If you are trying to debug the .NET app in VS Code that requires setting environment variables, and without the environment variables, the debugging just can’t work, you might find that it is not a way to temporarily set them other than set them permanently on the OS environment path or code them into .NET code. Those are some of the ways to solve the issue, but what if you just want to temporarily set the environment variables for that debugging session only?

One of the workaround is to set the environment path before you launch the VS Code using terminal. For example, on Linux is below:

cd INTO_YOUR_PROJECT_PATH
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/PATH_TO_YOUR_CUSTOM_LD_LIBRARY_PATH
code .

PS: Windows will be similar, just set the environment path in your Terminal before you launch the VS Code.

Post Data to InfluxDB 2.x API using C Programming on Ubuntu / RPI

InfluxDB 2.4 doesn’t provide any official C language client library for data manipulation, but since InfluxDB has built-in Rest-API, we can use the API with libcurl by Curl in Linux e.g. Ubuntu or Raspberry Pi to write data to InfluxDB.

First, we need to install the libcurl if you haven’t installed it.

sudo apt update
sudo apt install libcurl4-gnutls-dev

After that, we create a file with a name such as influxdbpost.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
//arm-linux-gnueabihf/curl/curl.h if 32-bit RPI
#include <x86_64-linux-gnu/curl/curl.h>
 
int main(void)
{
  CURL *curl;
  CURLcode res;
  struct curl_slist *headers= NULL;

  float aq; 
  float temperature; 
  float humidity; 
  float pressure;

  // Sample data, replace with the data you want to write to InfluxDB
  char *data = malloc(4096);
  char *tempStr = malloc(1024);
  strcpy(data, "airSensors,sensor_id=iot1 ");
  sprintf(tempStr, "air_quality=%.2f,", aq);
  strcat(data, tempStr);
  sprintf(tempStr, "temperature=%.2f,", temperature);
  strcat(data, tempStr);
  sprintf(tempStr, "humidity=%.2f,", humidity);
  strcat(data, tempStr);
  sprintf(tempStr, "pressure=%.2f", pressure / 100);
  strcat(data, tempStr);
  sprintf(tempStr, " %d", (int)time(NULL));
  strcat(data, tempStr);

  //fprintf(stdout, "%s\n", data);
 
  curl = curl_easy_init();
  if(curl) 
  {
    // Replace localhost:8086, org, and bucket with your InfluxDB Server URL, Organization and Bucket.
    curl_easy_setopt(curl, CURLOPT_URL, "http://localhost:8086/api/v2/write?org=test-org&bucket=test-bucket&precision=s");
    // Replace the API key
    char *token_header = "Authorization: Token YOUR_INFLUXDB_API_KEY";
    headers = curl_slist_append(headers, token_header);
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
    headers = curl_slist_append(headers, "Accept: application/json");
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
    headers = curl_slist_append(headers, "Content-Type: text/plain; charset=utf-8");
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

    /* Get size of the POST data */
    curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long) strlen(data));
 
    /* Pass in a pointer of data - libcurl will not copy */
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
 
    /* Perform the request, res will get the return code */
    res = curl_easy_perform(curl);
    /* Check for errors */
    if(res != CURLE_OK)
    {
      fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
    }
 
    /* always cleanup */
    curl_easy_cleanup(curl);
  }
  return 0;
}

The libcurl header file is in /usr/include, you can browse the directory to check and confirm where the curl.h located.

To compile the c code, we need to use the command below:

cc influxdbpost.c -lcurl -o influxdbpost

Please note the -lcurl is the libcurl cflag that you need to include for it to compile correctly.

After it has compiled, you can run it by calling ./influxdb. After executed, then you can check the result in InfluxDB already.

Lastly, for those using VS Code as IDE, you can debug the file by adding something like the below to .vscode/tasks.json.

{
    "tasks": [
        {
            "type": "cppbuild",
            "label": "C/C++: gcc-11 build active file",
            "command": "/usr/bin/gcc-11",
            "args": [
                "-fdiagnostics-color=always",
                "-g",
                "${file}",
                "-lcurl",
                "-o",
                "${fileDirname}/${fileBasenameNoExtension}"
            ],
            "options": {
                "cwd": "${fileDirname}"
            },
            "problemMatcher": [
                "$gcc"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "detail": "Task generated by Debugger."
        }
    ],
    "version": "2.0.0"
}

Reference:
https://curl.se/libcurl/c/simplepost.html

Installing .NET 7 and Visual Studio Code on Raspberry Pi 4 with AutoComplete/Intellisense for C#

First at all, to install .NET 7 and Visual Studio Code with C# Intellisense or code auto complete feature via C# extension, you need to have Raspberry Pi 4 in 64-bit version.

If you just have Raspberry Pi in 32-bit version, you still can install .NET 7 and Visual Studio Code but without C# code Intellisense feature. Below are some of the features of C# extension:

You will get an error if you try to use C# Extension (Omnisharp) in 32-bit Raspberry Pi like below:

So, the first thing you need to confirm is whether your Raspberry Pi is the 32-bit or 64-bit version. Please run the command below to check the version.

uname -a 
 
# Linux raspberrypi 5.10.103-v8+ #1529 SMP PREEMPT Tue Mar 8 12:26:46 GMT 2022 aarch64 GNU/Linux
# Linux raspberrypi 5.10.17-v7l+ #1421 SMP Thu May 27 14:00:13 BST 2021 armv7l GNU/Linux

If the result is aarch64 GNU/Linux then you are using 64-bit OS, meanwhile armv7l GNU/Linux will be 32-bit OS. After confirmed the Raspberry Pi 32/64 bit version, let us head to Microsoft .NET website to download .NET 7 (https://dotnet.microsoft.com/en-us/download/dotnet/7.0). Please note as per current date, .NET 7 is in preview version.

We just need to download the SDK only if we want to build apps using .NET, the runtimes are already included inside the SDK. Arm32 is for 32-bit OS and Arm64 is for 64-bit OS, please download the correct version for yourself. You can download by clicking the link or you can use the wget command to download. E.g. If you download the .NET 7 preview 7 Arm64 binary.

wget https://download.visualstudio.microsoft.com/download/pr/261a4c75-3058-4319-98b7-050c1c12f8e8/46d3da56919fb74ef4e1eccdfa24e4e8/dotnet-sdk-7.0.100-preview.7.22377.5-linux-arm64.tar.gz

After the download is completed, extract the downloaded file to $HOME/dotnet folder and set using export command so you able to execute the dotnet command from current session Terminal.

mkdir -p $HOME/dotnet && tar zxf dotnet-sdk-7.0.100-preview.7.22377.5-linux-arm64.tar.gz -C $HOME/dotnet
export DOTNET_ROOT=$HOME/dotnet
export PATH=$PATH:$HOME/dotnet

If you want to permanently add the export command to the shell profile, so you don’t need to re-key in the export every time you log in Raspberry Pi or open a new Terminal, then you need to copy the commands to .bashrc.

# Run nano editor to edit the .bashrc
nano ~/.bashrc

# Copy the commands below to the .bashrc
export DOTNET_ROOT=$HOME/dotnet
export PATH=$PATH:$HOME/dotnet

# Run this command so the terminal session will use the new settings
source ~/.bashrc

After all setup has been done, run dotnet --info to verify your installation, you should get something like below:

Then, we can proceed to install Visual Studio Code. Please run the commands below to install it.

sudo apt update
sudo apt install code

After you have installed VS (Visual Studio) Code, let us try to create a Hello World console app in C# and open it with VS Code, please run the command below:

dotnet new console -o HelloWorld.NET && code ./HelloWorld.NET

Once you open up the VS Code, it will ask if you want to install C# extension or not, just click Install. If you miss out, you can install it from the Extension there. Please note, this only works on 64-bit of Raspberry Pi.

Try type Console. in the Program.cs file, you will able to see the intellisense will popup. This feature is very convenient for everyone specially beginner.

Besides that, you can Generate C# Assets for Build and Debug. After generated, you will able to debug the C# program which you able to see how the program execute and run during runtime.

To debug, first place a debug breakpoint (red circle) on left of line number 2, then hit the Play button (green triangle) and it will start the debugging progress.

Below is the debugging process, it will stop on your breakpoint (red circle) with yellow highlight. Besides that, you can see many other debugging info like variables value, call stack and etc.

That all for the installation, Happy .NET-ing :).

PS: If you are using 32-bit of Raspberry Pi 4, you can try to get another new SD card and install a new 64-bit Raspberry Pi. It available on Raspberry Pi Imager > Operating System > CHOOSE OS > Raspberry Pi OS (other) > Raspberry PI OS (64-bit).

VS Code Remote .NET Development with Debugging in Ubuntu from Windows using SSH

Do you ever know we can do a remote .NET development and debugging using VS Code in Ubutun from Windows? It is just similar to what we can do our .NET development in WSL. Basically, what you require is just an SSH enabled Ubuntu and VS Code Remote – SSH extension on your Windows.

First thing first, what the pre-requisite is an SSH server on Ubuntu, if you haven’t setup, you can try google around to install or check this post > Raspberry Pi & Ubuntu SSH Login Without Password from Windows.

After that, open your VS Code on Windows and install the Remote – SSH published by Microsoft.


Reopen the VS Code, you should be able to see a new Remote Explorer Icon on the left (1), click it and change the drop-down list to SSH Targets (2) if not yet selected. Then, click the SSH TARGETS on (3), you will be able to see an Add button as (4). As (5), key in your SSH information e.g. ssh userid@192.168.0.2. Follow the instructions and this will be registered the Ubuntu SSH to the VS Code.


After that, reopen the VS Code, and go the Remote Explorer again, you will able to see the Ubuntu IP address. Right click and connect it.


VS Code will restart and you can go to Explorer and click Open Folder. The path listed will be Ubuntu folders.

If you have an existing .NET project in Ubuntu, try to open it and add the assets for VS Code. After added, you will be able to develop .NET with IntelliSense and debug the .NET project on Ubuntu.

If you don’t have the .NET project on Ubuntu, you can open the VS Code terminal, it will be the Bash for Ubuntu. You can run dotnet new to create a new .NET project on Ubuntu and do the remote development and debug on Windows.

FAQ
Q: You received this error message during you launch the project for debug > The terminal process failed to launch: Path to shell executable “dotnet” is not a file or a symlink.
A: Please make sure the Ubuntu dotnet path is correct. You can specify the full path in .vscode/tasks.json.

{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "build",
            "command": "dotnet",
            "type": "process",
            "args": [
                "build",
                "${workspaceFolder}/razorapptest.csproj",
                "/property:GenerateFullPaths=true",
                "/consoleloggerparameters:NoSummary"
            ],
            "problemMatcher": "$msCompile"
        },

Migrating IE ActiveXObject XMLHTTP XMLDOM to Chrome

Internet Explorer lifeline is set to unplug by Microsoft starting from June 15, 2022, so it already entering the countdown stage and everyone should need to full force and rushing to migrate legacy IE-only web applications to Chrome?

Credit: Pixabay

Actually, Microsoft still will provide backward compatibility for IE until at least 2029 inside the Edge browser via Microsoft Edge with IE mode. Detail information about the timeline can be found here > https://docs.microsoft.com/en-us/lifecycle/faq/internet-explorer-microsoft-edge

Although it still has some years to go before the actual unplug, it is no reason for us to continue procrastination about the migration. Today let us take a look at how to migrate some of the common use IE ActiveXObject Microsoft.XMLHTTP and Microsoft.XMLDOM to Chrome/Firefox…browser.

To simulate the Web API, let use .NET 6 Web API template project WeatherForecaset as our API. Please run the command below to create a new Web API project.

dotnet new webapi -o webapi

To enable XML format for that API, we need to AddXmlSerializerFormatters to the controller. We disabled the https redirection to make the test project simple and then add allowed server static files from the webserver.

using Microsoft.OpenApi.Models;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers().AddXmlSerializerFormatters();
builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new() { Title = "webapi", Version = "v1" });
});

var app = builder.Build();

// Configure the HTTP request pipeline.
if (builder.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
    app.UseSwagger();
    app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "webapi v1"));
}

//app.UseHttpsRedirection();

app.UseAuthorization();

app.UseDefaultFiles();
app.UseStaticFiles();

app.MapControllers();

app.Run();

For Javascript, the ActiveXObject Microsoft.XMLHTTP and Microsoft.XMLDOM actually have their equivalent object in Chrome liked browser, let have some of the comparisons on the table below. Noted: the Chrome supported code below are built-in code, you can consider using other libraries like jQuery too.

IE supported code Chrome supported code
xhr = new ActiveXObject(“Microsoft.XMLHTTP”); xhr = new XMLHttpRequest();
var domDoc = new ActiveXObject(“Microsoft.XMLDOM”); domDoc = new DOMParser().parseFromString(xhr.responseText, “text/xml”);
var node = root.selectSingleNode (“/ArrayOfWeatherForecast/WeatherForecast/Summary”); No equivalent, but it can code manually. Refer to funcSelectSingleNode.
var nodes = root.selectNodes(“WeatherForecast”); var nodes = root.getElementsByTagName(“WeatherForecast”);
element.text element.innerHTML
nodes[i].selectNodes(“Date”)[0].childNodes[0].text nodes[i].getElementsByTagName(“Date”)[0].childNodes[0].nodeValue
<!DOCTYPE html>
<html>
    <head>
        <title>XML - IE vs Chrome</title>
        <style>
            table,
            th,
            td {
                border: 1px solid black;
                border-collapse: collapse;
                padding: 5px;
            }
        </style>
    </head>
    <body>
        <div id="browser"></div>
        <div id="xsd"></div>
        <table id="result"></table>
        <div id="xml"></div>
        <script>
            var xhr;
            if (!window.document.documentMode) {
                xhr = new XMLHttpRequest();
                document.getElementById("browser").innerText = "Chrome";
            } else {
                xhr = new ActiveXObject("Microsoft.XMLHTTP");
                document.getElementById("browser").innerText = "IE";
            }
            xhr.onreadystatechange = function () {
                if (xhr.readyState == XMLHttpRequest.DONE) {
                    console.log(xhr.responseText);
                    if (window.document.documentMode) {
                        var domDoc = new ActiveXObject("Microsoft.XMLDOM");
                        domDoc.loadXML(xhr.responseText);

                        funcIE(domDoc);
                    }
                    else {
                        domDoc = new DOMParser().parseFromString(xhr.responseText, "text/xml");

                        funcChrome(domDoc);
                    }
                }
            }
            xhr.open('GET', './WeatherForecast?random=' + Math.random(), true);
            xhr.setRequestHeader("Accept", "application/xml")
            xhr.send(null);

            function funcIE(xmlDoc) {
                var root = xmlDoc.documentElement;

                var xsd = root.getAttribute("xmlns:xsd");
                document.getElementById("xsd").innerText = xsd;

                var node = root.selectSingleNode("/ArrayOfWeatherForecast/WeatherForecast/Summary");
                node.text = node.text + "+";

                var firstNode = root.selectSingleNode("WeatherForecast");
                var newNode = firstNode.cloneNode(true);
                root.appendChild(newNode);

                var newElem = xmlDoc.createElement("WeatherForecast");
                var tempElem = xmlDoc.createElement("Date");
                tempElem.text = new Date().toISOString();
                newElem.appendChild(tempElem);
                tempElem = xmlDoc.createElement("TemperatureC");
                tempElem.text = "32";
                newElem.appendChild(tempElem);
                tempElem = xmlDoc.createElement("Summary");
                tempElem.text = "Hot";
                newElem.appendChild(tempElem);
                root.appendChild(newElem);

                var n = root.selectSingleNode('WeatherForecast[2]');
                root.removeChild(n);

                var table = "<tr><th>Date</th><th>TemperatureC</th><th>Summary</th></tr>";
                var nodes = root.selectNodes("WeatherForecast");
                for (var i = 0; i < nodes.length; i++) {
                    table += "<tr><td>" +
                        nodes[i].selectNodes("Date")[0].childNodes[0].text +
                        "</td><td>" +
                        nodes[i].selectNodes("TemperatureC")[0].childNodes[0].nodeValue +
                        "</td><td>" +
                        nodes[i].selectNodes("Summary")[0].childNodes[0].nodeValue +
                        "</td></tr>";
                }
                document.getElementById("result").innerHTML = table;
                document.getElementById("xml").innerText = xmlDoc.xml;
            }

            function funcChrome(xmlDoc) {
                var root = xmlDoc.documentElement;

                var xsd = root.getAttribute("xmlns:xsd");
                document.getElementById("xsd").innerText = xsd;

                var node2 = funcSelectSingleNode(xmlDoc, "/ArrayOfWeatherForecast/WeatherForecast/Summary");
                node2.childNodes[0].nodeValue = node2.childNodes[0].nodeValue + "+";

                var firstNode = root.getElementsByTagName("WeatherForecast");
                var newNode = firstNode[0].cloneNode(true);
                root.appendChild(newNode);

                var newElem = xmlDoc.createElement("WeatherForecast");
                var tempElem = xmlDoc.createElement("Date");
                tempElem.innerHTML = new Date().toISOString();
                newElem.appendChild(tempElem);
                tempElem = xmlDoc.createElement("TemperatureC");
                tempElem.innerHTML = "32";
                newElem.appendChild(tempElem);
                tempElem = xmlDoc.createElement("Summary");
                tempElem.innerHTML = "Hot";
                newElem.appendChild(tempElem);
                root.appendChild(newElem);

                var n = root.getElementsByTagName('WeatherForecast')[2];
                root.removeChild(n);

                var table = "<tr><th>Date</th><th>TemperatureC</th><th>Summary</th></tr>";
                var nodes = root.getElementsByTagName("WeatherForecast");
                for (var i = 0; i < nodes.length; i++) {
                    table += "<tr><td>" +
                        nodes[i].getElementsByTagName("Date")[0].childNodes[0].nodeValue +
                        "</td><td>" +
                        nodes[i].getElementsByTagName("TemperatureC")[0].childNodes[0].nodeValue +
                        "</td><td>" +
                        nodes[i].getElementsByTagName("Summary")[0].childNodes[0].nodeValue +
                        "</td></tr>";
                }
                document.getElementById("result").innerHTML = table;
                document.getElementById("xml").innerText = xmlDoc.documentElement.outerHTML;
            }

            function funcSelectSingleNode(xmlDoc, elementPath) {   
                if (document.implementation && document.implementation.createDocument) {         
                    var nodes = document.evaluate(elementPath, xmlDoc, null, XPathResult.ANY_TYPE, null);
                    return nodes.iterateNext();
                }
            }
        </script>
    </body>
</html>

Below are the results:

GitHub Source Code:
https://github.com/sanme98/Migrating_ActiveXObject_XMLHTTP_XMLDOM

Reference:
https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ms757828(v=vs.85)
https://forums.asp.net/t/1227814.aspx?xml+SelectNodes+not+working+in+firefox+in+javascript+
https://blog.darkthread.net/blog/migrate-xml-data-island/

Different ways to consume/call JSON Web API in .NET

Did you know there have different ways to consume a JSON Web API in .NET? In this post, it will show different methods to call an API.

1) Using WebClient

string GetApiByWebClient(string byQuery, string byHeader)
{
    using WebClient webClient = new WebClient();
    if (byHeader.Length > 0)
    {
        webClient.Headers.Add("name", byHeader);
    }
    webClient.Headers.Add("Accept", "application/json");
    webClient.Headers.Add("Content-Type", "application/json");
    return webClient.DownloadString(myApiUrl + (byQuery.Length == 0 ? "" : "?name=" + byQuery));
}

string PostApiByWebClient(string data)
{
    using WebClient webClient = new WebClient();
    webClient.Headers.Add("Accept", "application/json");
    webClient.Headers.Add("Content-Type", "application/json");
    return webClient.UploadString(myApiUrl, WebRequestMethods.Http.Post, data);
}

2) Using HttpWebRequest

string PostApiByHttpWebRequest(string data)
{
    HttpWebRequest webClient = (HttpWebRequest)WebRequest.Create(myApiUrl);
    webClient.Accept = "application/json";
    webClient.ContentType = "application/json";
    webClient.Method = WebRequestMethods.Http.Post;
    webClient.ContentLength = data.Length;
    using (var writer = new System.IO.StreamWriter(webClient.GetRequestStream()))
    {
        writer.Write(data);
    }
    var response = webClient.GetResponse() as HttpWebResponse;
    using (var streamReader = new StreamReader(response.GetResponseStream()))
    {
        return streamReader.ReadToEnd();
    }
}

3) Using HttpClient

async Task<string> PostApiByHttpClientAsync(string data)
{
    string result = "";

    using (var httpClient = new HttpClient())
    {
        httpClient.DefaultRequestHeaders.Add("Accept", "application/json");

        HttpContent httpContent = new StringContent(data);
        httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");

        using (HttpResponseMessage response = await httpClient.PostAsync(myApiUrl, httpContent))
        {
            if (response.Content != null && response.IsSuccessStatusCode)
            {
                result = await response.Content.ReadAsStringAsync();
            }
            else
            {
                if (response.Content != null)
                {
                    result = await response.Content.ReadAsStringAsync();
                    string message = "POST Response Fail : " + result;
                    throw new Exception(message);
                }
                else
                {
                    string message = "POST Response Fail : No response";
                    throw new Exception(message);
                }
            }
        }
    }

    return result;
}

Apparently, the third method is longer, but as you can see from the code, it is an asynchronous method, which means it is an efficient approach towards activities blocked or access is delayed e.g. Web API. Therefore, it is a recommended method if responsive is important for the application you want to develop.

Bonus:
4) Using Powershell
Lastly, how about after you deployed to an environment and it is not working? You not sure is it due to a firewall or network issue and you have no tools like POSTMAN….? Then, PowerShell can come to help.

PS D:\Temp\dotnetcore\ConsumeAPI> Invoke-WebRequest -Uri http://localhost:7071/api/HttpExample `
>>     -ContentType 'application/json' `
>>     -Method POST `
>>     -Body '{name: "POST API request from PowerShell"}'


StatusCode        : 200
StatusDescription : OK
Content           : Hello, POST API request from PowerShell.
RawContent        : HTTP/1.1 200 OK
                    Transfer-Encoding: chunked
                    Content-Type: text/plain; charset=utf-8
                    Date: Sat, 14 Aug 2021 09:16:38 GMT
                    Server: Kestrel

                    Hello, POST API request from PowerShell.
Forms             : {}
Headers           : {[Transfer-Encoding, chunked], [Content-Type, text/plain; charset=utf-8], [Date, Sat, 14 Aug 2021
                    09:16:38 GMT], [Server, Kestrel]}
Images            : {}
InputFields       : {}
Links             : {}
ParsedHtml        : mshtml.HTMLDocumentClass
RawContentLength  : 40

.NET Interactive as C#/F#/PowerShell scratchpad/jupyter in VS Code

Are you still using a Visual Studio Console Application to write some simple testing code for C#/F#? Are you still looking for some alternative that is similar to Python Jupyter Notebook in .NET world? Are you still looking for a scratchpad application to write some simple .NET code? Are you want to learn and try .NET programming in a simple way but don’t have a simple learning tool?

Now you don’t need to continue the search for the alternative, .NET Interactive is the one you need. It is exactly similar to Python Jupyter Notebook and you can install it in Visual Studio Code.

Getting Started

  1. Install the latest Visual Studio Code.
  2. Install the latest .NET 5 SDK
  3. Install the .NET Interactive Notebooks extension from the marketplace.

After you have installed it, to start it, just follow the steps below:

  1. Press Ctrl+Shift+P to open the Command Palette.
  2. Select .NET Interactive: Create new blank notebook.
  3. Create as ‘.dib’ if you prefer the latest notebook format.
  4. Choose your preferred programming language from C#/F#/PowerShell.
  5. And you are done. Just start your coding :).

As you can see from the screenshot above, it supports IntelliSense (autocomplete) though it does not support debugging now.

FAQ:
Q: How to add reference for a library?
A: You can use something like below:

#r "System.Data.SqlClient"
#r "nuget:System.Text.Json,4.7.2"

Q: How about using keyword? Supported?
A: Yes.

using System.Net;

Q: How to execute the code?
A: Press either Ctrl+Alt+Enter or Alt+Enter (this will add a new cell below too).