• Featured post

C# Async ops

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

Primero hay que identificar si el codigo se usa para trabajos enlazados a I/O o si estos 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

Read More

Entity Framework Core Scaffolding

Go to Tools > Nuget Package Manager > Package Manager Console

Adapt and paste the following code. This includes:

  • db’s connection string
  • name for the context it’s going to create
  • Where it pastes the data classes to
Scaffold-DBContext "Host=host_here;Database=database_name;Username=username_here;Password=pwd_here" Npgsql.EntityFrameworkCore.PostgreSQL -DataAnnotations -Context ContextNameHereDbContext -ContextDir Data -o Models/DB -force -NoOnConfiguring -verbose

This creates:

  • DbContext.cs class
  • all Model/Data classes

Remember to modify Startup.cs to set the service classes as AddTransient for the services which use this DbContext

services.AddTransient<SomethingService, SomethingService>(); 

Add into the service, the context you’ve just created and set it at the constructor.

private readonly ApplicationDbContext _context;

EFCore consultas solo lectura - gran volumen entidades

Cuando se van a realizar consultas de solo lectura, las cuales NO se van a usar para actualizar en la base de datos, se puede llamar al método .AsNoTracking() para mejorar la performance.
Es recomendable utilizarla para manejar grandes volumenes de entidades.

Las entidades de las queries donde se use .AsNoTracking() no se podrán actualizar, por lo que no es recomendable para ADD, UPDATE o DELETE

// operacion de solo lectura
var libros = context.Libros
	.AsNoTracking()
	.Where(l => l.Autor == "Something")
	.ToList();

Data Models Entity Framework Core

You can easily mark fields for a model which are primary key or are required.

public class User
{
	[Key]
	public int Id { get; set; }
	
	[Required(ErrorMessage = "Name is mandatory")]
	public string Name { get; set; }
	
	public string Telephone { get; set; }
	
	public string Mobile { get; set; }
	
	[Required(ErrorMessage = "Email is mandatory")]
	public string Email { get; set; }
}

Nullables C#

Tipos Nullable (?)

El operador ? se usa para convertir un tipo de valor en un tipo de valor nullable, el cual puede contener un valor nulo.

int? numeroNullable = null;
if(numeroNullable.HasValue)
{
	Console.WriteLine(numeroNullable);
}

Acceso condicional nulo a miembros (?. o ?[])

Este operador permite acceder a un miembro de un objeto solo si el objeto no es nulo, evitando asi una NullReferenceException. Si no se cumple, devuelve null.

  • si a es nulo, el resultado de a?.x o a?[x] es null
  • si a no es nulo, el resultado de a?.x o a?[x] es el mismo que a.x o a[x]
string[] nombres = null;
int? longitud = nombres?.Length; // asigna null en lugar de lanzar una excepcion

(!) (revisar como usarlo mejor junto al operador ??) (!)

Coalescencia de Nulos (??)

Se usa para proporcionar un valor por defecto en caso de que una expresion nullable contenga un null.

sin este operador

string name = GetName();
if(name == null)
{
	name = "unknown";
}

se reemplaza por esto usando el operador

string name = GetName() ?? "unknown";

Se recomienda usar ?? en vez de expresiones ternarias para comprobar un null

// no usar esto
var v = x != null ? x : y; // or 
var v = x == null ? x : y;

// y usar esto en su lugar
var v = x ?? y;

(!) También se puede usar junto al operador ?. para asignar un valor por defecto en caso de que sea nulo (!)

string[] nombres = ...; // whatever 
string nombre = nombres?[index] ?? "NotFound"; // array is null or name not found

Se puede usar junto a un throw para hacer más concisa la asignación de valores.

public string Name
{
	get => name;
	set => name = value ?? throw new ArgumentNullException(nameof(value), "Name cannot be null");
}

Asignacion de coalescencia de nulos (??=)

reemplaza este formato de código

if (variable is null) 
{
	variable = "something";
}

por esto. lo hace más conciso

variable ??= "something";

Reference(s)

https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-coalescing-operator
https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0029-ide0030-ide0270
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/member-access-operators#null-conditional-operators–and-

Linq examples

Retrieve a simple subset of properties for all items in list

Usually, anon data types are used in the select clause to return a specific subset of properties for each object in the collection.

we have the employee class

public class Employee
{
	public int ID { get; set; }
	public string Name { get; set; }
	public int Age { get; set; }
	public string Address { get; set; }
}
List<Employee> employees = // whatever ...
// we convert an Employee into an anon object type with a subset of properties
var employeeDetails = from emp in employees
	select new { Id = emp.ID, Name = emp.Name };

How to map List of classes

// Method to show call usage. 
public List<MappedUser> MapListOfUsers(List<User> users)
{
	// option 1
	List<MappedUser> mappedUsers = users.Select(user => MapSingleUser(user))
		.ToList();
	
	// option 2
	List<MappedUser> mappedUsers2 = 
		(from user in users select MapSingleUser(user)).ToList();
}

method to encapsulate mapping itself

private MappedUser MapSingleUser(User user)
{
 var mapped = new MappedUser
 {
	 Id = user.Id,
	 Name = user.Name,
	 Email = user.Email
 };
 return mapped;
}

This provides easier and more legible than doing a foreach to iterate everything.

How to filter list per properties (Where)

var adminUserTask = users
	.AsParallel()
	.Where(user => "admin".Equals(user.type.ToLower()))
	.Select(async user => { return await ProcessAdmin(user);});
List<UserResults> results = (await Task.WhenAll(adminUserTask)).ToList();

How to select records based on another list (ids)

This is how to select a list of items, selecting them by id, based on another list of items

List<string> idList = // ...
var profiles = _context.UserProfiles
		.Where(userProf => idList.Contains(userProf.Id));

How to order based on a dynamic parameter

// Direction is an Enum w. values ASC or DESC
private List<Person> SortPersons(Direction direction, List<Person> persons, Func<Person, string> sortBy)
{
	if(direction.DESC)
	{
		return persons.OrderByDescending(sortBy.Invoke).ToList();
	} else 
	{
		return persons.OrderBy(sortBy.Invoke).ToList();
	}
}

How to use it

var sortedPersons = SortPersons(direction, persons, person => person.Name);

How to use in async await env

We separate the linq query into Task and Execution. This executes this Task concurrently.

Single param lambda

remember that for lambdas with a single param where it comes from the same query, you don’t need to explicitely set it

this transforms

public List<int> FilterList(List<int> listToFilter, List<int> filterintList)
{
	return listToFilter.Where(number => filteringList.Contains(number)).ToList();
}

How to filter list per properties (Where)

filter by properties in a nullable list and return true if there’s any row that match. If the list is null, it returns false.

return response.results?.rows
	.Where(row => row.id == requestId && (row.owner == requestOwner || row.responsible == requestResponsible))
	.Any() ?? false;

another example

var task = response.results.rows
	.AsParallel()
	.Where(row => "specifictype".Equals(row.type.ToLower()))
	.Select(async row => {
		if(row.type.Equals("specificType"))
		{
			return await Something(row, id, log);
		} else 
		{
			return row;
		}
	});

Methods worth a mention

Remove duplicates Distinct()

var task = response.results.rows
	.AsParallel()
	.Where(row => "specificType".Equals(row.type.ToLower()))
	.Select(async row => { return await Something(row, id, log);});

Get the difference of two lists Except()

var list = listToFilter
	.Except(filteringList)
	.ToList();

First() vs Single()

// gets the first element that matches and stops there
string list = listToFilter
	.First(s => s.StartsWith("ohno"));

// gets the matching element, or throws an exception if there's more than 1
string list = listToFilter
	.Single(s => s.StartsWith("ohno"));

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
}

Nomenclatura C#

class Program 
{
	// private variables start with _
	private readonly string _estaEsMiCadena;

	// constantes
	const int IVA = 7;

	// fields
	public int Name { get; private set; }

	static void Main(string[] args)
	{
		// camel case
		var thisIsMyVariable = "Camel";
	}

	// pascal case
	public void ThisIsAMethod()
	{
	
	}
}

Todas las Interfaces deben comenzar su nombre por I.

public interface IEjemplo
{

}

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

Tiene más que ver con el despliegue. Establece los perfiles con los que ejecutaremos nuestro proyecto. Configura la forma en la que se inicia la aplicación durante el desarrollo y contiene valores que NO DEBEN EXPONERSE EN PROD.

No se utiliza en entornos de producció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

commandName especifica el servidor web que se va a iniciar

  • IISExpress inicia IIS Express
  • Project inicia Kestrel

appsettings

Se utiliza para almacenar la configuración de la aplicación, como 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
	}
}

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));
	// ...
}

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