• 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

Validaciones C#

C# tiene los DataAnnotation

[Required] hace que sea un campo obligatorio

[Required(ErrorMessage = "Nombre es obligatorio")]
public string NombreCategoria { get; set; }

[Required(ErrorMessage = "Orden es obligatorio")]
[Range(1, int.MaxValue, ErrorMessage = "El orden debe de ser mayor a cero")]
public int Orden {get; set; }

Se controla mediante el siguiente codigo en el controller. El código de ModelState es código base de un Controller.

if(ModelState.IsValid) 
{
	// code if everything's valid
}

C# coding style

Interfaces must start by capital I

public interface IDataService 
{ 
	public Task SendData(DataModel model);
}

We use PascalCase for:

  • classes’ name
  • methods’ name
  • public variables

We use CamelCase for:

  • private or internal field names (they must include the prefix _)
  • methods’ paramters
public class DataService
{
	const int TAX = 7;
	
	public bool IsValid { get; private set; };
	private IWorkerQueue _workerQueue;
	
	public async Task SendData(DataModel model)
	{
		string someValue = "";
		// ... whatever
	}
}

async methods must end by Async

public async Task<string> GetUrlAsync()
{
	// ... whatever
}

Reference(s)

https://learn.microsoft.com/es-es/dotnet/csharp/fundamentals/coding-style/identifier-names

Introducción a .NET

CLR (Common language runtime)

Entorno de ejecucion para .NET. En tiempo de ejecucion el compilador de CLR convierte el codigo CIL en codigo nativo para el SO. Facilita la integración entre lenguajes.

CLR es la MV en la que se ejecutan nuestras apps. CLR se hizo para tener una capa de abstraccion entre las propias apps y el SO donde se ejecutaban.

El CLI se puede ejecutar en otros SO. El CLR se ejecuta solo en Windows.

.NET intro

Read More

.NET launchsettings vs appsettings

launchSettings

NO se despliega. Afecta a tu entorno local.

En él establecemos los perfiles con los que ejecutaremos nuestro proyecto y se usa para definir la variable ASPNETCORE_ENVIRONMENT. También permite establecer la url y puertos de ejecución.

{
  "profiles": {
    "my.project.namespace": {
      "commandName": "Project",
      "launchBrowser": false,
      "applicationUrl": "https://localhost:5011;http://localhost:5010",
	  "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }
  }
}

ASPNETCORE_ENVIRONMENT Indica el entorno - soporta los siguientes valores

  • Development
  • Staging
  • Production valor por defecto si se omite el valor

appsettings

Se utiliza para almacenar la configuración de la aplicación como por ej. cadenas de conexión de BBDD.
Se utiliza tanto en entornos de desarrollo como de producción.

{
	"LocalDirectory": "/opt/data-download",
	"MyServiceConfig": {
		"Uri": "http://localhost:8800",
		"Endpoint": "/some-endpoint",
		"Timeout": 30
	}
}

appsettings.{Environment}.json

Son archivos adicionales opcionales, donde {Environment} corresponde al valor de la variable ASPNETCORE_ENVIRONMENT del launchSettings.json.

El orden de carga es:

  1. appsettings.json
  2. appsettings.{Environment}.json (si existe)
  3. launchSettings.json

Hay varias maneras de leer la configuración.

leer de la raiz

Las que se encuentran en la raiz las podemos leer inyectando la config en la clase.

public class MyService(IConfiguration _config) : IMyService
{
	public void MyMethod()
	{
		var localDirectory = _config["LocalDirectory"];
	}
}

leer de una clase de config custom

por un lado tenemos la clase de config

public class MyServiceConfig
{
	public const string Section = "MyServiceConfig";

	public string Uri { get; set; }
	public string Endpoint { get; set; }
	public int Timeout { get; set; }
}

y por otro lado la inyectamos

public class MyService(MyServiceConfig _config) : IMyService
{
	public void MyMethod()
	{
		var uri = _config.Uri;
		var endpoint = _config.Endpoint;
		var timeout = _config.Timeout;
	}
}

la tendremos que poner también en el Startup

public void ConfigureServices(IServiceCollection services)
{
	// ...
	services.Configure<MyServiceConfig>(config.GetSection(MyServiceConfig.Section));
	// ...
}

Buenas prácticas

Usar appsettings.json como base y en cada appsettings.{Environment].json poner solo los datos que cambien.

No poner secretos en appsettings.json ni sus variantes:

  • En local (para development) utilizar User secrets
  • Para los entornos con Kubernetes inyectar secretos via ConfigMaps o Secrets

Reference(s)

https://learn.microsoft.com/es-es/aspnet/core/fundamentals/environments?view=aspnetcore-8.0
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-8.0

.NET Middleware

Middleware is software assembled into a pipeline to handle request and responses. Each component:

  • Chooses if he passes the request to the next component.
  • Can perform work before and after the next component.

Common use cases:

  • Logging
  • Authentication & Authorization
  • Request/Response processing
  • Caching
  • Error handling

.NET middleware

Implementation w. DI

For more details check this project

We create our own custom middleware with an internal dependency, which is the class it’s going to process before/after our request.

// interface and its class, which do the processing
public interface IMiddlewareService
{
	Task ProcessRequest(string request);
}

// interface implementation
public class MiddlewareService : IMiddlewareService
{
	public async Task ProcessRequest(string request)
	{
		// do something with the request
		Console.WriteLine(request);
	}
}

Then we have our middleware class as such

public class CustomMiddleware(IMiddlewareService _service) : IMiddleware
{
	public async Task InvokeAsync(HttpContext context, RequestDelegate next)
	{
		// custom logic to be executed BEFORE next middleware
		await _service.ProcessRequest("middleware processing request");
		await next(context);
		// custom logic to be executed AFTER next middleware
	}
}

Declaration inside Startup.cs

public void ConfigureServices(IServiceCollection services)
{
	// ... whatever

	// register both: our custom middleware, and its internal dependency
	services.AddTransient<IMiddlewareService, MiddlewareService>();
	services.AddTransient<CustomMiddleware>();
	
	// ... whatever else
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
	// ... whatever

	// declaration to use middleware
	app.UseMiddleware<CustomMiddleware>();
	// if we use this, the middleware declaration has to be BEFORE or else it won't work
	app.UseEndpoints(endpoints => 
	{
		endpoints.MapControllers();
	})

	// ... whatever else
}

Reference(s)

https://medium.com/@dushyanthak/best-practices-for-writing-custom-middlewares-in-asp-net-core-97b58c50cf9c
https://learn.microsoft.com/es-es/aspnet/core/fundamentals/middleware/write?view=aspnetcore-8.0
https://sardarmudassaralikhan.medium.com/custom-middleware-in-asp-net-core-web-api-70c2ffbbc095

Test APIs with Postman - Scripting

We can set pre-request scripts (run before the request) & tests (after execution) at several levels:

  • Collection
  • Folder
  • Request

Snippets

Inside pre-request Script and Tests we have a SNIPPETS column with templates we may use for our code.

postman scripting

Get / Set variables

console.log("Hello world");

// work with local vars
let urlVar = pm.variables.get("protocol");
console.log("value for protocol: " + urlVar);

pm.variables.set("protocol", "http");
console.log(pm.variables.get("protocol"));  

// work with global vars
let globalVar = pm.globals.get("env");
console.log(globalVar);

Read More

Test APIs with Postman - GUI

To test postman I use https://reqres.in. You can use it to learn how to write Postman tests.

Collections

Collections have metadata which you can set up. This includes:

  • Authorization so you don’t need to set it for every single request.
  • Variables, for variables only for this collection (so you don’t use environment vars).
  • Collection tests.

postman gui

Read More

Error al crear imagenes en local (Minikube)

Hay un problema al intentar correr una imagen tuya propia en local con minikube. Minikube tiene su propio repositorio de imagenes y si no encuentra una imagen alli, la intenta descargar siempre del repositorio.

Solución

Set imagePullPolicy: IfNotPresent

error crear imagenes minikube en local

Run the following command:

eval $(minikube docker-env)
# build again the image
# try to run it again

Reference(s)

https://medium.com/bb-tutorials-and-thoughts/how-to-use-own-local-doker-images-with-minikube-2c1ed0b0968

K8s config commands

Config

Azure

log-in into a tenant

az login --tenant 7f2c3a6b-e849-4d6f-b65d-1c0ef8b0f41c

configure local kubectl .kube config file

az aks get-credentials --resource-group my-aks --name aksname --admin

(the –admin part is optional but useful)

Contexts & Namespaces

see all configured contexts

kubectl config get-contexts

configure a context with a default namespace

kubectl config set-context <context_here> --namespace=<namespace_here>
# example
kubectl config set-context my-aks-context --namespace=aks-dev

Read More