Azure Functions (Serverless) + Entity Framework Core y SQL Azure
Tags: dotnet, sql, efcore, azure
Published: 2020-05-16 22:48:39

Azure Functions es una solución Serverless en la nube de Microsoft Azure muy útil y práctica de usar en la actualidad. Por otro lado desarrollar una Function que requiere funcionalidades relacionadas al acceso a datos es obvio que los desarrolladores .NET nos preguntamos en algún momento ¿puedo usar mi ORM favorito Entity Framework Core para acceder a los datos de una Base de Datos SQL Server o SQL Azure desde una Azure Function? 
Obviamente la respuesta es Sí, claro que es posible, Microsoft anunció en el Build del año pasado la compatibilidad entre la inserción de dependencias en Azure Functions, esto significa que puede registrarse y utilizar sus propios servicios como parte de las funciones. 
Aunque antes de esto ha sido posible utilizar Entity Framework Core en el pasado, la diferencia es que ahora el emparejamiento con la inserción de dependencias ha sido convertido a un ajuste mucho más natural. 
Obviamente esto se vuelve más interesante con la llegada de Azure Functions V3 que desde Enero de este año está disponible de manera general, esto significa que ahora es posible compilar e implementar funciones con la versión en tiempo de ejecución 3.0 en producción. Esta nueva versión de Azure Functions ofrece nuevas capacidades, incluida la capacidad de tener como destino .NET Core 3.1 
También es altamente compatible con versiones anteriores, por lo que la mayoría de las aplicaciones existentes que se ejecutan en versiones anteriores deben ser capaces de actualizar a la versión 3 y ejecutarse sin ningún cambio en el código.

## Que abordaremos en este post
>- Creación de una Azure Function V3 localmente
>- Integración de EF Core con Azure Function
>- Inyección de Dependencias
>- Migraciones con EF Core y Actualización de Base de Datos
>- Acceder a datos alojados en SQL Azure

## Requisitos
>- Suscripción de Azure, si no tienes una obtiene una aquí https://azure.microsoft.com/es-es/free/
>- DotNet Core 3.1 SDK
>- Visual Studio 2019
>- Azure Function Core Tools Versión 3

# Creando Azure Function
Vamos a crear un proyecto Azure Functions V3 desde Visual Studio 2019 para poder ejecutarla localmente desde nuestra máquina de desarrollo.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/fircqafmpgyf41kjleci.PNG)
* * *
Seguidamente vamos a crear la estructura de directorios en la solución.
Quedaría de la siguiente manera:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/6me8lf5ug6mpj5qq05wv.PNG)
>**Nota:** La plantilla de una solución de Azure Function en Visual Studio crea por defecto una clase llamada Function1.cs modificaremos ese nombre por FunctionEFCore.cs, también puedes notar que existe una clase Startup.cs pero no te preocupes explicaré el porqué existe esta clase en esta solución.

# Integración de EF Core con Azure Function
Debemos instalar los paquetes Nuget necesarios para utilizar Entity Framework Core.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/cjx291g9z6w48dvxz7j2.PNG)
> **Nota:** Microsoft.EntityFrameworkCore.SqlServer es el proveedor de base de datos de Microsoft SQL Server para Entity Framework Core y permite la manipulación de bases de datos mediante el ORM.
Microsoft.EntityFramewrokCore.Tools contiene las herramientas principales de Entity Framework para la consola del Administrador de paquetes NuGet en Visual Studio y habilita los comandos de uso común como: 
- Add-Migration 
- Drop-Database 
- Get-DbContext 
- Scaffold-DbContext 
- Script-Migrations 
- Update-Database

Ahora crearemos los modelos, utilizaré los modelos de Album y Artista que he utilizado en posts anteriores, si has leído alguno seguramente se te harán familiares.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/kng9wz6np3hsgfm29uru.PNG)
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/i8k8cqbqcug07padpude.PNG)
* * *
Escribiremos el contexto de datos y agregaremos los modelos correspondientes como propiedades DbSet.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/hbuns71rnswvi7po9hs4.PNG)
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/54tfsa9e90cvwml7tdl7.PNG)
> **Nota:** En este contexto hice un override del método OnModelCreating() para poder crear datos iniciales al momento de crear las migraciones, ejecutar el comando Update-Database y sincronizar la base de datos SQL Azure. Esto creará las tablas en la BD y seguidamente realizará múltiples insert para crear registros.

Previo a correr los comandos para crear las migraciones y hacer un update a nuestra base de datos habilitaremos la creación del DbContext en tiempo de diseño, ya que necesitamos crear una instancia derivada de DbContext. 
Para esto implementaremos la interface IDesignTimeDbContextFactory para habilitar los servicios en tiempo de diseño y para este caso en particular las migraciones. Esto aplica únicamente para los tipos de contexto que no tienen un constructor predeterminado público, recordemos que esta no es una solución ASP.NET por eso es necesario. 
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/mcoqkhsd7dr2rge8w62t.PNG)
***
Seguidamente un paso muy importante es obtener el .dll de la solución en el lugar correcto. Al compilar un proyecto de Azure Functions se organizan algunos artefactos de compilación para crear una estructura de proyecto de function válida, en este proceso el .dll de la solución se mueve a una subcarpeta. Desafortunadamente para las herramientas en tiempo de diseño como las migraciones, esperan que el archivo .dll esté en la raíz del destino de compilación pero esto no sucede así y para resolverlo tendremos que hacer una copia del .dll de la solución a la raíz del proyecto. 
Vamos a modificar el archivo .csproj y agregaremos un evento posterior a la compilación para copiar el archivo .dll en la raíz de la solución.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/jdlztilxp6d53s30g1as.PNG)
* * *
#Inyección de Dependencias
Para hacer uso de Inyección de Dependencias (DI) en Azure Function al igual que en una solución de ASP.NET necesitamos acceder al Contenedor de Inversión de control para lograr una inicialización de nuestros servicios.
La plantilla de una solución Azure Function en Visual Studio no incluye una clase Startup.cs por lo que debemos agregarla.
Si no sabes que es Inyección de Dependencias o quieres conocer más detalles y como funciona este patrón de diseño de software te recomiendo leas este post.
{% link https://dev.to/ebarrioscode/inyeccion-de-dependencias-di-en-asp-net-core-mejores-practicas-para-escribir-codigo-reutilizable-escalable-y-desacoplado-kho %}
* * *
Aquí es donde sucede la magia de Inyección de Dependencias. La clase Startup.cs configura los servicios y la canalización de solicitudes de la aplicación además de controlar el Contenedor de Inversión de Controles (IoC) encargado de proveer las instancias de los tipos los cuales le decimos en el inicio de la Aplicación (Startup).
En este punto le dejaremos la responsabilidad de instanciar e inicializar nuestras dependencias tales como el Contexto de Datos y los diferentes servicios específicos de cada tipo de objeto que necesitemos a lo largo del desarrollo de este software.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/2l1a1b74q76pyu1txkbd.PNG)
> **Nota:** 
 - El Método Configure(IFunctionsHostBuilder builder) es donde registramos todos los servicios. IFunctionHostBuilder.Services es el contenedor IoC.
 - Registrar servicios es exactamente lo mismo que con ASP.NET Core, mediante el uso de los métodos AddScoped, AddSingleton, AddTransient.
 - La clase Startup debe extender de FunctionsStartup.

Necesitaremos un Servicio de Albumes el cuál tendrá delegadas todas las responsabilidades de manipulación de datos que tengan que ver con objetos tipo Album, este servicio utiliza como dependencia el DbContext, solo debemos preocuparnos por inyectarlo, recordemos que IoC se encargará de instanciarlo e inicializarlo.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/1zlovh3sjmx8ldu7pgvs.PNG)
* * *
### Base de Datos SQL Azure
Necesitamos una base de datos de Azure, para esta práctica ya tengo creada una BD en Azure.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/jjhfjf1xrtt7rifujep7.PNG)
* * *
# Migraciones con EF Core y Actualización de Base de Datos
Antes de iniciar con los comandos para correr las migraciones y hacer el update a la base de datos remota alojada en Azure, debemos establecer una cadena de conexión que sea accesible en tiempo de diseño. Para esto utilizaremos una variable de entorno y correremos el siguiente comando en la consola de administración de paquetes nuget de Visual Studio.
> $env:SqlConnectionString="Server={Nombre o IP del Servidor};Initial Catalog={Nombre de la BD};Persist Security Info=False;UserID={UserSQL};Password={Password};MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;"

Ahora debemos correr el comando Add-Migration [Nombre_de_la_migracion] yo le llamaré Initial_Model.
Esta migración creará las instrucciones necesarias para crear las tablas en la bd con base en los modelos que tenemos registrados en el Contexto de Datos y posteriormente insertar datos iniciales, a esta técnica se le denomina Code First.
Tras una ejecución con éxito podremos notar que se agregó un directorio llamado Migrations en la la raíz del proyecto, este directorio contiene las migraciones creadas representadas como clases C#.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/henwa8dxk6dee5g5mko7.PNG)
* * *
Debemos utilizar la migración que acabamos de crear para que los cambios se vean reflejados en la bd, para eso correremos el comando Update-Database.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/tqrfm5va4e5w565hovqd.PNG)
* * *
Para ver los cambios reflejados en la bd vamos a conectarnos mediante Azure Data Studio, únicamente necesitamos el nombre del servidor, un usuario y contraseña para acceder, esto lo encuentras en la administración de la base de datos en el portal de Azure o puedes revisar la cadena de conexión que utilizamos al setear la variable de entorno y que obviamente se obtuvo en el portal de Azure.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/itp1gn0xk85q8xm2bhxl.PNG)
> Ya podemos ver las tablas creadas en la base de datos y corresponden a los modelos Album y Artista establecidos en nuestra solución.

Ejecutamos dos instrucciones SQL para verificar la existencia de datos que definimos en el método OnModelCreating() en la clase Contexto de Datos.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/yo754t5mb5779xvuhwud.PNG)
* * *
# Acceder a datos alojados en SQL Azure
Ahora lo que debemos hacer es modificar el código en la clase principal FunctionEFCore.cs para utilizar el servicio de Albumes pero para utilizar ese servicio debemos inyectar la dependencia en el método constructor de la clase FunctionEFCore.cs. 
Eliminamos el código que crea por defecto la plantilla, eliminamos el static de la clase y el método, invocamos el método GetAsync del Servicio de Albumes y le enviamos ese objeto resultado a una nueva instancia de OkObjectResult que será el resultado a retornar de nuestra Azure Function.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/usw9n5fksc6hykcnv57b.PNG)
* * *
Probaremos localmente nuestra function, posterior a compilar y ejecutar esta solución, vamos a PostMan y enviamos una solicitud Http GET.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/8ikcz1y9z1xh6f5785mu.PNG)
> Funciona correctamente, el resultado es el esperado.

**Conclusión** 
Esta nueva característica de inserción de dependencias en Azure Functions facilita el trabajo con contextos de base de datos de Entity Framework Core, esto nos da la facilidad de montar un backend en un ambiente de development, test, stage, o production en poco tiempo basándonos en las soluciones Serverless.

**Link al Repositorio en Github**
https://github.com/EbarriosCode/AzureFunctionV3_EfCore.git

**Referencias**
https://docs.microsoft.com/en-us/azure/azure-functions/
https://devblogs.microsoft.com/dotnet/announcing-ef-core-3-0-and-ef-6-3-general-availability/
https://docs.microsoft.com/en-us/ef/core/
https://docs.microsoft.com/en-us/azure/azure-functions/functions-dotnet-dependency-injection