• Featured post

C# Task async programming (TAP) and parallel code

The core for asynchronous programming are the objects Task and Task<T>. Both of them are compatible with the keywords async and await.

First of all we need to identify if the code’s I/O-bound or CPU-bound.

  • the code’s limited for external operations and waits for something a lot of time. Examples of this are DDBB calls, or a server’s response. In this case we have to use async/await to free the thread while we wait
  • the code does a CPU-intensive operation. Then we move the work to another thread using Task.Run() so we don’t block the main thread.

async code vs parallel code

(!) Asynchronous code is not the same as parallel code (!)

  • In async code you are trying to make your threads do as little work as possible. This will keep your app responsibe, capable to serve many requests at once and scale well.
  • In parallel code you do the opposite. You use and keep a hold on a thread to do CPU-intensive calculations

async code

The importante of async programming is that you choose when to wait on a task. This way, you can start other tasks concurrently

In async code, one single thread can start the next task concurrently before the previous one completes.
(!) async code doesn’t cause additional threads to be created because an async method doesn’t run on its own thread. (!) It runs on the current synchronization context and uses time on the thread only when the method is active.

parallel code

For parallelism you need multiple threads where each thread executes a task, and all of those tasks are executed at the same time

Read More

SQL Views & Materialized Views

SQL View

A view in SQL is essentially a virtual table. It doesn’t store data physically. Instead it presents data from one or more underlying tables through a predefined SQL query. Think of it as a saved query that you can treat like a table.

  • Views don’t hold data themselves. When you query a view, the database executes the underlying query to fetch data in real-time.
  • Views can simplify complex queries by encapsulating them. Instead of writing a complex JOIN or subquery each time, you select from the view.
  • Views can restrict user access to specific rows or columns, enhancing security by exposing only necessary data.
  • Since views are generated on the fly, they always reflect the current state.
CREATE OR REPLACE VIEW active_customers AS
SELECT customer_id, name, email
FROM customers
WHERE status = 'active';

When to use a view

  • Use it to simplify complex queries that you use frequently and you need the most current data every time.
  • Restrict user access to specific data by exposing only certain columns or rows through a view

Materialized View

A materialized view is like a regular view, but it stores the query result’s phisically and it doesn’t involve executing the underlying query each time.

  • Since data is precomputed and stored, querying a materialized view is faster for complex queries over large datasets.
  • Because of this, data in a materialized view can become outdated and needs to be refreshed periodically.
CREATE OR REPLACE MATERIALIZED VIEW sales_summary AS
SELECT product_id, SUM(quantity) AS total_quantity
FROM sales
GROUP BY product_id;

When to use a materialized view

  • Is ideal for speeding up complex queries that are resource-intensive and slow to execute.
  • Suitable for scenarios where data doesn’t change frequently and fast read performance is needed.
  • You need to tolerate data that’s not always up-to-date

SQL Triggers & Stored Procedures

SQL Triggers

A SQL trigger is a code block that executes automatically when a specified event occurs on a table or view, such as an insert, update or delete.

A trigger can be used to perform actions before or after the event such as checking data integrity or spread data changes between tables.

Such an example would be to create a trigger that prevents users from inserting or updating data in a table if the data violates a rule, such as a maximum length or a required field.

CREATE TRIGGER trigger_name
BEFORE/AFTER event
ON table_name
FOR EACH ROW
BEGIN
	-- trigger code or call to procedure
END;

SQL Stored Procedures

A SQL Procedure is a code block that performs one or more tasks and can be called by other programs or queries. A procedure can accept parameters, return values and use variables, loops and conditional statements.

A procedure can be used to encapsulate complex logic or reuse code.

CREATE PROCEDURE procedure_name (parameters)
BEGIN
	-- procedure code
END;

Best practices

  • Use descriptive and consistent names.
  • Document your code with comments and explain the purpose and logic
  • Avoid using too many or complex procedures or functions. This may affect performance or reliability of your database. They really increase difficulty to follow an operation.

Reference(s)

https://www.linkedin.com/advice/3/how-do-you-use-sql-triggers-procedures-functions?lang=en

C# Raw string literals

Before raw string literals

The main problems with long string literals were:

  • strings that include double quotes tend to be unreadable
  • identation looks messy in multiline strings

we have the following json we want to put into a string literal

{
	"number": 42,
	"text": "Hello, world",
	"nested": { "flag": true}
}

an ordinary quoted string literal looks like this:

string json = "{\r\n  \"number\": 42,\r\n  \"text\": \"Hello, world\",\r\n  \"nested\": { \"flag\": true }\r\n}"

verbatim string literals work slightly better here but will be missaligned when used over nested code. Also quotes look different as they still need to be scaped.

foreach(var item in list)
{
	if(Check(item))
	{
		string json = @"{
  ""number"": 42,
  ""text"": ""Hello, world"",
  ""nested"": { ""flag"": true }
}";
	}
}

Using raw string literals

Here’s how this example looks using raw literals.

foreach(var item in list)
{
	if(Check(item))
	{
		string json = """
		{
			"number": 42,
			"text": "Hello, world",
			"nested": { "flag": true }
		}
		""";
	}
}

Inside raw literals we don’t need to scape chars. Also we’re able to indent our code.

Read More

C# Anonymous methods and lambda expressions

Named method vs anonymous method

A named method is a method that can be called by its name:

// named method declaration
public string Join(string s1, string s2)
{
	return s1+s2;
}
// named method usage
var result = Join("this is ", "a joined string");

An anonymous method is a method that is passed as an argument to a function, without the need for its name. These methods can be constructed at runtime or be evaluated from a lambda expression.

// declaration
public void ProcessBook(Action<Book> process, List<Book> books)
{
	foreach (Book b in books)
	{
		process(b);
	}
}
// usage - print book titles
ProcessBook(bookList, book => Console.WriteLine(book.Title))

Here book => Console.WriteLine(book.Title) is the lambda expression and it’s result is an anonymous function that will be run by the method ProcessBook

Read More

C# Partial classes

A partial class in c# can be used to split functionality across multiple files, each with the same namespace and name.

A good use case for them is to use them as a stepping-stone in refactoring god-classes. If a class has multiple responsibilities (really large files), it may be a TEMPORAL way to refactor & split behaviours, before splitting them into different classes.

Usually, you can’t have 2 classes with the same name. This is unless you mark them as partial.

// this works fine, although it's not the best use case
public partial class MyClass
{
	public bool Ok { get; set; }
}

public partial class MyClass
{
	public bool IsOk()
	{
		return Ok;
	}
}

When you have multiple partial classes the compiler will merge them all into one single class.
Some rules:

C# Pattern matching

Overview of scenarios where you can use pattern matching. These techniques can improve the readability and correctness of your code.

Reduce nested if-else

I have a complex nested if-else statements scenario

// DON'T DO THIS
if (order.Status == "Pending")
{
	ProcessPendingOrder(order);
} else if (order.Status == "Completed")
{
	ProcessCompletedOrder(order);
} else if (order.Status == "Cancelled")
{
	ProcessCancelledOrder(order);
} else
{
	throw new InvalidOperationException("Unknown status");
}

instead do this

order.Status switch
{
	"Pending" => ProcessPendingOrder(order),
	"Completed" => ProcessCompletedOrder(order),
	"Cancelled" => ProcessCancelledOrder(order),
	_ => throw new InvalidOperationException("Unknown status")
};

Using it as a method

You can test a variables to find a match on specific values

public State PerformOperation(string command) =>
	command switch
	{
		"SystemTest" => RunDiagnostics(),
		"Start" => StartSystem(),
		"Stop" => StopSystem(),
		_ => throw new ArgumentException("Invalid string value", nameof(command)),
	};

_ is the discard pattern that matches all values. It handles any error conditions where the value doesn’t match one of the defined values.

Read More

C# Expression bodied members

Expression body definitions let you provide a member’s implementation in a concise, readable form.

Methods

Expression-bodied method consists of a single expression that returns:

  • a value whose type matches the return value
  • or performs an operation for void methods
public class Person(string firstName, string lastName)
{
	public override string ToString() => $"{firstName} {lastName}".Trim();
	public void DisplayName() => Console.WriteLine(ToString());
}

Read-only properties

You can use them to implement read-only property.

public class Location(string locationName)
{
	public string Name => locationName;
}

Reference(s)

https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/expression-bodied-members

C# Collections' index and range operators

Index operator ^1

The index operator is used to reach the last positions of an array or list.

before

List<int> arr = [0, 1, 2, 3, 4, 5];
if(arr[arr.Count-1] == 5)
	Console.WriteLine("match!"); // prints

same but using index operator

List<int> arr = [0, 1, 2, 3, 4, 5];
if(arr[^1] == 5)
	Console.WriteLine("match!"); // prints

we can use it to reach any position starting from the back

List<int> arr = [0, 1, 2, 3, 4, 5];
if(arr[^2] == 4)
	Console.WriteLine("match!"); // prints

Read More

Deconstruction in ASP.NET Core

Desconstructors allows us to extract in a single expression, properties of an object or elements of a tuple, and assign them to distinct variables.

record and record struct automatically provide a Deconstruct method.

Classes

before deconstruction we had to manually extract each variable

var pat = new Person() { Name = "Patrick", BirdDate = new DateOnly(1999, 1, 18)};
var name = pat.Name;
var birthDate = pat.BirthDate;
Console.WriteLine($"Name: {name}, birthdate: {birthDate}");

class Person

public class Person
{
	public string Name { get; set; }
	public string Location { get; set; }
}

with deconstruction

var pat = new Person() { Name = "Patrick", BirdDate = new DateOnly(1999, 1, 18)};
var (name, birthDate) = pat;
Console.WriteLine($"Name: {name}, birthdate: {birthDate}");

class Person with Deconstructor

public class Person
{
	public string Name { get; set; }
	public string Location { get; set; }
	
	public void Deconstruct(out string name, out string location) 
	{
		name = Name;
		location = Location;
	}
}

if you don’t want all properties, just discard some using the discard character _

var pat = new Person() { Name = "Patrick", BirdDate = new DateOnly(1999, 1, 18)};
var (name, _) = pat;
Console.WriteLine($"Name: {name}");

Tuples

The same applies to tuples

// tuple creation
(string name, string location) person = ("mario", "spain");
// deconstructor
var (name, location) = person;

Reference(s)

https://blog.ndepend.com/deconstruction-in-c/

Dependency injection in .NET Core

Dependency injection is native-available in .NET Core

DI implementation

interface we want to inject

public interface IDateTime
{
	DateTime Now { get; }
}

interface’s implementation

public class SystemDateTime : IDateTime
{
	public DateTime Now
	{
		get { return DateTime.Now; }
	}
}

service we inject into

public class HomeController : Controller
{
	private readonly IDateTime _dateTime;

	public HomeController(IDateTime dateTime)
	{
		_dateTime = dateTime;
	}

	public IActionResult Index()
	{
		var serverTime = _dateTime.Now;
		return Ok(serverTime);
	}
}

Read More