Visual Studio Code: Running a ASP.NET Core and Angular/React/Vue site at the same time in debug mode

There’s a good chance if you are working on the SPA (Angular/React/VUE) and a back end like ASP.NET Core site. How do you get both these to run at the same time in debug mode.

Here’s a easy way.

In your .vscode folder let’s add a launch.json file

Create two nodes.

compounds

configurations

Under Configurations add these and change the folder locations:

	"configurations": [
		{
			"name": ".NET Core Start",
			"type": "coreclr",
			"request": "launch",
			"preLaunchTask": "build",
			"program": "${workspaceFolder}/WebApi/bin/Debug/net8.0/WebApi.dll",
			"args": [],
			"cwd": "${workspaceFolder}/WebApi",
			"stopAtEntry": false,
			"env": {
				"ASPNETCORE_ENVIRONMENT": "Development"
			}
		},
		{
			"name": "Launch Chrome",
			"request": "launch",
			"type": "msedge",
			"webRoot": "${workspaceFolder}/WebApi/ClientApp",
			"sourceMaps": true,
			"url": "https://localhost:5001"
		}
	]

Now, add this for the compounds

		{
			"name": ".Net+Browser",
			"configurations": ["Launch Chrome", ".NET Core Start"]
		}

When you press F5 to run the configuration, you can choose “.Net+Browser”

In the run handler, you’ll both your configurations!

How to get the UPS OAuth Token for UPS REST services in Visual Studio

Connecting to the UPS REST services requires an Auth Token, a tasks that seems to be more difficult to find out how to that it should. This short article will show you how to.

Login to UPS https://developer.ups.com/apps?loc=en_US then tap Applications to see your Apps. Make one if needed. Then get your client_id and client_secret:

You will need these two.

There is a Production and Test URL you will use for both OAuth token maker (below) and Test:

https://onlinetools.ups.com (Production)

https://wwwcie.ups.com (Test)

In the below code, I’ll assume you have a .NET IOptions setup and IMemoryCache

public class UpsOAuthHelper(AppOptions options, IMemoryCache cache){
    public async Task<string> GetToken()
    {
        var cacheKey = "UPS_TOKEN";
        return await cache.GetOrCreateAsync(cacheKey, async entry =>
        {
           if (string.IsNullOrWhiteSpace(options.UPSWebService_ClientId))
               throw new Exception("UPSWebService_ClientId not found");

           if (string.IsNullOrWhiteSpace(options.UPSWebService_ClientSecret))
               throw new Exception("UPSWebService_ClientSecret not found");


           var client = new HttpClient();
           var base64String =
               Convert.ToBase64String(Encoding.ASCII.GetBytes($"{options.UPSWebService_ClientId}:{options.UPSWebService_ClientSecret}"));
            client.DefaultRequestHeaders.Add("Authorization", "Basic " + base64String);

           //What to send to the Backend
           var postData = new List<KeyValuePair<string, string>> { new("grant_type", "client_credentials") };
           var request = await client.PostAsync(options.UPSWebService_BaseURL.TrimEnd('/') + "/security/v1/oauth/token",
                new FormUrlEncodedContent(postData));
           var response = await request.Content.ReadAsStringAsync();
           var responseObject = JsonConvert.DeserializeObject<dynamic>(response);

           //Get the token and expiration
           var accessToken = responseObject?.access_token?.ToString() ?? "";
           int expiresInSeconds = responseObject?.expires_in;

           if (string.IsNullOrWhiteSpace(accessToken))
               throw new Exception("Failed to get UPS access token");

           //How long until the token expires
           //Be conservative in how long 
           entry.AbsoluteExpiration = DateTimeOffset.UtcNow.AddSeconds(expiresInSeconds * .8); 
           return accessToken;
       });
    }
}

Running this code will return a token and will store it in Memory. Call it as often you you need, the IMemoryCache will handle it

VS Code: Setting up a REST endpoint in SalesForce APEX to handle all requests from ASP.NET Core, the easy way

If you are working on SalesForce and using APEX, then you are using VS Code.

In your ASP.NET Core code where your call and talk to SalesForce’s REST endpoint for your APEX code, there are many ways to quickly get lost in too many classes.

In this article I’ll show you how to architect a simple APEX REST endpoint to handle all your REST calls for an object.

In this example, we will be managing a “Cat Site” object

The first step is to Add an APEX class in SalesForce and set up 4 REST endpoints for GET, POST,PUT and DELETE

@RestResource(urlMapping='/CatSites/*')
global with sharing class WD_REST_CATSITES {
@HttpGet
global static void GetController() {}

@HttpPost
global static void PostController() {}

@HttpPut
global static void PutController() { }

@HttpDelete
global static void DeleteController() {}
}

This will accept all the calls to manage the Cat Site object.

Inside each “controller” you will look for the “action” from the param called “action” which you will pass from ASP.NET Core. This will tell the Controller what you want to do. The controller will then run the APEX code for that action.

@HttpGet
global static void GetController() {
RestResponse res = RestContext.response;
try {
String action= RestContext.request.params.get('action')?.toLowerCase();
switch on action{
when 'vendorsitegetmanyofthem' {
res.responseBody = Blob.valueOf(JSON.serialize(Your APEX));
}
when else {
throw new Exception('UnKnown Action: ' + action);
}
}

res.headers.put('Content-Type', 'application/json;charset=UTF-8');
res.statusCode = 200;
} catch (Exception e) {
res.responseBody = Blob.valueOf(e.getMessage());
res.statusCode = 500;
}
}

Now you can pass many different GET actions, and not have to worry about the URL. We use the Blob.valueof() and JSON.serialize() to get the response from your APEX code so we don’t need to know the type.

If your APEX throws an error, we catch it and send back an http 500 message for the ASP.NET Core code to catch.

Repeat this design for all the other http verbs.

ASP.NET Core: Running scheduled jobs the effortless way

Your web applications, or API often needs things to happen in the background, like a scheduled job to send emails out at the end of the day to a manager, or to clean up the Temp table in a database Sunday at 3am

With ASP.NET Core it’s remarkabley easy. I’ll start with the super simple in this first article.

Send an Email once a Day

Install the NuGet package Coravel (it’s got a huge community, so you can rely on it)

dotnet add package coravel

In your StartUp.cs go to the ConfigureServices() and add Coravel like this:

 services.AddScheduler();

Now we are going to tell Coravel to send an email using an email service once a day:

var jobs = app.ApplicationServices;
jobs .UseScheduler(scheduler =>
{
scheduler.Schedule(
() => YourEmailService.EmailReports();
)
.DailyAt(16,0); (//16 is 24 hour time for 4PM
});

Whoa, that’s it? Yep. Coravel will now send the emails once a day at 4pm. If you want to tell your manager you did it manually, I won’t tell on you.

Coravel has a built in mailer, but for this example I wanted to keep it simple.

VS Code and Kendo Grids: How to optimize the Kendo Grid for small and large loads

When using Kendo, you will most likely be using Visual Studio Code and a framework like Angular or React. Kendo Grid lets you optimize it for small or large work loads. Small workloads use a simpler setup, while larger ones understandably use and off more complex options.

Simple Setup for small data loads.

If you have a couple hundred records, it’s not work the effort to configure the Kendo Grid for complex paging. Let’s set it up for a simple binding:

<kendo-grid
[kendoGridBinding]="myArray"
[pageable]="true"
[pageSize]="5">
<kendo-grid-column field="BahaiInstitution"></kendo-grid-column>
</kendo-grid>

The myArray will be a simple array of data. but if you are getting data from the backend via an RxJS Observable, thy this

Angular:

@if (queryResult$ | async; as model) {
<kendo-grid size="small" [kendoGridBinding]="model"

The Obserable queryResult$ get subscribed to and put into the variable “model“. Bind it to kendoGridBinding and you’re all set.

More complex setup for larger data loads

Instead of using the kendoGridBinding we use the data option

        <kendo-grid
[data]="gridView"
[pageable]="pageableSettings"
[pageSize]="pageSize"
[skip]="skip"
(pageChange)="pageChange($event)"
>
With these values in the component:
public pageSize = 25;
public pageableSettings: PagerSettings = {
buttonCount: 5,
info: true,
type: "numeric",
pageSizes: [25, 50, 100, 250],
previousNext: true,
position: "bottom",
};
public skip = 0;

In your Angular Component you will need to create the gridView, which is a paged view of the data .

Step 1: Get the data using your backend and perhaps an Observable:

this.queryResult$ = this.myService.get.pipe(
tap((items) => {
this.gridData = items;
this.loadItemsIntoKendoGrid();
})
);

You’ll need a little herlp function to get the gridView

/** Takes the grid data and updates the grid view */
public loadItemsIntoKendoGrid(): void {
const sliced = this.gridData?.slice(this.skip, this.skip + this.pageSize);

this.gridView = { data: orderBy(sliced, this.sort),
total: this.gridData?.length,};
}

Now you’ll need a little method to handle the paging requests

/**  Paging */
public pageChange(event: PageChangeEvent): void {
this.skip = event?.skip;
this.pageSize = event?.take;
this.loadItemsIntoKendoGrid();
}

Problems and Solutions when installing GitHub Copilot and GitHub Copilot Chat in VS Code

Run, don’t walk, to your nearest computer and install GitHub Copilot into your Visual Studio Code.

Ok, enough of the hype. Sometimes problems occur so I’ve put together a few things you can try if things don’t work when you install this amazing tool.

Fix 1: Logout of VS Code

Click the Account then choose your GitHub account and sign out

Now restart VS Code

Sign back in.

Fix 2: Check account status

Open the OUTPUT tab in VS Code, then choose GitHub Copilot and see if there are any obvious messages, like those about account not being active/paid. You might need to renew your credit card.

Fix 3: Remove Extention, reboot, add back

This fix is a lot like “did your restart your computer”, but it works really wel..
Logout (see Fix 2) then remove the GitHub Copilot extenstion

Restart VS Code

Add it back

Login

Returning the results from a series of Tasks run in Batches using C# and .Net (dotnet)

This article will review a straightforward way to return all the results from several Tasks which are run in batches. Batching your tasks will help manage resources on your servers so it is always a clever idea.

In your project, first install the batching NuGet package morelinq.

dotnet add package morelinq --version 4.1.0

With this package Batching is made much easier.

In our example I’ll be using a custom Cat object

class Cat

{
  public string Name { get; set; }
  public string Breed { get; set; }
}

We first create several tasks:

// Create a list of tasks that return Cat objects
List<Task<Cat>> tasks = new List<Task<Cat>>()
{
  Task.Run(() => GetCat("Task 1")),
  Task.Run(() => GetCat("Task 2")),
  Task.Run(() => GetCat("Task 3"))
....and so on
};

Then we batch the calls, 5 at a time:

int batchSize=5;
foreach (var batch in tasks.Batch(batchSize))
  await Task.WhenAll(batch.ToArray());

Next, we can pull all the Cat results out of the completed tasks:

// Retrieve the Cat objects from each task

List<Cat> cats = tasks.Select(task => task.Result).ToList();

Done!

VS Code and Typescript: Using ‘as const’ to pass in values

The as const keyword is under rated. It has some really nice features though to help you convince the Typescript compiler to take your values.

Let’s create a settings class with some values:

const settings = {

colour: 'blue',
country: 'canada',
language: 'english'
}

Next, let’s create a function that is restricted to only a small subset of languages.

function gotoLanguagePage(language: 'english'|'french'|'spanish'){


}

If we now try and call it, we’ll get an error because Typescript can’t guarantee that the values will match the function signature:

To force Typescript to take the language value we can change the settings so that it’s “as const”

const settings = {

colour: 'blue',
country: 'canada',
language: 'neglish'
} as const;

Now when we call the function it’ll work nicely:

gotoLanguagePage(settings.language)

Visual Studio Code: Typescript Enums – Should I stop using them?

Enums aren’t “real”, they don’t exist in Javascript. They are Typescript only entities.

Here’s a sample enum in typescript:

enum LoginType {

PHONE,
EMAIL,
}

console.log(LoginType.PHONE); //0
console.log(LoginType.EMAIL); //1

Typescript will automatically assign number in order. But what about is you add a new item to the list at the top? The number values change!

enum LoginType {

CARD ,
    PHONE,
    EMAIL,
}

console.log(LoginType.PHONE); //1
console.log(LoginType.EMAIL); //2

Oops, that’s not good. You can of course force Typescript to assign values, which will help, but if you forget, the simple act of adding a new value to the enum could be disasterous.

enum LoginType {

CARD=2 ,
    PHONE=0,
    EMAIL=1,
}

But hey, doesn’t this break what Typescript is good at…Types?

Let’s say we have a function like this:

function validateLoginType(choice:LoginType){

......
}

These are all valid to Typescript, and not so great to read:

validateLoginType(LoginType.CARD); 

validateLoginType(1);

Alternatives:

type LoginType = 'CARD' | 'PHONE' | 'EMAIL'

You can then pass in a simple string, which is readable, and Typescript will validate it

validateLoginType('CARD')

Nice intellisence!

High Impact .NET Community results

2023 was a momentous year for my Microsoft MVP activities. Here’s some of my highlights as of January 2024:

My Microsoft MVP Blog https://ianvink.wordpress.com hit 70,000 in 2023, a dramatic jump in users and traffic. The years I have put into the blog and the community it has built has been richly paid off. 2024 I hope to do even better.

Month on month my viewers have continued to grow, with February coming along nicely:

I have continued to maintain my StackOverflow presence for 14 years now. Wow. I’m now in the top 0.28% worldwide: