December 31, 2023

Microsoft.Data.SqlClient.SqlException: The certificate chain was issued by an authority that is not trusted

I faced this error while using the Entity Framework Core with .Net 6.0,

Microsoft.Data.SqlClient.SqlException: 'A connection was successfully established 
with the server, but then an error occurred during the login process. 
(provider: SSL Provider, error: 0 - The certificate chain was issued by an authority
that is not trusted.)'

Breaking change in Microsoft.Data.SqlClient 4.0.0.

I found there is breaking change in Microsoft.Data.SqlClient - 4.0.0 .

Changed Encrypt connection string property to be true by default:
The default value of the Encrypt connection setting has been changed from false to true. 
With the growing use of cloud databases and the need to ensure those connections are secure, 
it's time for this backwards-compatibility-breaking change.

Ensure connections fail when encryption is required:
In scenarios where client encryption libraries were disabled or unavailable, 
it was possible for unencrypted connections to be made when Encrypt was set to true
or the server required encryption.

Solution

The quick-fix is to add Encrypt=False to your connection-strings.

Alongwith Encrypt=False, setting Trusted_Connection=True would also help.

Another workaround if you are using local computer is to set

TrustServerCertificate=True

Please note that, setting TrustServerCertificate=True is not a real fix. The more authentic part of the solution is installing a CA signed certificate.

References:

December 26, 2023

Serialize with System.Text.Json.JsonSerializer

The System.Text.Json namespace provides functionality for serializing to and deserializing from JavaScript Object Notation (JSON). When serializing C# objects to json, by default all public properties are serialized.

Lets say we have this User class object we want to serialize:

 class User
 {
     public int Id { get; set; }
     public string Name { get; set; }
     public string? Email { get; set; }
     public string Password { get; set; }
 }

var obj = new User
 {
     Id = 1,
     Name = "Idrees",
     Email = null,
     Password = "123"
 };

If we serialize this object:

string txtJson = Json.JsonSerializer.Serialize(obj);

We will get the this json:

{"Id":1,"Name":"Idrees","Email":null,"Password":"123"}

If we want to skip the Password property, we can use the attribute [JsonIgnore] on that property.

After this change the User class will look like this:

 class User
 {
     public int Id { get; set; }
     public string Name { get; set; }
     public string Email { get; set; }
     [JsonIgnore]
     public string Password { get; set; }
 }

Serializing now will give you this result:

{"Id":1,"Name":"Idrees","Email":null}

Notice that the Password property is no longer serialized.

You can also specify the condition for exclusion by setting the [JsonIgnore] attribute's Condition property.

In above example, we have set the null value for Email property and it is showing null in serialized json text.

To exclude that property we can specify the condition to ignore when it has null value. After this change the User class will become like this:

 class User
 {
     public int Id { get; set; }
     public string Name { get; set; }
     [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
     public string? Email { get; set; }
     [JsonIgnore]
     public string Password { get; set; }
 }

The serialization will remove the Email property and generate this json.

{"Id":1,"Name":"Idrees"}

References:

Repeat a String in C#

C# do not have built-in function, as of C# 12.0, to repeat a string for x number of times.

Lets say we have a string variable:

	string text = "-"; //the string we need to repeat
	int n = 5; // number-of-times to repeat

Here are some ways you can use to repeat a string value:

Using Enumerable.Repeat and string.Concat:

	string result1 = string.Concat(Enumerable.Repeat(text, n));

Using StringBuilder:

	string result2 = new StringBuilder().Insert(0, text, n).ToString();

Using Enumerable.Range, Select with string.Concat

	var repeatedStrings = Enumerable.Range(0, n).Select(i => text);
	string result3 = string.Concat(repeatedStrings);

Using String() constructor

If the required string is single character, you can also use String constructor.

	string result4 = new String('-', n);

November 28, 2023

C# Caller Argument Expression Attribute

The System.Runtime.CompilerServices.CallerArgumentExpressionAttribute enables you to receive the expression passed as an argument. It captures the expression passed for another parameter as a string.

This would be helpful specially in diagnostic libraries which need to provide more details about the expressions passed to arguments. By providing the expression that triggered the diagnostic, in addition to the parameter name, developers have more details about the condition that triggered the diagnostic.

Lets say we have this method to log the method name.

public static void LogMethodName(string name, 
                 [CallerArgumentExpression("name")] string? message = null)
{
   if (string.IsNullOrEmpty(name))
   {
      //we are printing the 'message' which represents the actual expression 
      //being passed for parameter 'name'
      throw new ArgumentException($"Argument validation: <{message}>", name);
   }

   Console.WriteLine($"Method {name} is called.");
}

Here we are calling above method:

public static void RegisterUser()
{
    LogMethodName(nameof(RegisterUser));
}

In RegisterUser method, we are calling LogMethodName with the value as name of RegisterUser. The expression used for name parameter, is injected by the compiler into the message argument.

In this example, the expression passed for the name parameter is nameof(RegisterUser), so the value of message parameter in LogMethodName is "nameof(RegisterUser)"

References:

C# documentation comments - para and cref

C# documentation comments use XML elements to define the structure of the output documentation. It helps to write the information about code and makes it more readable and understandable.

<para> tag

The <para> tag can be used inside a tag, such as <summary>, <remarks>, or <returns>. It allows you to add structure to the text.

Here the <para> is used to add another line (paragraph) in the summary section.

/// <summary>
/// This property will keep the type of project.
///     <para>
///         The Type of project defined in the document.
///     </para>
/// </summary>
public string ProjectType { get; set; }

It will display this information on mouse hover like this:

cref attribute

The cref attribute in an XML documentation tag means "code reference." It specifies that the inner text of the tag is a code element, such as a type, method, or property.

Here the cref attribute is referencing another class which contains constant string values.

/// <summary>
///     <para>
///         Use <see cref="MyNamespace.ProjectStatus"/> class.
///     </para>
/// </summary>
public string Status { get; set; }

On mouse hover it will display like this:

Note that the ProjectStatus will appear as link, and will take you to the definition of class when clicked.

References:

October 29, 2023

Kendo UI Grid - Nested child grids

To add hierarchy or nested child grids in Kendo UI Grid component, we can use the detailInit event for the parent grid and initialize the next level of hierarchy.

Lets say we have the following function which will return dummy data based on ParentAccountID.

private getGridData(parentAccountID: string) {
	var data = [
		{
			"RowLabel": "Initial Balance",
			"AccountID": "1",
			"AccountNo": 1,
			"AccountName": "Account1",
			"AccountTitle": "1 - Account1",
			"ParentAccountID": null,
		},
		{
			"RowLabel": "Initial Balance",
			"AccountID": "2",
			"AccountNo": 2,
			"AccountName": "Account2",
			"AccountTitle": "2 - Account2",
			"ParentAccountID": null,
		},
		{
			"RowLabel": "Initial Balance",
			"AccountID": "3",
			"AccountNo": 3,
			"AccountName": "Account3",
			"AccountTitle": "3 - Account3",
			"ParentAccountID": "1",
		},
		{
			"RowLabel": "Initial Balance",
			"AccountID": "4",
			"AccountNo": 4,
			"AccountName": "Account4",
			"AccountTitle": "4 - Account4",
			"ParentAccountID": "2",
		}
	];

	var tempData = data;
	tempData = tempData.filter((obj) => {
		return obj.ParentAccountID === parentAccountID;
	});
		
	return tempData;
}

Here is how we can define the grid configuration to call above function to get data for ParentAccountID.

private createDataInquiryGrid3() {

	var tempData = this.getGridData(null);//Pass null as ParentAccountID for 1st level data.
	
	var element = $("#grid").kendoGrid({
		dataSource: {
			data: tempData,
		},
		height: 600,
		sortable: false,
		pageable: false,
		detailInit: this.detailInit,//this event will define child grid configuration
		dataBound: function () {
			this.expandRow(this.tbody.find("tr.k-master-row").first());
		},
		columns: [
			{ field: "AccountTitle", title: " ", aggregates: ["count"] },
			{ field: "AccountNo", title: "AccountNo" },
			{ field: "AccountName", title: "AccountName" },
		]
	});
}

private detailInit(e) {
	//e.data contains the current row's data-object.
	var getDataFunction = window["getGridData"];
	var tempData = getDataFunction(e.data.AccountID);
	
	$("<div/>").appendTo(e.detailCell).kendoGrid({
		dataSource: {
			data: tempData,
		},
		scrollable: false,
		sortable: false,
		pageable: false,
		columns: [
			{ field: "AccountTitle", title: " ", aggregates: ["count"] },
			{ field: "AccountNo", title: "AccountNo" },
			{ field: "AccountName", title: "AccountName" },
		]
	});
}

It will display the grids like this:

With detailInit event you can add multiple level of nested grids using the same event in nested grids.

References:

Related Post(s):

October 28, 2023

Kendo UI Grid - Column Template

Kendo UI grid will display the column data in table format. If we need to customize the display value for specific column we can use the template function. It allows us to define the template which renders the actual content for that column.

columns array can be defined like this:

columns: [
    { field: "AccountTitle", title: "AccountTitle"},
    { field: "TransactionType", title: "Type" },
    { field: "DatePosting", title: "Date", format: "{0:dd/MM/yyyy}" },
	{
		field: "Status",
		template: function (dataRow) {
			var linkHtml = "";
			if (dataRow.StatusNo == '1') {
				linkHtml = "<span style='color:green;'>Active</span>";
			}
			else {
				linkHtml = "<span style='color:red;'>Deactive</span<";
			}
			return linkHtml;
		},
		title: "Status"
	},
]

Note that Status column, we have defined a template function, which will accept the dataRow as parameter and return the final html as string, which the Kendo Grid will display as cell's content.

References:

Related Post(s):

September 30, 2023

Enable Tag Helper using @tagHelperPrefix

The @tagHelperPrefix directive allows you to specify a tag prefix string to enable Tag Helper support and to make Tag Helper usage explicit.

For example, you could add the following markup to the Views/_ViewImports.cshtml file:

@tagHelperPrefix cst:

This allows you to enable Tag Helper support by using this prefix (cst). Only those elements using the prefix cst, support the Tag Helpers (Tag Helper-enabled elements have a distinctive font/color).

In the code image below, the <label> element have the Tag Helper prefix (cst), so the Tag Helper is enabled for this element. While the <input> element doesn't have Tag Helper prefix (cst), so Tag Helper is not enabled for this element, this is just a regular HTML element.

The same hierarchy rules that apply to @addTagHelper also apply to @tagHelperPrefix.

References:

Related Post(s):

Tag Helper scope with _ViewImports file

  • The _ViewImports.cshtml can be added to any view folder, and the view engine applies the directives from both that file and the Views/_ViewImports.cshtml file.
  • The _ViewImports.cshtml file is additive, If you add an empty Views/Home/_ViewImports.cshtml file for the Home views, there would be no change because it will import all tag helpers from Views/_ViewImports.cshtml file.
  • If you add new tag helpers in Views/Home/_ViewImports.cshtml file for the Home views, it will import all tag helpers from Views/_ViewImports.cshtml file, also it will import all tag helpers from Views/Home/_ViewImports.cshtml file.
  • Any new @addTagHelper directives you add to the Views/Home/_ViewImports.cshtml file (that are not in the default Views/_ViewImports.cshtml file) would expose those Tag Helpers to views only in the Home folder.

References:

Related Post(s):

August 28, 2023

Remove Tag Helpers with @removeTagHelper

Remove Tag Helpers with @removeTagHelper

The @removeTagHelper removes a Tag Helper that was previously added by @addTagHelper.

It has the same two parameters as @addTagHelper:

@removeTagHelper *, MyApplicationTagHelpers

For example, when you want to restrict a specific Tag Helper on a particular view, you can apply the @removeTagHelper to remove the specified Tag Helper.

Using @removeTagHelper in a child folder's _ViewImports.cshtml file (e.g. Views/Folder/_ViewImports.cshtml), it removes the specified Tag Helper from all of the views in that Folder.

Opting out of individual Tags

Instead of removing the Tag Helper from a View, you can also disable a Tag Helper at the element level with the Tag Helper opt-out character ("!").

For example, we can disable the Email validation in the <span> with the Tag Helper opt-out character:

<!span asp-validation-for="Email" class="text-danger"></!span>

In this case, the Tag Helper will be disabled on a single element (<span> in this case), rather than all elements in a View.

The Tag Helper opt-out character must be applied to the both opening and closing tags.

References:

Related Post(s):

Custom Tag Helpers in ASP.NET Core

Tag Helper allows you to add or modify HTML elements from server-side code. In Razor markup, the Tag Helper looks like standard HTML elements. It provides more productive syntax than manually writing the C# Razor markup.

Tag Helpers enable server-side code to participate in creating and rendering HTML elements in Razor files.

(Microsoft Docs - Tag Helpers in ASP.NET Core)

To create a custom Tag Helper, you need to inherit from built-in TagHelper class. For example we have this Tag Helper,

public class StringTagHelper : TagHelper
{
   public override void Process(TagHelperContext context, TagHelperOutput output)
   {
      output.TagName = "span";
      output.TagMode = TagMode.StartTagAndEndTag;

      output.Attributes.Add("data-entity", "customer");

      base.Process(context, output);
   }
}

This Tag Helper will output the span element and add a custom attribute data-entity with value customer.

In the Razor markup, we can use this Tag Helper as:

<string field="CustomerID"></string>

Ofcourse we can add any extra/custom attributes to html tags, these will not interrupt with Tag Helpers.

The browser will render the final output as:

<span field="CustomerID" data-entity="customer"></span>

To use the Tag Helpers in the views, you need to set its scope, usually we define the scope in Views/_ViewImports.cshtml file.

Tag Helpers scope is controlled by a combination of @addTagHelper, @removeTagHelper, and the "!" opt-out character.

To make the required TagHelper avialable for one or more views we use @addTagHelper.

If you create a new ASP.NET Core web app named MyApplicationTagHelpers, the following Views/_ViewImports.cshtml file will be added to your project:

@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, MyApplicationTagHelpers

Some points for above code:

  • The code above uses the wildcard syntax ("*") to specify that all Tag Helpers in the specified assembly (Microsoft.AspNetCore.Mvc.TagHelpers) will be available to every view file in the Views directory or subdirectory.
  • The first parameter after @addTagHelper specifies the Tag Helpers to load ("*" is used to load all Tag Helpers)
  • The second parameter "Microsoft.AspNetCore.Mvc.TagHelpers" specifies the source assembly from which we want to load the Tag Helpers. It can be current project's assemly or any reference to external library.

References:

Related Post(s):

July 24, 2023

MSB3037 - NSwag- openapi2csclient exited with code -1

I was working with Visual Studio 2022 (Version 17.6.5). The project has consumed the nuget package for NSwag.ApiDescription.Client (version 13.19.0). It was working fine.

When I moved the project to another machine, it starts generating this error on Rebuild:

The command ""C:\Users\x\source\repos\G21-V5-Web\packages_rep\nswag.msbuild\13.0.5\build\
../tools/Win/NSwag.exe" 
openapi2csclient /className:ServiceClient /namespace:MyService 
/input:C:\Users\x\source\repos\G21-V5-Web\swagger.json /output:obj\swaggerClient.cs " 
exited with code -1.

I found out the root cause in my case was the whitespaces in the path to the project.

We need to fix in the file:

C:\Users\x\source\repos\G21-V5-Web\packages_rep\nswag.apidescription.client\13.0.5
\build\NSwag.ApiDescription.Client.targets

You will find line # 21 similar to this:

    <Command>%(Command) /input:%(FullPath) /output:%(OutputPath) %(Options)</Command>

To make swagger work with whitespaces, change this line by adding double quotes around the path:

    <Command>%(Command) /input:"%(FullPath)" /output:"%(OutputPath)" %(Options)</Command>

My project works fine after this fix.

Compile TypeScript code with ASP.NET WebSite Project

In previous posts we have seen how to configure TypeScript code with ASP.NET Core Project and Web Application Project . In this post we will configure the TypeScript in an ASP.NET WebSite Project (.Net Framework).

Suppose we already have an ASP.Net WebSite Project.

Inside Scripts folder, Create a new TypeScript file, say app.ts.

Add the folowing code:

sayHello(name: string) {
	console.log("Hello " + name);
}

As we changed some configurations for TypeScript compiler in the last post, we will do the same here. Select Add New Item, and choose TypeScript Configuration File and use the default name of tsconfig.json. Replace the content in tsconfig.json file with following.

{
  "compilerOptions": {
    "noImplicitAny": false,
    "noEmitOnError": true,
    "removeComments": false,
    "sourceMap": false,
    "target": "ES2015",
    "allowJs": false,
    "inlineSourceMap": true,
    "sourceRoot": "./",
    "outDir": "Scripts",
    "inlineSources": true,
    "lib": [ "es2015", "es2016", "dom", "es2018.promise" ]
  },
  "exclude": [
    "./Scripts/JS"
  ],
  "include": [
    "Scripts/*.ts"
  ],
  "compileOnSave": true
}

The include section above instructs the compiler to compile all typescritps files inside Scripts folder. It will exlude the files for compilation which will be palced inside Scripts/JS folder as mentioned in exclude section above. It also tells the compiler to copy the output js files in Scripts folder (mentioned by outDir key). We need to use the same path when referencing the js file in HTML.

<script src="Scripts/app.js"></script>

Save the changes, and reload the project.

Now we have all setup with TypeScript, we can write TypeScript code and it should work.

Related Post(s):

June 22, 2023

Compile TypeScript code with ASP.NET Web Applicaton Project

In the last post we have seen how to configure TypeScript code with ASP.NET Core Project. In this post we will configure the TypeScript in an ASP.NET Web Applicaton Project (.Net Framework).

Suppose we already have an ASP.Net Web Application Project.

Inside Scripts folder, Create a new TypeScript file, say app.ts.

Add the folowing code:

sayHello(name: string) {
	console.log("Hello " + name);
}

As we changed some configurations for TypeScript compiler in the last post, we will do the same here. Select Add New Item, and choose TypeScript Configuration File and use the default name of tsconfig.json. Replace the content in tsconfig.json file with following.

{
  "compilerOptions": {
    "noImplicitAny": false,
    "noEmitOnError": true,
    "removeComments": false,
    "sourceMap": false,
    "target": "ES2015",
    "allowJs": false,
    "inlineSourceMap": true,
    "sourceRoot": "./",
    "outDir": "Scripts",
    "inlineSources": true,
    "lib": [ "es2015", "es2016", "dom", "es2018.promise" ]
  },
  "exclude": [
    "./Scripts/JS"
  ],
  "compileOnSave": true
}

It will exlude the files for compilation which will be palced inside Scripts/JS folder as mentioned in exclude section above. It also tells the compiler to copy the output js files in Scripts folder (mentioned by outDir key). We need to use the same path when referencing the js file in HTML.

<script src="Scripts/app.js"></script>

Sometimes you might also need to manually add a TypeScript compiler task to your website project. Edit [YourProjectName].csproj file, and add the following <Target> element before the </Project> closing tag:

<Target Name="TypeScriptCompile" BeforeTargets="Build">
  <Exec Command="tsc" />
</Target>

Save the changes, and reload the project.

Now we have all setup with TypeScript, we can write TypeScript code and it should work.

Related Post(s):

June 18, 2023

Compile TypeScript code with ASP.NET Core

In this post I will explain the steps we need to setup TypeScript into an ASP.NET Core project.

Lets suppose we already have an ASP.Net Core Project.

We need to install Nuget Package Microsoft.TypeScript.MSBuild to build typescript code/files.

Create a new file named app.ts.

Add the folowing code:

sayHello(name: string) {
	console.log("Hello " + name);
}

We need to tell TypeScript by configuration settings to direct the behavior for compilation. Select Add New Item, and choose TypeScript Configuration File and use the default name of tsconfig.json. Replace the content in tsconfig.json file with following.

{
  "compileOnSave": true,
  "compilerOptions": {
    "noImplicitAny": false,
    "noEmitOnError": true,
    "removeComments": false,
    "sourceMap": true,
    "target": "es5",
    "outDir": "wwwroot/ts_build"
  },
  "exclude": [
    "./node_modules",
    "./wwwroot",
  ],
  "include": [
    "./TypeScripts"
  ]
}

It will include any typescript file for compilation which will be palced inside TypeScripts folder as mentioned in include section above. It also tells the compiler to copy the output js files in wwwroot/ts_build folder (mentioned by outDir key). We need to use the same path when referencing the js file in HTML.

<script src="~/ts_build/app.js"></script>

Now we have all setup with TypeScript, we can write TypeScript code and it should work.

References:

Related Post(s):

May 4, 2023

TypeScript - Promise.race()

The Promise.race() static method takes an iterable of promises as input and returns a single Promise. This returned promise settles with the eventual state of the first promise that settles (either fulfilled or rejected).

It's useful when you want the first async task to complete, but do not care about its eventual state (i.e. it can either succeed or fail).

Lets see an example:

const promise1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 300, 'promise1 value');
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'promise2 value');
});

Promise.race([promise1, promise2]).then((value) => {
     // promise2 is faster, so the 'value' will be the result of promise2
   console.log(value);
});

Output will be :

promise2 value

In this example, both promises will get resolved, but since the promise2 is faster (with less waiting time), the Promise.race() method will return promise2, it is the first promise in the input list which get settled (rejected or resolved).

If the iterable contains one or more non-promise values and/or an already settled promise, then Promise.race() will settle to the first of these values found in the iterable.

See this example:

const promise1 = new Promise((resolve, reject) => {
  setTimeout(reject, 100, 'promise1 rejected');
});

const promise2 = new Promise((resolve, reject) => {
  reject('promise2 rejected');
});

Promise.race([promise1, promise2])
.then((value) => {
  console.log(value);
})
// promise2 is already settled (rejected), 
// so the 'error' will be the rejection-error of promise2
.catch((error) => { console.error(error);})
;

Output will be :

promise2 rejected

Since promise2 is already settled (rejected), Promise.race() method will return promise2.

References:

Related Post(s):

TypeScript - Promise.any()

The Promise.any() method is useful for returning the first promise that fulfills. It short-circuits after a promise fulfills, so it does not wait for the other promises to complete once it finds one.

This method returns the first fulfilled value and ignores all rejected promises up until the first promise that fulfills. This can be beneficial if we need only one promise to fulfill but we do not care which one does.

Lets see an example:

const p1 = Promise.reject("some error");
const p2 = Promise.resolve("resolve value1");
const p3 = Promise.resolve("resolve value2");

const promises = [p1, p2, p3];

(Promise as any).any(promises)
.then((value) => console.log(value))
.catch((error) => { console.error(error.message);});

Output will be :

resolve value1

In this example, the first promise p1 is rejected, but Promise.any method ignores this and continue to the next promise p2 which get resolved/fulfilled. Once it finds the first fulfilled promise it stops processing further promises.

If all of the input promises are rejected, then it rejects with an AggregateError containing an array of rejection reasons.

See this example:

const p1 = Promise.reject("some error1");
const p2 = Promise.reject("some error2");
const p3 = Promise.reject("some error3");

const promises = [p1, p2, p3];

(Promise as any).any(promises)
.then((value) => console.log(value))
.catch((error) => { console.error(error);});

Output will be :

[AggregateError: All promises were rejected] {
  [errors]: [ 'some error1', 'some error2', 'some error3' ]
}

When all of the input promises are rejected, it generates the AggregateError (array of the rejection error from each input promise).

References:

Related Post(s):

April 6, 2023

TypeScript - Promise.allSettled()

The Promise.allSettled() method is one of the promise concurrency methods. It takes an iterable of promises as input and returns a single Promise. This returned promise fulfills when all of the input's promises settle, with an array of objects that describe the outcome of each promise.

The result(object) returned by each input promise has the following properties:
  • status: A string, indicating the eventual state of the promise (either "fulfilled" or "rejected").
  • value: if status is "fulfilled". The result/value of the promise.
  • reason: if status is "rejected". The reason for promise rejection.

Promise.allSettled() is typically used when you have multiple asynchronous tasks that are not dependent on one another to complete successfully, and we like to know the result of each promise.

Lets see an example:

const p1 = 10;
const p2 = Promise.resolve(20);
const p3 = Promise.reject("some error");
const p4 = new Promise((resolve, reject) => {
  setTimeout(() => {
	resolve("value from promise4");
  }, 100);
});

Promise.allSettled([p1, p2, p3, p4])
.then((results) => {
  console.log(results);
});

Output will be :

[
  { status: 'fulfilled', value: 10 },
  { status: 'fulfilled', value: 20 },
  { status: 'rejected', reason: 'some error' },
  { status: 'fulfilled', value: 'value from promise4' }
]

Promise.allSettled() executes .then() for all the input promises regardless of the status of promise (either rejected or fulfilled). You can inspect the result object to find status, value and reason properties.

References:

Related Post(s):

TypeScript - Promise.all()

The Promise.all() method is one of the promise concurrency methods. It takes an interable of promises as input and returns a single promise object. It is useful for aggregating the result of multiple promises passed as input.

  • This returned promise fulfills when all of the input's promises fulfill/resolve.
  • It rejects immediately when any of the input's promises rejects.

It is typically used when there are multiple asynchronous tasks that we want to fulfill before the code execution continues.

In this example, since all the input promises are resolved, so the final promise will also get resolved.

const p1 = 10;
const p2 = Promise.resolve(20);
const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
	resolve("value from promise3");
  }, 100);
});

Promise.all([p1, p2, p3]).then((values) => {
  console.log(values);
});

Output will be :

[ 10, 20, 'value from promise3' ]

Note that, the values parameter in .then() function will be an array containing all the output values from each input promise.

Lets see an example, if one of the promise from input is rejected.

// pass 4 promises: (1,2,3 and rejected-promise)
const p = Promise.all([1, 2, 3, Promise.reject(new Error("some error"))]);

p.then((values) => {
  //this will not get called.
  console.log(values);
})
.catch((error) => {
  //catch here for rejected promise
  console.error(error.message);
});

Output will be :

some error

Promise.all() executes the success callback when all the input promises are resolved. If any of the promise is rejected, then the rejection callback will be executed.

References:

Related Post(s):

March 20, 2023

JavaScript - What is Promise

A promise object is an instance of the Promise class. It represents the eventual completion or failure of an asynchronous operation.

A Promise is kind of proxy for a value might be unknown when the promise is created. You can associate handlers with a promise to receive notification/result of success and failure of asynchronous operation performed by promise executor.

Promise executor is simply a function passed to the constructor of Promise class, it controls the behavior of promise's resolution (success )or rejection (failure).

Promise lets asynchronous methods return values like synchronous methods. Instead of immediately returning the final value, it returns a promise object as a mechanism to supply the value at some point in the future.

A Promise object can have one of these states:

  • pending: initial state (neither fulfilled nor rejected)
  • fulfilled: when the operation was completed successfully.
  • rejected: when the operation failed.

To create a promise, we use new Promise(executor) syntax and provide an executor function as an argument.

To consume a promise, we use .then() function to receive the result of promise.

The .then() method of the promise object takes up to two arguments:

  1. First argument is a callback function for the fulfilled case of the promise (usullay known as resolve)
  2. Second argument is a callback function for the rejected case (usullay known as reject).

An example:

let myPromise = new Promise(function(resolve, reject) {
    let status_code = 200;  //to call resolve/success
    //let status_code = 201; //to call reject/failure
  
    if (status_code == 200) {
      resolve("Status code is 200");
    } else {
      reject("Status code is not 200");
    }
  });
  
myPromise.then(
	function(value) {console.log("Success:", value);},
	function(error) {console.log("Error:", error);}
);

For this example, inside the Promise executor function, if the variable status_code value is 200 then it will call resolve handler of the promise, which ultimately fall in the fulfilled callback of .then() function.

If you change the status_code value to anything other than 200, then it will call reject handler of the promise, which ultimately fall in the rejected callback of .then() function.

References:

Related Post(s):

TypeScript - Promise only refers to a type, but is being used as a value

When using Promise in TypeScript code and transpiling, it generates the error:

'Promise' only refers to a type, but is being used as a value here. 
Do you need to change your target library? 
Try changing the 'lib' compiler option to es2015 or later

There could be following reasons/fixes for this issue:

  • Check in tsconfig.json file, if the target property (under compilerOptions) is set to es2015 or later (as suggested in the error message).
    {
        "compilerOptions": {
            "target": "es2015",
        }
    }
    
  • In tsconfig.json file, add lib property (under compilerOptions), and set it values to es2015 or later.
    {
        "compilerOptions": {
            "target": "es2015",
            "lib": ["dom", "es2015", "es5", "es6"],
        }
    }
    
  • A quick work around for this error is just removing the type check for Promise, rather than fixing it. Declar the Promise as a variable with type any.
    declare var Promise: any;
    
  • Try to install @types/node from npm;
    npm instal @types/node
    
  • Please be aware that if you are running the tsc command with a file name, then the compiler will ignore the tsconfig.json file. For example, transpiling the file like this:
    tsc myfile.ts
    
    You can edit the tsconfig.json to include a set of files with files property, e.g:
    {
        "compilerOptions": {
            "module": "commonjs",
            "noImplicitAny": true,
            "removeComments": true,
            "preserveConstEnums": true,
            "sourceMap": true,
            "target": "es2015",
            "lib": ["dom", "es2015", "es5", "es6"],
        },
        "files": [
            "myfile.ts",
            "service1.ts",
            "common.ts",
            "util.ts",
        ]
    }
    

February 23, 2023

TypeScript - Class Decorators

The class decorator takes the constructor function as a parameter, allows us to change the way how this class is initialized.

Let's say we have a couple of classes Customer and Order. It is required that every class needs to have created property.

The normal solution is to create a base class which will have common fields and allow the childern to inherit from this.

In this example, we will use decorator to achieve this behavior, the decorator function will receive the target's constructor function as a parameter, and add the created property to its prototype.

Here is the code for decorator:

function EntityWithTimeStamp(constructorFunction: Function) {
    constructorFunction.prototype.created = new Date().toLocaleString("en-US");
    console.log("decorator called");
}

It receives constructorFunction(of the target class) as parameter, and adds created property to its prototype.

The decorator is ready to be used in each entity. We only need to add @EntityWithTimeStamp before class definition.

Here we define our classes and adds @EntityWithTimeStamp decorator.

@EntityWithTimeStamp
class Customer {
  constructor(public name: string) {
    console.log("Customer controctor called");
  }
}

@EntityWithTimeStamp
class Order {
  constructor(public amount: number) {
    console.log("Order  controctor called");
  }
}

Both of these classes have defined their own public properties received in constructor. Also they will have an additional property created which will be added by the decorator.

You can create objects of these classes as usual, and output the public properties to console:

let customer = new Customer("Idrees");
let order  = new Order(100);

console.log(order.amount);
console.log(customer.name);

Note that the decorator does not change the TypeScript type and the new property created is not known to the type system. The following lines will give you the error because compiler will not find created property in these referenced classes/types:

console.log(order.created);
console.log(customer.created);

Error message:

error TS2339: Property 'created' does not exist on type 'Customer'.
error TS2339: Property 'created' does not exist on type 'Order'.

There is a work around this issue, you can access the created property by temporarily casting the object to type any.

console.log((<any>order).created);
console.log((<any>customer).created);

Output will display the value of crearted property, which shows that decorator has added this field as the class member.

Related Post(s):

February 22, 2023

Decorators in TypeScript

Decorators provide a mechanism to metaprogramming syntax in TypeScript, which is a programming technique basically means "writing code that writes code".

Decorators allow us to decorate members of a class, or a class itself, with extended functionality. This is a function that we can hook into our code, to extend with some behavior and helps us to write code abstractions and provide extension mechanism.

When you apply a decorator to a class or a class member, it will actually call a function that is going to receive details of target (what is being decorated), and the decorator implementation will then be able to transform the code dynamically (e.g. adding extra functionality, and reducing boilerplate code).

The decorators are used on class itself and its members:

  • class definition
  • properties
  • methods
  • accessors
  • parameters

Note that:

Decorators are a stage 2 proposal for JavaScript and are available as an experimental feature of TypeScript.

Before using decorators in TypeScript, we need to enable it. If the decorator support is not enabled and you try to compile a TypeScript file/project that is using decorator, it will give you this error:

 error TS1219: Experimental support for decorators is a feature that is subject to change 
 in a future release. Set the 'experimentalDecorators' option in your 'tsconfig' or 'jsconfig'
 to remove this warning.

We have two ways to enable decorators support in TypeScript:

  • You can enable decorators support at compile time. When using the TypeScript Compiler CLI (tsc), we need to provide additional flag --experimentalDecorators:
    tsc --experimentalDecorators
    
  • When working in a project that has a tsconfig.json file, to enable decorators support you can add the experimentalDecorators property (with value true) to the compilerOptions object:
    {
      "compilerOptions": {
        "experimentalDecorators": true
      }
    }
    

Related Post(s):

January 24, 2023

TypeScript - Function as a parameter

Sometimes we need to pass a function as a parameter to another function, which will internally call the parameter function.

Lets see an example how we can receive a function as a parameter.

We have to define the parameter type as Function.

function shipOrder(func: Function): void {
   //some logic
   console.log("shipOrder function is called");
	
   //call the function which is passed a parameter
   func();
}

Lets say we have a function getBillingAddress as follows:

getBillingAddress() {
   //some logic
   console.log("getBillingAddress function is called");
}

shipOrder is the function which will accept another function as a parameter, and then internally call this function. Since getBillingAddress accepts not parameter and returns void, we can simply invoke the function by its name alongwith paranthesis.

This is how we will call the shipOrder function by passing the function name as parameter.

shipOrder(getBillingAddress);

We can make it easier to read, define an interface describing the function signature that need to pass as paramter:

interface IFunction {
(): void;
}

The shipOrder function will become like this:

function shipOrder(func: IFunction): void {
   //some logic
   console.log("shipOrder function is called");
	
   //call the function which is passed a parameter
   func();
}

Since IFunction interface contains a function, with not paramters and void return type. Passing our old getBillingAddress function is still valid because of same function signature.

shipOrder(getBillingAddress);

We can also specify the paramters and return type for the function. Lets define a new interface:

interface INumberFunction {
(num: number): string;
}

Change the paramter type to INumberFunction

function shipOrder(func: INumberFunction): void {
   //some logic
   console.log("shipOrder function is called");
	
   //call the function which is passed a parameter
   //now the func is of type INumberFunction, we need to pass a number paramter and it will return a string.
   let someValue: string = func(1);
}

Calling the function and passing parameter is same.

shipOrder(getBillingAddress);

But when you need to invoke the actual function (which is passed a paramter), you have to take care of its signature, the required paramters and return type.

   let someValue: string = func(1);

C# Yield Return & Break Statements

The yield keyword makes the method (in which it appears) as an iterator block. An iterator block, or method, will return an IEnumerable as the result. Within this iterator block or method, we will use the yield keyword to return the values for the IEnumerable.

Note that IEnumerable is lazily evaluted. If you call a method with an iterator block (with yield keyword), it will not run any code until we actually access the IEnumerable result values.

Lets see an exmaple.

Usually you may find code similiar to this where we are creating a temp list to hold the items, and at the end return the list from a method:

public IEnumerable<int> MyList()
{
	List<int> list = new List<int>();
	list.Add(1);
	list.Add(2);
	list.Add(3);
	list.Add(4);
	list.Add(5);

	return list;
}

You can simplify the method using the yield return statement, it allows us to remove the intermediate/temporary list required to hold the values.

public IEnumerable<int> MyList()
{
	yield return 1;
	yield return 2;
	yield return 3;
	yield return 4;
	yield return 5;
}

This method will return the same list of intergers but it does not need a temporary list to hold values.

You can use the yield break statement to explicitly stop the current iteration and cancel the iterator block. In this case it will return all these values which are produced with yield return statement. Once it reaches the yield break statement, it will stop producing any further value and exit the iterator block.

Lets see this example:

public static IEnumerable<int> MyList()
{
	yield return 1;
	yield return 2;
	yield break;
	yield return 3;
	yield return 4;
	yield return 5;
}

This method will only produce two values 1 and 2. Once it reaches the yield break statement, it will exit from the iterator block.

Typically you would do this when a certain condition is met, you only want to return a specific set of values from the iterator block.

Let see this example:

IEnumerable TakeWhilePositive(IEnumerable<int> numbers)
{
    foreach (int n in numbers)
    {
        if (n > 0)
        {
            yield return n;
        }
        else
        {
            yield break;
        }
    }
}

If you call this method like:

foreach (var item in TakeWhilePositive(new[] { 1, 2, 3, -1, 4, 5}))
{
	Console.WriteLine(item);
}

It will print the values from given array as:

1
2
3

Once it found a negative value, the iteration loop will be stopped and control is returned to the caller method.

References: