• Featured post

C# Async ops

El nucleo de la programacion asincrona son los objetos Task y Task<T>. Son compatibles con las palabras clave async y await.

Primero hay que reconocer si el codigo se usa para trabajos enlazados a I/O o si son CPU intensivos.

  • Si el codigo espera algo como una BBDD o una response de un servidor, es codigo I/O. En este caso hay que usar async y await
  • Si el codigo realiza un calculo costoso, es CPU intensivo. Use await para esperar una operacion que se ha comenzado en background con Task.run

Async / Await (Operaciones I/O)

La palabra clave importante aqui es await. Lo que hace es suspender la ejecucion del metodo actual y devolver el control hasta que está lista para seguir.

public async Task Main()
{
	string contenido = await LeerPaginaWebAsync("http://example.com");
}

private async Task<string> LeerPaginaWebAsync(string url)
{
	using (HttpClient client = new HttpClient())
	{
		return await client.GetStringAsync(url);
	}
}

Read More

Herencia vs composicion

Ambas son dos maneras de reutilizar código para crear nuevas clases

Herencia

Permite que una subclase herede propiedades y métodos de su superclase o clase base. Facilita la reutilización de código y creación de relaciones jerárquicas entre clases.

Sin embargo puede llevar a estructuras de clases rígidas y complejas, y a problemas de acoplamiento.

Composición

En lugar de heredad de una clase base, la clase se “compone” de otras clases incluyendo instancias de otras clases como campos.

Esto promueve un diseño más modular y flexible ya que permite cambiar el comportamiento en tiempo de ejecución.

Depende del problema específico pero la composición suele ser preferida por su flexibilidad y capacidad para evitar problemas comunes de herencia.

Read More

SOLID Principles

These principles establish practices that helps maintain and extend software as it grows.

S - single responsibility
O - open/closed
L - liskov substitution
I - interface segregation
D - dependency inversion

Single responsibility

A class should have only one job - it should have only one reason to change

Read More

PgSQL Functions (Stored Procedure)

(This is an implementation example. For an explanation on this, please check my other post: SQL Triggers & Stored Procedures)

Function example

-- example of function that triggers when an entry is inserted into a table
--   and manages this data inserting data as needed in another table
CREATE OR REPLACE FUNCTION your_schema.my_function_name(arg1 character varying, data character varying)
	RETURNS character varying
	LANGUAGE plpgsql
AS $function$
BEGIN

-- check if already exists in the other table
IF
	(SELECT COUNT(*) FROM your_schema.other_table WHERE name=arg1) > 0) THEN
		RETURN 'Error: 1210. data already exists';
END IF;

-- insert and manage data
INSERT INTO your_schema.other_table (name, data) VALUES (arg1, data);
RETURN 'Success: 1200';

END $function$;

How to Debug in DBeaver

There are two options, logs or break the function with an exception.

If logs are enough, you just write the message to output per console.

RAISE NOTICE 'this is null';

To see logs in DBeaver click here. Then you may execute the function to see the logs.

how to debug in dbeaver

If you want to break the function runtime with an exception, you write the following instead

RAISE EXCEPTION SQLSTATE '90001' USING MESSAGE = 'error. this already exists';

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