tag:blogger.com,1999:blog-79845771237412991272024-02-08T06:19:35.068+01:00SergiNetDevOps y mucho más. El blog de Sergio Navarro PinoSergio Navarro Pinohttp://www.blogger.com/profile/15840814836353973005noreply@blogger.comBlogger37125tag:blogger.com,1999:blog-7984577123741299127.post-83988025216588017572023-03-20T16:29:00.005+01:002023-03-20T16:40:09.412+01:00Gestionando la configuración de nuestras aplicaciones (2/x) <p>
</p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;">A raíz de
la entrada anterior, surgió una interesante conversación en twitter (algo que
me resultó muy satisfactorio, pues es uno de mis objetivos al escribir es
generar debate):</p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;"> </p>
<p style="margin: 0in;"><img alt="Javier Campos @javiercampos • 7 mar.
En respuesta a @javiercampos y @snavarropino
En mi caso, estamos migrando a un proveedor de "configuración de
aplicación" que lee de un datastore común a todos los procesos, y lo que
se configura en "el proceso" (appsettings/entorno) es ese proveedor de
configuración
11,1 19
Javier Campos @javiercampos • 7 mar.
En respuesta a @javiercampos y @snavarropino
Y si, se puede implementar ese proveedor fácilmente para IConfiguration,
pero no hay una forma fácil para tener un IConfiguration que configure
ese proveedor (se puede hacer de otros modos)
11,1 22
Javier Campos @javiercampos • 7 mar.
En respuesta a @javiercampos y @snavarropino
Es decir, si tu haces un proveedor que lea (un key/valor, sencillo) desde
una BD y Io meta en IConfiguration (igual que se leen los appsettings o el
entorno), donde configuras la cadena de conexión -a ese- proveedor? Se
puede hacer, pero no es amigable (en .NET al menos)
Javier Campos @javiercampos • 7 mar.
En respuesta a @javiercampos y @snavarropino
(básicamente, el método es hacer un proveedor de 'Configuration para
leer esa cadena de conexión -puede ser appsettings/entorno-, crear la
configuración, leer la conexión [un IOptions o similar], y luego crear -otro
'Configuration con tu proveedor metido)" height="677" src="data:image/png;base64,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" width="627" /></p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;"> </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;">Javier
exponía que en uno de sus proyectos, estaban tratando de hacer lo siguiente:</p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;"> </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;">Configurar
tu proceso, mediante mecanismos tipos fichero json o variable de entorno, para
indicar de donde tiene que leer la configuración necesaria para ejecutarse.</p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;"> </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;">Si he
entendido bien, Javier pretende definir, en tienpo de ejecución, el proveedor
del que se van a leer los valores de configuración que tu aplicación necesita.</p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;"> </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;">Me
sorprende, porque realmente no es necesario en .NET, que dispone de un <a href="https://learn.microsoft.com/en-us/dotnet/core/extensions/configuration">mecanismo de configuración</a> en el que configuramos uno o más proveedores para que resuelvan la
configuración. Estos proveedores están priorizados, de modo que si se resuelven
varios, uno de ellos "mandará".</p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;"> </p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;">
</p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;">Por
lo tanto, no es necesario configurar en tiempo de ejecución el proveedor que
quieres, solo has de dejar todos preparados, y en cada entorno configurar como
quieres. Por ejemplo, puedes preparar la siguiente configuración:</p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;"> </p>
<ol style="direction: ltr; font-family: Verdana; font-size: 12pt; font-style: normal; font-weight: normal; margin-bottom: 0in; margin-top: 0in; unicode-bidi: embed;" type="1"><li lang="en-US" style="margin-bottom: 0px; margin-top: 0px; vertical-align: middle;" value="1"><span style="font-family: Verdana; font-size: 12pt; font-style: normal; font-weight: normal;">Fichero
json appSettings.json</span></li><li lang="en-US" style="margin-bottom: 0px; margin-top: 0px; vertical-align: middle;"><span style="font-family: Verdana; font-size: 12pt;">Fichero json específico de un
entorno: appSettings.Production.json o appSettings.Staging.json por
ejemplo</span></li><li lang="en-US" style="margin-bottom: 0px; margin-top: 0px; vertical-align: middle;"><span style="font-family: Verdana; font-size: 12pt;">Variables de entorno</span></li></ol>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;"> </p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;">Esto se hace así:</p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;"> </p>
<script src="https://gist.github.com/snavarropino/a086fe55e4bace87a6d4db92b18d1575.js"></script>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;"> </p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;"> </p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;">En el fichero <span style="font-family: Verdana; font-size: 12pt; font-style: normal; font-weight: normal;">appSettings.json estarán los valores comunes, y por ejemplo, puedo poner en el entorno de staging un fichero </span><span style="font-family: Verdana; font-size: 12pt;">appSettings.Staging.json que los sobrescriba, prevaleciendo estos (si te fijas en el código, cuanto más abajo el proveedor tendrá más "peso").</span></p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;"><span style="font-family: Verdana; font-size: 12pt;"><br /></span></p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;"><span style="font-family: Verdana; font-size: 12pt;">Y finalmente, para producción (imaginad que esto se ejecuta en un kubernetes) podría setearía variables de entorno por cada par clave/valor del fichero json, y estas variables de entorno prevalecerían sobre los valores del fichero.</span></p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;"><span style="font-family: Verdana; font-size: 12pt;"> </span></p><h3 lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"><span style="font-family: Verdana; font-size: 12pt;">¿Cómo le decimos a la aplicación en que entorno está?</span></h3><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;"><span style="font-family: Verdana; font-size: 12pt;"> </span></p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;"><span style="font-family: Verdana; font-size: 12pt;">Por defecto estará en Producción, y setearemos otros entorno mediante una variable de entorno (valga la redundancia), que tradicionalmente ha sido </span><code>ASPNETCORE_ENVIRONMENT</code>.</p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;"> </p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;">Sin embargo hay otros modos, como la variable DOTNET_ENVIRONMENT o el parámetro en line de comandos que podemos pasar a dotnet run:</p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;"> </p><pre class="has-inner-focus" tabindex="0"><code class="lang-dotnetcli" data-author-content="dotnet run --environment Production
"><span><span style="font-size: medium;"><span class="hljs-keyword">dotnet</span> <span class="hljs-keyword">run</span><span class="hljs-parameter"> --environment</span> <entorno></span></span></code></pre><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;"> </p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;"><span style="font-family: Verdana; font-size: 12pt;">Todo lo tenéis <a href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/environments?view=aspnetcore-7.0">aquí</a> detallado.<br /></span></p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;"><span style="font-family: Verdana; font-size: 12pt;"><br /></span></p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;"><span style="font-family: Verdana; font-size: 12pt;">Sin duda algo se me escapa... espero que Javier me lo aclare. Mientras tanto seguiré preparando la siguiente entrada del blog. Os dejo una pregunta para abrir boca:</span></p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;"><span style="font-family: Verdana; font-size: 12pt;"><br /></span></p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;"><span style="font-family: Verdana; font-size: 12pt;">Si tenemos microservicios o simplemente un sistema distribuido, ¿cómo configuramos valores que deben estar presentes en varios servicios (por ejemplo la url del proveedor de identidad)? ¿Repetimos la configuración en cada servicio?</span></p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;"><span style="font-family: Verdana; font-size: 12pt;"><br /></span></p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;"><span style="font-family: Verdana; font-size: 12pt;">Veremos como evitarlo en la siguiente entrada sobre </span><span style="font-family: Verdana; font-size: 12pt;">configuración centralizada</span></p>
Sergio Navarro Pinohttp://www.blogger.com/profile/15840814836353973005noreply@blogger.com028939 Arroyomolinos, Madrid, Spain40.2783308 -3.921554111.968096963821154 -39.0778041 68.588564636178845 31.2346959tag:blogger.com,1999:blog-7984577123741299127.post-88833205688597250202023-03-06T21:28:00.004+01:002023-03-06T21:30:52.910+01:00Gestionando la configuración de nuestras aplicaciones (1/x)<p style="text-align: center;"> <img alt="What is JSON? The most important questions explained simply" class="n3VNCb pT0Scc KAlRDb" data-noaft="1" height="167" src="https://www.opc-router.de/wp-content/uploads/2020/08/what-is-json_600x250px.jpg" style="height: 250px; margin: 7.45px 0px; width: 600px;" width="400" />
</p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;">Nuestras
aplicaciones requieren valores de configuración para funcionar correctamente en
cada uno de los entornos disponibles (producción, staging, etc…).</p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;"> </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;">Estas
configuraciones pueden ser clasificadas en diferentes tipos, vamos a verlo con
un ejemplo. Imaginad una aplicación que tiene que hacer una llamada a un API
tercero, un sistema de compra de tickets por ejemplo. Vamos a necesitar tres
variables de configuración:</p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </p>
<ul style="margin-bottom: 0in; margin-top: 0in; text-align: left; unicode-bidi: embed;" type="disc"><li lang="en-US" style="margin-bottom: 0px; margin-top: 0px; vertical-align: middle;"><span style="font-family: Verdana; font-size: 12pt;">La url del servicio</span></li><li lang="en-US" style="margin-bottom: 0px; margin-top: 0px; vertical-align: middle;"><span style="font-family: Verdana; font-size: 12pt;">El tiempo que esperaremos por
la respuesta del servicio antes de asumir que falla y actuar en
consecuencia</span></li><li lang="en-US" style="margin-bottom: 0px; margin-top: 0px; vertical-align: middle;"><span style="font-family: Verdana; font-size: 12pt;">El API key necesario para
invocar el servicio</span></li></ul>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;">Si nos
paramos a analizar las tres variables, vamos a ver que podemos clasificarlas en
tres tipos bien diferenciados:</p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </p>
<ul style="margin-bottom: 0in; margin-top: 0in; text-align: left; unicode-bidi: embed;" type="disc"><li lang="en-US" style="margin-bottom: 0px; margin-top: 0px; vertical-align: middle;"><span style="font-family: Verdana; font-size: 12pt;">Valores de configuración que </span><span style="font-family: Verdana; font-size: 12pt; font-weight: bold;">no dependen
del entorno</span><span style="font-family: Verdana; font-size: 12pt;">
(tiempo que esperaremos por la respuesta del servicio)</span></li></ul>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in 0in 0in 0.375in; text-align: left;"> </p>
<ul style="margin-bottom: 0in; margin-top: 0in; text-align: left; unicode-bidi: embed;" type="disc"><li lang="en-US" style="margin-bottom: 0px; margin-top: 0px; vertical-align: middle;"><span style="font-family: Verdana; font-size: 12pt;">Valores de configuración que </span><span style="font-family: Verdana; font-size: 12pt; font-weight: bold;">si dependen
del entorno</span><span style="font-family: Verdana; font-size: 12pt;">,
como la url del servicio, que podría variar para hacer pruebas (apuntando
a un entorno no productivo).</span></li></ul>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in 0in 0in 0.375in; text-align: left;"> </p>
<ul style="margin-bottom: 0in; margin-top: 0in; text-align: left; unicode-bidi: embed;" type="disc"><li lang="en-US" style="margin-bottom: 0px; margin-top: 0px; vertical-align: middle;"><span style="font-family: Verdana; font-size: 12pt; font-weight: bold;">Secretos</span><span style="font-family: Verdana; font-size: 12pt;">, como el API key, que
debemos proteger y no deben estar guardados en claro en ficheros.</span></li></ul><p style="text-align: left;">
</p><br /><h1 style="text-align: left;">¿Cómo gestionamos estas variables? </h1><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;">Si
hablamos de .NET (empezando en la primera versión de NET core) podríamos pensar
en ficheros de configuración json, que de un modo sencillo nos permitirán dar
valores para cada entorno:</p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </p>
<ul style="margin-bottom: 0in; margin-top: 0in; text-align: left; unicode-bidi: embed;" type="disc"><li lang="en-US" style="margin-bottom: 0px; margin-top: 0px; vertical-align: middle;"><span style="font-family: Verdana; font-size: 12pt;">appsetting.json</span></li><li lang="en-US" style="margin-bottom: 0px; margin-top: 0px; vertical-align: middle;"><span style="font-family: Verdana; font-size: 12pt;">appsettings.<environmentName>.json</span></li></ul>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in 0in 0in 0.375in; text-align: left;"> </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: center;"> <br /></p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"></p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;">En el
primer fichero estarán todas las variables de configuración. Además podemos
crear ficheros adicionales que sobrescriban los valores para cada entorno
deseado: <span style="font-style: italic;">appsettings.Staging.json</span> o <span style="font-style: italic;">appsettings.Production.json</span> por ejemplo.</p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;">¿Cómo se
fija en que entorno estamos?</p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;">Por
defecto estaremos en el entorno Production. Podemos fijar otro entorno seteando
una de las siguientes variebles de entorno:</p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </p>
<ul style="margin-bottom: 0in; margin-top: 0in; text-align: left; unicode-bidi: embed;" type="disc"><li lang="en-US" style="margin-bottom: 0px; margin-top: 0px; vertical-align: middle;"><span style="font-family: Verdana; font-size: 12pt;">DOTNET_ENVIRONMENT</span></li><li lang="en-US" style="margin-bottom: 0px; margin-top: 0px; vertical-align: middle;"><span style="font-family: Verdana; font-size: 12pt;">ASPNETCORE_ENVIRONMENT</span></li></ul>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;">Dependiendo
de tipo de aplicación y la versión de .NET debemos usar una u otra, no es el
objetivo de este post entrar a verlo en detalle, aquí os dejo la documentación
oficial: <a href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/environments?view=aspnetcore-7.0">https://learn.microsoft.com/en-us/aspnet/core/fundamentals/environments?view=aspnetcore-7.0</a></p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"></p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;">Con lo
anterior, resolvemos de un plumazo la configuración por cada entorno,
cumpliendo además el tercer factor promulgado <a href="https://12factor.net/">por 12-factor app</a>, que indica que la
configuración debe guardarse en el entorno:Store
config in the environment.</p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;">Pero aún
tenemos un problema: los secretos</p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </p>
<h1 style="text-align: left;"><span style="font-family: Verdana; font-size: 12pt;"></span>¿Cómo gestionamos los secretos?</h1><div style="text-align: left;">
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;"> </p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;">Desde
luego <span style="font-weight: bold;">no pueden estar en ficheros de
configuración</span> que guardemos en nuestro repositorio de código fuente.
¿Imagináis una cadena de conexión de Azure Storage publicada en GitHub? Los
malos ya tendrían su Cloud drive infinito, a tu costa :-(</p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;"> </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;">Podríamos
tener las variables en los ficheros sin valores reales, y reemplazar sus
valores en tiempo de despliegue en nuestra herramienta de CI/CD (Github Actions
o Azure DevOps son mis preferidas), es algo que se ha venido haciendo mucho.
Sin embargo, aunque las herramientas de CI/CD pueden guardar secretos, carecen
de la seguridad y las características avanzadas que proporcionan herramientas
específicas como Azure Key Vault o HashiCorp Vault.<span style="mso-spacerun: yes;"> </span>Por dar una pincelada, rotar un secreto con
Azure Key Vault no es tan doloroso como hacerlo con secretos de la herramienta
CI/CD.</p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;"> </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;">Además,
no nos engañemos: guardar un secreto en Azure Pipelines, y aplicarlo al fichero
de configuración al desplegar hace que el secreto acabe guardado en claro un
archivo.</p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;"> </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;">Espero
que os haya resultado de interés, continuaré profundizando en la configuración,
con otro artículo en el que usaremos dos herramientas para gestionar la
configuración de nuestras aplicaciones de un modo centralizado: Consul y Azure
App configuration.</p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;"> </p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in;">¡Hasta la próxima! <br /></p>
</div>Sergio Navarro Pinohttp://www.blogger.com/profile/15840814836353973005noreply@blogger.com028939 Arroyomolinos, Madrid, Spain40.2783308 -3.921554111.968096963821154 -39.0778041 68.588564636178845 31.2346959tag:blogger.com,1999:blog-7984577123741299127.post-31326899007823494222022-12-28T18:47:00.006+01:002022-12-28T19:29:00.211+01:00Trucos Git: "Commitea" con el usuario correcto<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;">¿Trabajas
con varias cuentas de Git en diferentes proveedores? ¿Quizás GitHub, Azure
DevOps, Bitbucket u otros?</p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;">Si es así
seguro que te ha pasado algo muy molesto: haces los commits con el usuario
erróneo. Esto sucede porque normalmente el nombre y el mail con el que hacemos
nuestros commits se establece de forma global (al instalar Git en
el equipo por ejemplo) y se usa con todos los commit, esté donde esté el repositorio remoto. </p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;">Estos
datos nada tienen que ver con la autenticación contra tu proveedor, así
que aunque mi usuario en Azure DevOps fuese sergio@<b>midominio</b>.com, yo podría commitear como sergio@<b>otrodominio</b>.com</p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;">Vamos a verlo con un ejemplo</p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"><br /></p><p></p><h2 style="text-align: left;">Commiteando con el usuario equivocado</h2><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"></p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;">Voy a
clonarme un repositorio que tengo en GitHub:</p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"><br /></p><p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;"><span lang="es"><span> </span><span> </span><span> </span><span style="font-size: small;">git
clone <a href="https://github.com/snavarropino/CacheSleeve.git">https://github.com/snavarropino/CacheSleeve.git</a> </span><br /></span></p><span lang="es"></span><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"></p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;">
</p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;">Una vez
hecho esto, voy a setear mi configuración de Git, eligiendo un usuario erróneo:</p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;"> </p><div style="margin-left: 40px; text-align: left;"><span style="font-family: Calibri;"><span style="font-size: small;"><span lang="es">git
config --global</span><span lang="en-US"> user.name "wrong user"</span>
<span style="font-size: small;"><br /><span lang="es">git
config --global</span><span lang="en-US"> user.email
"sergio@wrongdomain.com"</span>
</span></span></span></div><div style="text-align: left;"><span style="font-size: small;"><span style="font-size: small;"><p lang="en-US" style="font-size: 12pt; margin: 0in; text-align: left;"><span style="font-family: Calibri;"> </span></p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;">Y
seguidamente haré un commit y lo subiré al repo</p></span></span></div><div style="margin-left: 40px; text-align: left;"><span style="font-size: small;"><span style="font-size: small;"><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </p></span></span></div><div style="margin-left: 40px; text-align: left;"><span style="font-family: Calibri;"><span><span style="font-size: small;"><span style="font-size: small;"><div style="text-align: left;">echo hi > mychange.txt</div><div style="text-align: left;">git add .\mychange.txt</div><div style="text-align: left;">git commit -m "Added my change"</div><div style="text-align: left;">git push </div></span></span></span></span></div><div style="margin-left: 40px; text-align: left;"><span style="font-size: small;"><span style="font-size: small;"><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </p></span></span></div><div style="text-align: left;"><span style="font-size: small;"><span style="font-size: small;"><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;">Si
revisamos la historia, tanto en local como en remotlo, vamos a ver que el
commit queda "atado" a ese "wrong user". ¿Un poco loco
verdad?</p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyzRUnkJxFjzIa6aR8SKFkMjJkpxJH_joNMU0f6a5NE26tbrAFqNerUI4yNqe00idTrOUZJOPVMPkgAEm7eK3VnW0mdf8AJpvKdRvhgWCRNixHgoRBEJbBDkmoi_MKeDgmiApYCStrBE_qmUmbsYUsyz47p7vGgFXskE-m6O4BNL_ZopYjP34TMVYYCg/s851/localcommit.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="112" data-original-width="851" height="85" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyzRUnkJxFjzIa6aR8SKFkMjJkpxJH_joNMU0f6a5NE26tbrAFqNerUI4yNqe00idTrOUZJOPVMPkgAEm7eK3VnW0mdf8AJpvKdRvhgWCRNixHgoRBEJbBDkmoi_MKeDgmiApYCStrBE_qmUmbsYUsyz47p7vGgFXskE-m6O4BNL_ZopYjP34TMVYYCg/w640-h85/localcommit.png" width="640" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhC0EUxUscSAI5yUr9pT_EXcTLtLX8GYOeCSU-kUdpEJedO7nz68VGsOwhcgKwgkCjnTOaNY6jW56V9Sq9AAVtm5EGyI1b1KnOMaKLCu4G-B7YYBqlr7dAVxsrewmp-JWYdkHywdFKEtvhW6Exi4l1_4I5SpF4gAUJGxbF4neAwBiJSkm3te0FRQzse-Q/s897/githubcommit.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="439" data-original-width="897" height="313" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhC0EUxUscSAI5yUr9pT_EXcTLtLX8GYOeCSU-kUdpEJedO7nz68VGsOwhcgKwgkCjnTOaNY6jW56V9Sq9AAVtm5EGyI1b1KnOMaKLCu4G-B7YYBqlr7dAVxsrewmp-JWYdkHywdFKEtvhW6Exi4l1_4I5SpF4gAUJGxbF4neAwBiJSkm3te0FRQzse-Q/w640-h313/githubcommit.png" width="640" /></a></div><br /> <br /><p></p></span></span><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;">Aquí he forzado el error, pero lo que suele pasar es que estableces la configuración para commitear por ejemplo en Azure Devops
(trabajo), luego te clonas un repo de GitHub, y al commitear va la info
del trabajo en vez de la de tu cuenta personal en GitHub... <br /></p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;">¡Me pasa
constantemente! Vamos a solucionarlo.</p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </p><h2>Forzando que cada repo tenga configuración local</h2><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;">Desde git
versión 2.8 (ha llovido ya) tenemos la opción de forzar que deban configurarse usuario y email
en cada repo, de modo local a este.</p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;">Esto lo
conseguimos así</p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </p>
<div lang="en-US" style="margin: 0in 0in 0in 40px; text-align: left;"><span style="font-family: Calibri; font-size: small;"><span><span>git
config --global --unset-all user.email<br />
git config --global --add user.useConfigOnly true</span></span></span></div>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;">Una vez
hecho esto, si en un repositorio en el que no hemos configurado localmente
hacemos un commit, obtendremos un error. Ya no se usará más la configuración
global.</p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcoSf2CPZWIm9fra173dxTFP_eWnshxA5cC_lgSyuxg49M_9UlE5i7TWzWKMhlOICVavlPhk-C1E-PTP8gDFyBvgZrOGDEeKSyQT-tF-rkOxqRG4huFmBcCodvcKwDrAnun4Sof9ifn2HL-nw-7I5G2hWrS_08dFi3QMPVwg20HzNxPDK_1FwYpH9-vg/s905/commit%20error.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="336" data-original-width="905" height="238" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcoSf2CPZWIm9fra173dxTFP_eWnshxA5cC_lgSyuxg49M_9UlE5i7TWzWKMhlOICVavlPhk-C1E-PTP8gDFyBvgZrOGDEeKSyQT-tF-rkOxqRG4huFmBcCodvcKwDrAnun4Sof9ifn2HL-nw-7I5G2hWrS_08dFi3QMPVwg20HzNxPDK_1FwYpH9-vg/w640-h238/commit%20error.png" width="640" /></a></div><br /> <br /><p></p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;">Ahora tendremos que configurar el nombre y el email apropiado para cada repo, es más tedioso pero evitamos errores. ¿Cómo lo hacemos?</p><p lang="en-US" style="margin: 0in; text-align: left;"><span style="font-size: small;"><span style="font-family: Calibri;"><br /></span></span></p><p lang="en-US" style="margin: 0in; text-align: left;">
</p><p style="margin: 0in 0in 0in 40px; text-align: left;"><span style="font-size: small;"><span style="font-family: Calibri;">git config --local
user.email sergio.navarropino@gmail.com <br /></span></span></p><div style="margin-left: 40px; text-align: left;">
</div><p style="margin: 0in 0in 0in 40px; text-align: left;"><span style="font-size: small;"><span style="font-family: Calibri;">git config --local
user.name "Sergio Navarro Pino"</span></span></p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </p><span style="font-size: small;"><span style="font-size: small;"><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;">Espero que os resulte útil y que evitéis esos molestos errores</p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;">Saludos! </p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"><br /></p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;">PD: Jose María Flores Zazo ya escribió sobre este tema, el lo resuelve con GitKraken, interesante!<br /><br /><a href="https://jmfloreszazo.com/git-mejores-practicas-informacion-del-usuario">https://jmfloreszazo.com/git-mejores-practicas-informacion-del-usuario</a><br /></p></span></span></div>Sergio Navarro Pinohttp://www.blogger.com/profile/15840814836353973005noreply@blogger.com028939 Arroyomolinos, Madrid, Spain40.2783308 -3.921554111.968096963821154 -39.0778041 68.588564636178845 31.2346959tag:blogger.com,1999:blog-7984577123741299127.post-72801077449575720262022-03-23T18:59:00.011+01:002022-03-23T19:03:11.972+01:00Jekyll, Hugo, static site generators y otras petisoperias<br />
<div lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;">
<span style="font-family: verdana;">El los
últimos meses hemos visto como varios bloggers han cambiado de plataforma, bien
saliendo de plataformas hospedadas por terceros (<a href="https://www.blogger.com/" target="">blogger</a> por ejemplo), bien de
instalaciones Wordpress hosteadas por ellos mismos.</span></div><span style="font-family: verdana;">
</span><div lang="es" style="font-size: 11pt; margin: 0in;">
<span style="font-family: verdana;"><br /></span></div><span style="font-family: verdana;">
</span><div lang="es" style="font-size: 11pt; margin: 0in;"><span style="font-family: verdana;">
Los motivos
son varios, pero podríamos resumirlos en tres:</span></div><span style="font-family: verdana;">
</span><div lang="es" style="font-size: 11pt; margin: 0in;">
<span style="font-family: verdana;"><br /></span></div><span style="font-family: verdana;">
</span><ul style="direction: ltr; margin-bottom: 0in; margin-left: 0.375in; margin-top: 0in; unicode-bidi: embed;" type="disc">
<li style="margin-bottom: 0px; margin-top: 0px; vertical-align: middle;"><span style="font-family: verdana;"><span lang="es" style="font-size: 11pt;">Abandono de la
plataforma, algo que sucede en gran medida con <a href="https://www.blogger.com/" target="">blogger</a></span><span lang="es" style="font-size: 11pt;">. Da la sensación de
ser un proyecto que Google mantiene al ralentí, y ya sabemos que Google no
duda un momento en cerrar servicios.</span></span></li>
</ul><span style="font-family: verdana;">
</span><div lang="es" style="font-size: 11pt; margin-left: .375in; margin: 0in;">
<span style="font-family: verdana;"><br /></span></div><span style="font-family: verdana;">
</span><ul style="direction: ltr; margin-bottom: 0in; margin-left: 0.375in; margin-top: 0in; unicode-bidi: embed;" type="disc">
<li lang="es" style="margin-bottom: 0px; margin-top: 0px; vertical-align: middle;"><span style="font-family: verdana;"><span style="font-size: 11pt;">Propiedad de los contenidos:
¿Qué pasa si una plataforma cierra de un día para otro? ¿recuperaré mis
contenidos? Mentiría si dijese que tengo un backup de todas mis entradas.
En general el contenido lo tengo, pero sin formato, quizás sin imágenes,
etc… ¡sería un desastre! <br /><br /> Además podríamos hablar (no lo haré ahora) del "efecto Medium", una plataforma
que cambió su modelo de negocio, pasando a cobrar a los lectores.</span></span></li>
</ul><span style="font-family: verdana;">
</span><div lang="es" style="font-size: 11pt; margin-left: .375in; margin: 0in;">
<span style="font-family: verdana;"><br /></span></div><span style="font-family: verdana;">
</span><ul style="direction: ltr; margin-bottom: 0in; margin-left: 0.375in; margin-top: 0in; unicode-bidi: embed;" type="disc">
<li lang="es" style="margin-bottom: 0px; margin-top: 0px; vertical-align: middle;"><span style="font-family: verdana;"><span style="font-size: 11pt;">Dificultad de mantenimiento:
esto aplica principalmente a Wordpress. Nunca he hecho nada serio con él,
pero si he escuchado quejas al respecto, y no pocas. Cuando el río suena… </span></span></li>
</ul><span style="font-family: verdana;">
</span><div lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"><span style="font-family: verdana;">
</span><br /></div>
<div lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;">
<h2>
¿Dónde han
ido esos bloggers?</h2>
</div>
<div lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;">
<br /></div>
<div lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;">
<span style="font-family: verdana;">Parece que
el uso de generadores de sitios estáticos se ha impuesto (<a href="https://gohugo.io/" target="">Hugo</a>, <a href="https://jekyllrb.com/" target="">Jekyll</a>, <a href="https://www.gatsbyjs.com/" target="">Gatsby</a>),
alojando dichos sitios principalmente en <a href="https://pages.github.com/" target="_blank">GitHub Pages</a>, aunque esta no es la
única opción; podríamos hostear en <a href="https://docs.microsoft.com/en-us/azure/storage/blobs/storage-blob-static-website" target="">Azure Storage</a>, en <a href="https://docs.gitlab.com/ee/user/project/pages/">GitLab Pages</a>, también usando
<a href="https://azure.microsoft.com/es-es/services/app-service/static/">Azure Static Web Apps</a>... hay muchas opciones válidas.<br /></span></div>
<div lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;">
<br /></div>
<div lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;">
<span style="font-family: verdana;">Hay mucha
información al respecto, aquí os dejo unos enlaces:</span></div><span style="font-family: verdana;">
</span><div lang="es" style="font-size: 11pt; margin: 0in;">
<span style="font-family: verdana;"><br /></span></div><span style="font-family: verdana;">
</span><ul style="direction: ltr; margin-bottom: 0in; margin-left: 0.375in; margin-top: 0in; unicode-bidi: embed;" type="disc">
<li lang="es" style="margin-bottom: 0px; margin-top: 0px; vertical-align: middle;"><span style="font-family: verdana;"><a href="https://docs.github.com/en/github/working-with-github-pages/setting-up-a-github-pages-site-with-jekyll"><span style="font-size: 11pt;">https://docs.github.com/en/github/working-with-github-pages/setting-up-a-github-pages-site-with-jekyll</span></a></span></li>
<li lang="es" style="margin-bottom: 0px; margin-top: 0px; vertical-align: middle;"><span style="font-family: verdana;"><a href="https://dev.to/dgavlock/creating-a-hugo-site-on-github-pages-3cjo"><span style="font-size: 11pt;">https://dev.to/dgavlock/creating-a-hugo-site-on-github-pages-3cjo</span></a></span></li>
<li lang="es" style="margin-bottom: 0px; margin-top: 0px; vertical-align: middle;"><span style="font-family: verdana;"><a href="https://docs.microsoft.com/es-es/azure/static-web-apps/publish-jekyll"><span style="font-size: 11pt;">https://docs.microsoft.com/es-es/azure/static-web-apps/publish-jekyll</span></a></span></li><li lang="es" style="margin-bottom: 0px; margin-top: 0px; vertical-align: middle;"><span style="font-family: verdana;"><span style="font-size: 11pt;">Incluso uno de mi amigo @panicoenlaxbox: <a href="https://www.panicoenlaxbox.com/post/hugo-azure-static-web-app/">https://www.panicoenlaxbox.com/post/hugo-azure-static-web-app</a><br /></span></span></li>
</ul><span style="font-family: verdana;">
</span><div lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"><span style="font-family: verdana;">
<br />
Algunas de las ventajas son muy claras y directas: propiedad 100% del blog, cuyo código estará alojado en un repositorio Git <b>de tu propiedad</b>, con todos los beneficios que esto trae, como el control de versiones y la posibilidad de ejecutar pipelines para para diversas tareas (cross posting como ejemplo).<br />
<br />
Por otro lado, el performance del site será buenísimo, difícilmente un motor dinámico puede competir con un sitio estático.</span><br />
<br /></div>
<div lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;">
<h2 style="text-align: left;">
¿Y yo qué
voy a hacer? ¿Voy a seguir en Blogger?</h2>
</div>
<div lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;">
<span style="font-family: verdana;"><br /></span></div><span style="font-family: verdana;">
</span><div lang="es" style="font-size: 11pt; margin: 0in;"><span style="font-family: verdana;">
M apetece moverme, pero echo la vista atrás y veo
que no he logrado publicar regularmente. <b>Debo empezar a publicar con regularidad de nuevo</b>, y si lo consigo, haré el
movimiento. Hacerlo al revés me parece un error.</span></div><span style="font-family: verdana;">
</span><div style="font-size: 11pt; margin: 0in;">
<span style="font-family: verdana;"><br /></span></div><span style="font-family: verdana;">
</span><div style="font-size: 11pt; margin: 0in;">
<span style="font-family: verdana;"><span lang="en-US">Por
lo pronto</span><span lang="es"> me pongo a escribir la siguiente entrada, sobre git y mis problemas por usar múltiples cuentas. </span></span></div><div style="font-size: 11pt; margin: 0in;"><span style="font-family: verdana;"><span lang="es"><br /></span></span></div><div style="font-size: 11pt; margin: 0in; text-align: left;"></div><div style="font-size: 11pt; margin: 0in; text-align: left;"><h2 style="text-align: left;"><span style="font-family: verdana;"></span></h2></div><div style="font-size: 11pt; margin: 0in;"><h2 style="text-align: left;">¿Y que es eso de las petisoperías?<span style="font-family: verdana;"><span lang="es"></span></span></h2><div style="text-align: left;"><span style="font-family: verdana;"><span style="font-weight: normal;"><span lang="es"> </span></span></span></div><div style="text-align: left;"><span style="font-family: verdana;"><span style="font-weight: normal;"><span lang="es">Si has leido Superlopez, seguro que lo recuerdas</span></span></span></div><div style="text-align: left;"><span style="font-family: verdana;"><span style="font-weight: normal;"><span lang="es"> </span></span></span></div><div style="text-align: left;"><span style="font-family: verdana;"><span style="font-weight: normal;"><span lang="es"><div class="separator" style="clear: both; text-align: center;"><a href="https://cloud10.todocoleccion.online/comics-ediciones-b/tc/2021/09/21/03/288586463.webp" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="405" data-original-width="720" height="405" src="https://cloud10.todocoleccion.online/comics-ediciones-b/tc/2021/09/21/03/288586463.webp" width="720" /></a></div><br /> </span></span></span></div><h2 style="text-align: left;"><span style="font-family: verdana;"></span></h2></div>Sergio Navarro Pinohttp://www.blogger.com/profile/15840814836353973005noreply@blogger.com0Valdebebas, 28055 Madrid, Spain40.482641 -3.616334616.00812 -44.9249286 64.957162 37.6922594tag:blogger.com,1999:blog-7984577123741299127.post-44238923092814218232022-02-07T20:57:00.012+01:002022-02-07T22:03:04.691+01:00Feature flags o deployment rings. ¿Por qué no ambos?<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;">DevOps
nos permite entregar valor al negocio de un modo sostenido, obteniendo feedback
de producción de un modo temprano, de modo que nuestras decisiones sean tomadas
en base a datos objetivos. </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;"> </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;">Entregar
features rápido no implica que todas estas features resulten siempre
satisfactorias para nuestros usuarios o el negocio. Algunas pueden fallar
(tener bugs) o pese a funcionar correctamente, no resultar útiles o cómodas a
nuestros usuarios, generando rechazo.</p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;"> </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;">En esos
casos, una <span style="font-weight: bold;">exposición progresiva</span>
(Progressive exposure) de estas nuevas features puede reducir el rechazo, o al
menos que este se produzca en un número reducido de usuarios, en vez de en toda
nuestra base de clientes.</p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;"><span style="font-family: Verdana; font-size: 12pt;"> </span></p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;"><span style="font-family: Verdana; font-size: 12pt;">El conjunto de usuarios a los que llega una nueva
feature se conoce como </span><a href="https://en.wikipedia.org/wiki/Blast_radius"><span style="font-family: Verdana; font-size: 12pt; font-weight: bold;">Radius blast</span></a><span style="font-family: Verdana; font-size: 12pt;">, </span><span style="font-family: Verdana; font-size: 12pt;">por analogía con el radio de
expansión de una bomba. Si decimos que incrementamos el Radius blast, estaremos
haciendo que una feature llegue a más usuarios.</span></p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"><span style="font-family: Verdana; font-size: 12pt;"><br /></span></p><br /><h2 style="text-align: left;">Feature flags y Deployment rings</h2><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"><span style="font-family: Verdana; font-size: 12pt;"><br /></span></p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"><span style="font-family: Verdana; font-size: 12pt;">
</span></p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;">Dos de
las técnicas más extendidas que podemos usar para exponer nuestras features
progresivamete a nuestros usuarios son el uso de <span style="font-weight: bold;">feature
flags</span> y la estrategia de <span style="font-weight: bold;">despligue en
anillos</span> (deployment rings).</p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;"> </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;">Ambas
cumplen el propósito, pero como no podía ser de otro modo, presentan pros y
contras, que debemos conocer antes de "lanzarnos" a seleccionar una.</p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"><span style="font-family: Verdana; font-size: 12pt;"><br /></span> </p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"><span style="font-weight: bold;">Deployment rings</span><span style="font-family: Verdana; font-size: 12pt;"></span> </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;">
</p><br /><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;">Fueron
descritos por primera vez por Jez Humble en el famosísimo libro "Continuous
Delivery", donde se hablaba también de <b>canary deployments</b>, que podemos
considerar un caso especial de despliegue en anillos.</p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"></p><div style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjJy2tfPt51q1L1Dezb9IZe61RGS_sAH06lJlZDD2nRtVbxqDjWmICn2W4nmzha8MK9AUWBMG7BhUZDLL1aARouk4L3KKmN9NYHd7_Be3I-_vXSWStnDycBzOGmAoH40HI563KoOkyd03o1KNREsnbmKu1nclWkB1Jrobj6PI6tDA1wGMtC_mMA5ir9Gw=s500"><img alt="Portada del libro Continuous Delivery" border="0" data-original-height="500" data-original-width="347" height="320" src="https://blogger.googleusercontent.com/img/a/AVvXsEjJy2tfPt51q1L1Dezb9IZe61RGS_sAH06lJlZDD2nRtVbxqDjWmICn2W4nmzha8MK9AUWBMG7BhUZDLL1aARouk4L3KKmN9NYHd7_Be3I-_vXSWStnDycBzOGmAoH40HI563KoOkyd03o1KNREsnbmKu1nclWkB1Jrobj6PI6tDA1wGMtC_mMA5ir9Gw=w222-h320" width="222" /></a></div><br /> <p style="text-align: left;"></p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;">
</p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;">Se trata
de disponer de varios entornos de producción, siendo cada uno de tus usuarios
siempre enrutado al mismo entorno, por criterios variados (geográficos, por
tipo de usuario, por antigüedad u otros).</p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;"> </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;">Cuando se
despliega una nueva feature, inicialmente solo estará disponible en el primero
de los entornos (anillo), donde será visible solo para un porcentaje de usuarios
(los redirigidos allí). </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;"> </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;">A partir
de ese momento comenzaremos a monitorizar activamente el comportamiento de esta
nueva feature en ese primer anillo, para pasado un tiempo razonable, decidir si
la progresamos al siguiente anillo (hemos ganado suficiente confianza) o bien
es necesario algún cambio en el software, y por tanto un nuevo despliegue al
primer anillo.</p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;"> </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;">Este
mismo proceso lo realizaremos para cada uno de los anillos que hayamos
definido. Podemos verlo en el siguiente gráfico:</p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"><br /></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEh5mgwoTg-bKhXjHBU2qlCdrffyo0bqYP0Yjyc__o-v2ymQ3taQDpjp9qanENysRr1i8KaUFdjYx9_xGxnVafgGm3KJo0UqzDUEM62QjPCSPGss5eRjhykTHkrLaiqTprxaF5RcxXZYaiSqmiYVjog-26NnmQhovWvvMzcR2ryvq6vxhTQVNK_E0QFrKQ=s855" style="margin-left: 1em; margin-right: 1em;"><img alt="Proceso de CI/CD con deployment rings" border="0" data-original-height="354" data-original-width="855" height="265" src="https://blogger.googleusercontent.com/img/a/AVvXsEh5mgwoTg-bKhXjHBU2qlCdrffyo0bqYP0Yjyc__o-v2ymQ3taQDpjp9qanENysRr1i8KaUFdjYx9_xGxnVafgGm3KJo0UqzDUEM62QjPCSPGss5eRjhykTHkrLaiqTprxaF5RcxXZYaiSqmiYVjog-26NnmQhovWvvMzcR2ryvq6vxhTQVNK_E0QFrKQ=w640-h265" width="640" /></a></div><br /><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"><br /></p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;">
</p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;">Como veis
se han definido 3 anillos: al primero lo llaman "Canaries", el siguiente es para "Early
adopters" y el último engloba al resto de usuarios. A modo de ejemplo, y
para una aplicación tipo "Spotify" podríamos definirlos así:</p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;"> </p>
<ul style="margin-bottom: 0in; margin-top: 0in; text-align: justify; unicode-bidi: embed;" type="disc"><li lang="en-US" style="margin-bottom: 0px; margin-top: 0px; vertical-align: middle;"><span style="font-family: Verdana; font-size: 12pt;">Canaries: trabajadores de
Spotify</span></li><li lang="en-US" style="margin-bottom: 0px; margin-top: 0px; vertical-align: middle;"><span style="font-family: Verdana; font-size: 12pt;">Early adopters: Clientes que
han indicado que quieren recibir versiones "beta"</span></li><li lang="en-US" style="margin-bottom: 0px; margin-top: 0px; vertical-align: middle;"><span style="font-family: Verdana; font-size: 12pt;">Users: resto de usuarios</span></li></ul>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;"> </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;">Podríamos
definirlo de otro modo, con más o menos anillos y usando otros criterios como
decía antes, pero lo importante es el proceso, que paso a detallar:</p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </p>
<ol style="font-family: Verdana; font-size: 12pt; font-style: normal; font-weight: normal; margin-bottom: 0in; margin-top: 0in; text-align: left; unicode-bidi: embed;" type="1"><li lang="en-US" style="margin-bottom: 0px; margin-top: 0px; vertical-align: middle;" value="1"><span style="font-family: Verdana; font-size: 12pt; font-style: normal; font-weight: normal;">Un commit
llega a la rama principal</span></li><li lang="en-US" style="margin-bottom: 0px; margin-top: 0px; vertical-align: middle;"><span style="font-family: Verdana; font-size: 12pt;">Se dispara atomáticamente el
pipeline de CI/CD</span></li><ol style="font-family: Verdana; font-size: 12pt; font-style: normal; font-weight: normal; margin-bottom: 0in; margin-top: 0in; unicode-bidi: embed;" type="a"><li lang="en-US" style="margin-bottom: 0px; margin-top: 0px; vertical-align: middle;" value="1"><span style="font-family: Verdana; font-size: 12pt; font-style: normal; font-weight: normal;">Se
compila, se pasan los tests y se empaqueta el software</span></li><li lang="en-US" style="margin-bottom: 0px; margin-top: 0px; vertical-align: middle;"><span style="font-family: Verdana; font-size: 12pt;">Se despliega en el primer
anillo</span></li></ol><li lang="en-US" style="margin-bottom: 0px; margin-top: 0px; vertical-align: middle;"><span style="font-family: Verdana; font-size: 12pt;">Se monitoriza activamente
durante un tiempo definido</span></li><ol style="font-family: Verdana; font-size: 12pt; font-style: normal; font-weight: normal; margin-bottom: 0in; margin-top: 0in; unicode-bidi: embed;" type="a"><li lang="en-US" style="margin-bottom: 0px; margin-top: 0px; vertical-align: middle;" value="1"><span style="font-family: Verdana; font-size: 12pt; font-style: normal; font-weight: normal;">Si se
encuentra algun problema, se descarta la release y no será progresada a
siguientes anillos</span></li><li lang="en-US" style="margin-bottom: 0px; margin-top: 0px; vertical-align: middle;"><span style="font-family: Verdana; font-size: 12pt;">Si no se encuentran
problemas, se progresa la release al siguiente anillo. Podría ser con una acción de aprobación
manual o de modo automático (cuando termina el tiempo definido)</span></li></ol><li lang="en-US" style="margin-bottom: 0px; margin-top: 0px; vertical-align: middle;"><span style="font-family: Verdana; font-size: 12pt;">Se actúa igual en los
siguientes anillos</span></li></ol>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in 0in 0in 0.75in; text-align: left;"> </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;">Como
vemos <b>ganamos en confianza a coste de ser más lentos</b> haciendo llegar las nuevas
features a nuestros usuarios.</p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"><br /></p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"><span style="font-weight: bold;">¿Qué herramientas podemos usar para implementar una
estrategia de despliegue en anillos?</span><br /></p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;">Sin duda
mis favoritas son GitHub actions y Azure Pipelines, aunque podríamos usar otras
como CircleCI, Jenkins o GitLab</p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;"> </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;">En GitHub
actions nos será muy útil usar environments (aunque solo están presentes en la
versión Enterprise):</p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;"><a href="https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment">https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment</a></p><div style="text-align: justify;"><br /></div><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;">En Azure
DevOps Pipelines, también nos apoyaremos en los environments, y además<span style="mso-spacerun: yes;"> </span>disponemos de las Gates, que nos pueden
ayudar a monitorizar nuestra release en un anillo para decidir si progresa al
siguiente:</p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"><a href="https://docs.microsoft.com/en-us/azure/devops/pipelines/release/approvals/">https://docs.microsoft.com/en-us/azure/devops/pipelines/release/approvals/</a></p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> <br /></p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;">
</p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"><span style="font-weight: bold;">Feature flags (o feature toggles)</span><br /></p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </p>
<p lang="en-US" style="margin: 0in; text-align: justify;"><span style="font-family: Verdana; font-size: 12pt;">Fueron descritos por primera vez por Martin Fowler en este <a href="https://martinfowler.com/bliki/FeatureToggle.html">artículo</a> </span><span style="font-family: Verdana; font-size: 12pt;">y nos permiten desacoplar la
exposición<span style="mso-spacerun: yes;"> </span>progresiva de features del
despliegue de una release. Para ello, las nuevas features van controladas con
un toggle, que nos permite, en tiempo de ejecución, elegir si se encuentran
activas o no, y por tanto modificar el comportamiento de nuestro software.</span></p>
<p style="text-align: left;"> </p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhbAbfKg6s1vd6ui_tHwzRWXiTB5P24wJqZBbZx7ND1DDgui_-LSSIiV1hytcO3-1eHzAsOWNSLWY2Vz3VVPkpEPBLIajNvZnDwL0TEA1TsEvou25aV6rivSWTGOXy3A2jtt_tqLlSfM06NcTZroqSV1zbAyMVSGah8hlfbP-oD01H_v0ML2_TWC9ZQag=s326" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="326" data-original-width="300" height="320" src="https://blogger.googleusercontent.com/img/a/AVvXsEhbAbfKg6s1vd6ui_tHwzRWXiTB5P24wJqZBbZx7ND1DDgui_-LSSIiV1hytcO3-1eHzAsOWNSLWY2Vz3VVPkpEPBLIajNvZnDwL0TEA1TsEvou25aV6rivSWTGOXy3A2jtt_tqLlSfM06NcTZroqSV1zbAyMVSGah8hlfbP-oD01H_v0ML2_TWC9ZQag=s320" width="294" /></a></div><br /><p style="text-align: left;"></p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;">
</p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;">Es decir,
podemos desplegar una release con varias features desactivadas, para más
adelante activarlas poco a poco, monitorizando en todo momento su
comportamiento.</p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;">¿Qué
necesitamos para esto?</p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </p>
<ol style="font-family: Verdana; font-size: 12pt; font-style: normal; font-weight: normal; margin-bottom: 0in; margin-top: 0in; text-align: left; unicode-bidi: embed;" type="1"><li lang="en-US" style="margin-bottom: 0px; margin-top: 0px; vertical-align: middle;" value="1"><span style="font-family: Verdana; font-size: 12pt; font-style: normal; font-weight: normal;">Un servicio
de gestión de toggles. </span></li><li lang="en-US" style="margin-bottom: 0px; margin-top: 0px; vertical-align: middle;"><span style="font-family: Verdana; font-size: 12pt;">Una query que pregunte en
tiempo de ejecución el valor de un toggle</span></li><li lang="en-US" style="margin-bottom: 0px; margin-top: 0px; vertical-align: middle;"><span style="font-family: Verdana; font-size: 12pt;">Una remificación if-else (o
algo más sofisticado) en nuestro código que haga que el software se
comporte de distinta manera cuando el toggle esté o no activado.</span></li></ol>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"><span style="font-weight: bold;"> </span></p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"><span style="font-weight: bold;">¿Qué herramientas podemos usar? <br /></span></p><div style="text-align: justify;"><br /></div><p lang="en-US" style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: justify;"><span style="font-family: Verdana; font-size: 12pt;"><a href="https://docs.microsoft.com/en-us/azure/azure-app-configuration/overview">Azure App Configuration</a> nos ofrece una gestión simple de Feature
toggles, que puede servir en algunos escenarios, pero lo más seguro es que
rápidamente necesites algo más avanzado, como <a href="https://github.com/Xabaril/Esquio">Xabaril Esquio</a></span><span style="font-family: Verdana; font-size: 12pt;"> (un paquete para .NET hecho por
algunos amigos) o un servicio como <a href="https://launchdarkly.com/">LaunchDarkly</a>.</span> <br /></p><p></p><p><br /></p><h2 style="text-align: left;">¿Qué opción elegir?</h2><p lang="en-US" style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;"> </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;">Como
siempre que se hacen este tipo de preguntas, sería temerario responder
categóricamente, un "depende" siempre es más acertado.</p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;"> </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;">En ambos
casos vamos a controlar qué usuarios reciben features, para después ir
exponiéndolas progresivamente a otros grupos de usuarios o la totalidad de estos.
Además ambas opciones nos van a permitir realizar un A/B testing controlado.</p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;"> </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;">No
obstante hay algunas diferencias importantes que debemos conocer. </p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"><span style="font-weight: bold;"> </span></p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"><span style="font-weight: bold;">Coste</span></p><br /><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;">Con Deployment rings vamos a necesitar varios entornos de producción, mientras que
con Feature flags vamos a necesitar una herramienta para controlarlas (con
un store para persistir las toggles), además de cambios en nuestro
código para gestionarlas.</p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;"> </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;">Para mí
este último detalle es importante. <b>Mantener muchas toggles no es gratis</b>, tu
código se ve afectado, y las toggles deben ser eliminadas una vez que hemos
decidido que una feature nunca más será desactivada. </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;"> </p>
<p lang="en-US" style="margin: 0in; text-align: justify;"><span style="font-family: Verdana; font-size: 12pt;">La </span><span style="font-family: Verdana; font-size: 12pt; font-weight: bold;">deuda de toggles </span><span style="font-family: Verdana; font-size: 12pt;">puede ser peligrosa, y debemos tener en cuenta que no se trata solo de borrar la toggle, sino la
ramificación de código asociada. Por aquí hablan de ello: <a href="https://codescene.com/blog/feature-toggles-are-technical-debt/">https://codescene.com/blog/feature-toggles-are-technical-debt/</a> </span></p><br /><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"><span style="font-weight: bold;">Gestión del Radius Blast</span></p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"><span style="font-weight: bold;"> </span></p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;">Podemos
pensar que es más sencilla con los Deployment rings, ya que se trata de enrutar
a los usuarios al entorno adecuado según los hayamos categorizado (canaries,
early adopter o usuario general en el ejemplo que presentaba).</p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;"> </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;">Sin
embargo las librerías y los servicios avanzados nos permiten definir grupos de
usuarios y conseguir efectos similares con toggles.</p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;"> </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;">Además
<b>las Feature toggles nos "regalan" un rollback sencillo</b>: si una feature no
funciona podemos desactivarla al momento.</p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"><span style="font-weight: bold;"> </span></p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"><span style="font-weight: bold;">Confianza</span> <br /></p><br /><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;">La
confianza es algo importante para mi, siempre digo que quiero dormir por las
noches. Con Feature toggles, lo normal es que tengas un único entorno
productivo, por lo que cuando despliegas una release, esta va a todos los
usuarios, aunque para algunos haya features desactivadas.</p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;"> </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;">Si una
release es "mala", a veces eso pasa :-(, y su problema no está tras una feature
toggle, todos los usuarios lo sufrirán.</p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;"> </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;">Sin
embargo, con Deployment rings, solo los usuarios del primer anillo
"sufrirían" esa "mala" release. </p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"><span style="font-weight: bold;"> </span></p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"><span style="font-weight: bold;">Habilidades del equipo y aspectos organizativos<br /></span></p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: justify;">Diría que
la opción de las Feature toggles es más cercana al desarrollo, mientras que los Deployment rings están más cerca de la infraestructura. Las habilidades de tu equipo o la
organización de tu empresa pueden determinar la decisión. <br /></p><br /><h2 style="text-align: left;">¿Podemos usar ámbas opciones simultaneamente?</h2><h1 lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </h1>
<h1 lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"><span style="font-weight: normal;">Claro, y
en algunos casos será la opción más acertada :-)</span> <br /></h1>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"> </p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;">Continuaré
con estos temas en otros posts, espero que sean de interés.</p>
<p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"><br /></p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"><b>Actualización:</b></p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"><br /></p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;">Me indica <a href="https://twitter.com/vgaltes?s=20">Vicenç García</a> que en Uber han hecho su propia herramienta para manejar esa deuda asociada a las Feature flags. ¡Gracias!</p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"><br /></p><p lang="en-US" style="font-family: Verdana; font-size: 12pt; margin: 0in; text-align: left;"><a href="https://eng.uber.com/piranha/">Introducing Piranha: An Open Source Tool to Automatically Delete Stale Code - Uber Engineering Blog</a></p>
Sergio Navarro Pinohttp://www.blogger.com/profile/15840814836353973005noreply@blogger.com0tag:blogger.com,1999:blog-7984577123741299127.post-88647893605747355022021-05-23T09:58:00.007+02:002021-05-24T19:34:48.879+02:00Conceptos: Deploy vs Release<div><p style="font-family: Calibri; font-size: 11pt; margin: 0in;">Hola a todos,</p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;">hoy vengo a hablar
de dos conceptos importantes, que a veces confundimos: Deploy y release.</p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;">¿Son lo mismo?
¿Podemos usarlos indistintamente? ¿Siempre que despliego estoy
"releaseando"? ¿Necesito necesariamente desplegar para hacer release
de nuevas características en mi software?</p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;">Vamos a entender
ambos conceptos y responder a esas preguntas, y para ello comencemos por el
primero, "Deploy".</p><p style="font-family: Calibri; font-size: 11pt; margin: 0in;"><br /></p></div> <h2>Deploy</h2> <p style="font-family: Calibri; font-size: 11pt; margin: 0in;">
</p><p style="font-family: Calibri; font-size: 11pt; margin: 0in;"><span style="font-weight: bold;"> </span></p><p style="font-family: Calibri; font-size: 11pt; margin: 0in;"><span style="font-weight: bold;">Deploy</span> es el acto técnico de hacer que un
software sea "copiado" a un entorno, y pueda ser utilizado por los
usuarios. </p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;">El <span style="font-weight: bold;">mecanismo que usemos para desplegar va a diferir
bastante según la tecnología que usemos</span> para nuestra aplicación: no
desplegamos igual si nuestra aplicación funciona en Azure App Service (hay que
copiar ficheros a </p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;"><span style="font-style: italic;">/home/site/wwwroot</span>) o si se ejecuta en un
cluster de kubernetes (desplegaríamos subiendo una imagen a un container
registry y después dando una instrucción al cluster para usarla).</p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;">Por otro lado, <span style="font-weight: bold;">el despliegue puede ser con parada o sin ella</span>.
En el primer caso la aplicación dejará de estar disponible por un tiempo (a
poder ser el mínimo posible), mientras que en el segundo (despliegue sin
parada) la aplicación estará siempre disponible a los usuarios mientras es
actualizada con una nueva versión.</p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;">Aquí te dejo un
enlace a una sesión donde explico técnicas para conseguir despliegues sin
parada: <a href="https://youtu.be/_BEklD8eOCI?list=PLcTDLfPc39eAR8U8A7jn0yvKpvc-8XXJS" target="_blank">Despliega como los grandes: zero downtime deployment</a>.</p><p style="font-family: Calibri; font-size: 11pt; margin: 0in;"><br /></p><h2>Release </h2> <p style="font-family: Calibri; font-size: 11pt; margin: 0in;">
</p><p style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p><p style="font-family: Calibri; font-size: 11pt; margin: 0in;">Se trata de hacer
que una nueva característica (feature) de la aplicación esté disponible a los
usuarios.</p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;">¿<span style="font-weight: bold;">Cómo podemos hacer que una aplicación disponga de una
nueva característica</span>? La respuesta parece sencilla: desplegando una
nueva versión del software que habilite esa nueva feature.</p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;">Sin embargo hay más
modos, esa nueva feature puede ser "activada" en un momento dado,
habiendo estado oculta por un tiempo. Para ello podemos por ejemplo hacer uso
de <a href="https://martinfowler.com/articles/feature-toggles.html" target="_blank">Feature flags</a>, para activar y desactivar features. Y lo que es mejor,
<span style="font-weight: bold;">puede ser el negocio el que lo haga, sin
necesitar conocimiento técnico</span>.</p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;">Escribiré sobre
Feature flags, mientras tanto te dejo este <a href="https://geeks.ms/lfraile/2019/03/30/some-ideas-on-feature-flags/" target="_blank">post</a> de Luis Fraile con algunas ideas.<br /></p><br /><h2 style="text-align: left;">Respondiendo a las preguntas</h2> <p style="font-family: Calibri; font-size: 11pt; margin: 0in;">
</p><p style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p><p style="font-family: Calibri; font-size: 11pt; margin: 0in;">Una vez explicados
los conceptos podemos responder a las preguntas anteriores</p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<h3 style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;">¿Podemos hablar
indistintamente de Deploy y de Release?</h3>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;">No, como decía el
despliegue es una práctica técnica (copiar nuevos bytes para que se ejecuten),
mientras que la release es un concepto de negocio, que entrega nuevas features
a los usuarios.</p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<h3 style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;">¿Siempre que
despliego estoy "releaseando"?</h3>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;">No tiene por qué,
podría desplegar una nueva versión que por ejemplo llevase nuevo código (clases), pero que aún no son usadas.</p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;">¿Y por qué querría
hacer esto? Se escapa del ámbito de este post, de nuevo hablaré sobre ello,
mientras tanto te dejo esta sesión de Edu Ferro donde lo entenderás:</p><p style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p><p style="font-family: Calibri; font-size: 11pt; margin: 0in;"><a href="https://www.youtube.com/watch?v=B2niPlMyuw0" target="_blank">#SDsummit -<span style="mso-spacerun: yes;"> </span>"S3 Small Safe Steps. El secreto de la
agilidad", Eduardo Ferro</a><br /></p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<h3 style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;">¿Necesito
necesariamente desplegar para hacer release de nuevas características en mi
software?</h3>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;">No, de hecho podemos ofrecer
una nueva feature a nuestros clientes, habiéndola desplegado en el pasado y
habiéndola mantenido oculta durante un tiempo.</p><p style="font-family: Calibri; font-size: 11pt; margin: 0in;"><br /></p><h2 style="text-align: left;">Bola extra: Delivery </h2> <p style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p><p style="font-family: Calibri; font-size: 11pt; margin: 0in;">Ahora que hemos
visto la diferencia entre Deploy y Release, añado un nuevo concepto íntimamente
relacionado con Agile: <b>Delivery.</b></p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;">Uno de los
principios del <a href="https://agilemanifesto.org/principles.html" target="_blank">manifiesto ágil</a> dice: "Our highest priority is to satisfy the customer
through early and continuous delivery of valuable software"</p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;">Se habla de entrega
(<b>delivery</b>) de software de valor al cliente, pero ¿cómo entregamos valor a un
cliente? </p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;">Con <span style="font-weight: bold;">nuevas releases</span> de software, pero también
desplegando nuevas versiones de software que aún no entreguen nuevas features,
pero que por ejemplo vayan <span style="font-weight: bold;">preparando el
software para la llegada de dichas features</span>, o que como decíamos, <span style="font-weight: bold;">las lleven ocultas</span>.</p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;">Pero también <span style="font-weight: bold;">podemos entregar valor de otros modos, totalmente
ajenos al código de nuestra aplicación</span>: Piensa que podríamos entregar
valor activando un sistema de telemetría, que permitiese a negocio tomar
decisiones… interesante.</p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;">Hasta aquí mis ideas
sobre estos conceptos importantes, ¿estás de acuerdo? ¿quieres matizar algo? </p><p style="font-family: Calibri; font-size: 11pt; margin: 0in;">¡No
dudes en comentar aquí o en <a href="https://twitter.com/snavarropino/status/1396719313575456774?s=20" target="_blank">twitter</a>! </p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;">Saludos</p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
Sergio Navarro Pinohttp://www.blogger.com/profile/15840814836353973005noreply@blogger.com0Valdebebas, 28055 Madrid, Spain40.482641 -3.616334612.172407163821156 -38.7725846 68.792874836178839 31.539915399999998tag:blogger.com,1999:blog-7984577123741299127.post-53784717642818327362021-04-26T07:44:00.015+02:002021-04-26T07:54:38.930+02:00Liveness<p>Hablábamos en el artículo anterior sobre Readiness, una sonda de kubernetes que indica cuando un pod está listo para atender tráfico, dejándo la plataforma de enviárselo en caso de no superar la sonda. <a href="https://www.serginet.com/2021/04/readiness.html">Aquí</a> puedes echarle un ojo.</p><p>Un pod puede no ser capaz de atender tráfico temporalmente (está arrancando por ejemplo) o debido a un error del que no pueda recuperarse solo. Para este segundo caso tenemos otro tipo de sonda, llamada <b>Liveness</b>, que es explicada por David Fowler en el siguiente tweet:</p><p> </p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgfelr3PYY2rNJ5tLjVESaqNPOHhaHfgY7FvW4Xpyu3nFhtspwahWpvazT0fa-0mnWG_ycVBA9tEsrAGVBMe_eMzltLlFLase9OWfma3PhIBq1PvIr6AMEfEvfDnjmWV3h-_xOaZNNL2zAF/s659/fowler.png" style="margin-left: 1em; margin-right: 1em;"><img alt="Definición de liveness de David Fowler" border="0" data-original-height="163" data-original-width="659" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgfelr3PYY2rNJ5tLjVESaqNPOHhaHfgY7FvW4Xpyu3nFhtspwahWpvazT0fa-0mnWG_ycVBA9tEsrAGVBMe_eMzltLlFLase9OWfma3PhIBq1PvIr6AMEfEvfDnjmWV3h-_xOaZNNL2zAF/s16000/fowler.png" /></a></div><br /><p></p><p><b>Esta sonda "pregunta" a un pod si sigue vivo, para en caso contrario reiniciarlo</b>. Podemos verlo de un modo muy claro en la siguiente imagen (cortesía de la documentación de Google Cloud Platform):</p><p> </p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgigv1KXNGmnLAhevtmB4dCXre7AyBh2yZ2NoAOHVt3ZdgnHDNf4fm0BzmenotRl6hkiGrlt-tXXx4ihk-Jdgh_OllG83A5IBvXRo7kIr_IrU4QCthsXQqwiDZECGYqHdEhc4439t-5tARy/s1010/Liveness.GIF" style="margin-left: 1em; margin-right: 1em;"><img alt="Liveness en funcionamiento" border="0" data-original-height="862" data-original-width="1010" height="546" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgigv1KXNGmnLAhevtmB4dCXre7AyBh2yZ2NoAOHVt3ZdgnHDNf4fm0BzmenotRl6hkiGrlt-tXXx4ihk-Jdgh_OllG83A5IBvXRo7kIr_IrU4QCthsXQqwiDZECGYqHdEhc4439t-5tARy/w640-h546/Liveness.GIF" width="640" /></a></div><p></p><p></p><p> </p><p>Como
veis, si la sonda no se supera, el pod es reiniciado, pudiendo esto resolver un problema del pod, que le impidiese hacer su trabajo con normalidad.</p><p> </p><h3 style="text-align: left;">¿Siempre necesito definir esta sonda?<br /></h3><p>Para poder responder debemos entender el ciclo de vida de un pod, que podemos encontrar en la <a href="https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle">documentación oficial</a>.<br /> <br />Allí se hace referencia a que los contenedores de un pod (si, no olvidemos que en un pod se ejecutan 1 o más contenedores) pueden ser reiniciados cuando terminan, dependiendo de la "restart policy" definida.<br /> <br />Esta política admite tres valores:</p><ul style="text-align: left;"><li>Always (valor por defecto)</li><li>On failure</li><li>Never</li></ul><p> <br />Salvo que utilicemos la opción "never", nuestros contenedores serán rearrancados después de terminar, usando un retardo exponencial (10s, 20s, 40s…) que está limitado a un máximo de hasta 5 minutos.<br /> <br />Si fijamos el valor "On failure" solo serán rearrancados contenedores que finalicen con un valor de retorno distinto a 0.<br /> <br />Explicado lo anterior, parece que dejando el valor por defecto, vamos a conseguir que nuestros pods siempre esten "vivos", ya que si sus contendores acabasen, por el motivo que fuere, serían reiniciados.<br /> <br />Pero la clave es: <b>¿mi contenedor siempre va a terminar si encuentra algún error o no es capaz de funcionar correctamente?</b> ¿Estamos seguros de que no va a entrar en un estado de dead lock? ¿Y en un bucle infinito?<br /> <br />Si no estamos seguros de poder responder afirmativamente a las preguntas anteriores, mejor será definir una sonda de liveness, que se encargue de reiniciar el pod si este no es capaz de responder satisfactoriamente.</p><p><br /></p><h3 style="text-align: left;">Definiendo la sonda de liveness</h3><p>Debemos hacerlo en nuestros deployments: <script src="https://gist.github.com/snavarropino/c9735a265fa0b98e347765598a542f3a.js"></script></p><p></p><p>La sonda anterior tratará de conectar con el puerto 80 cada 5 segundos, y en caso de poder hacerlo la prueba se considerará exitosa. En caso contrario, tras dos fallos consecutivos, el pod será reiniciado.<br /> <br />En este caso he definido un probe tcp, aunque no es la única opción disponible, ya que también podemos definir probes http o probes basados en comandos. En la documentación oficial podemos verlo en detalle, no obstante aquí os dejo un fragmento de una sonda http:<br /><script src="https://gist.github.com/snavarropino/522fe0a240ed80472bfc1a2232732fb2.js"></script>Ten en cuenta que tu eres el responsable de impementar el endpoint /healthy en tu aplicación. En el mismo puedes devolver Ok sin más (sonda "tonta") o realizar comprobaciones avanzadas, como tratar de ver si la base de datos está accesible (sonda "lista").</p><p><br /></p><h3 style="text-align: left;">¿Http, tcp o comandos?</h3><p>No es una respuesta sencilla, pero de entrada diría que una buena opción para empezar sería la siguiente: <br /></p><ol style="text-align: left;"><li>Http si está disponible</li><li>Tcp si el pod no "habla" http</li><li>Comando si el pod no "escucha" peticiones (hace tareas batch por ejemplo) <br /></li></ol><p>Pero no se trata de una "ley" a cumplir siempre, aquí lo que mejor funciona es el clásico "depende". Depende de tu aplicación y su contexto :-)<br /> <br />Lo que si me parece interesante es comentar que si tu pod tiene su contenedor principal y un contenedor sidecar, tipo proxy inverso nginx por ejemplo, una sonda tcp puede resultar insuficiente: podría conectar con el nginx sin problema, pudiendo estar el contenedor principal bloqueado. </p><p> </p><h3 style="text-align: left;">¿Cómo sería una sonda basada en un comando?</h3><p>Como comentaba, si tu pod no escucha peticiones, no son viables las sondas http o tcp, y debemos basarnos en comandos.<br /> <br />Un ejemplo sería ver si el pod está generando periódicamente un fichero concreto en un directorio. En caso de que el pod no estuviese vivo, no podría generar el fichero y la sonda fallaría.<br /> <br /><script src="https://gist.github.com/snavarropino/fe7e4661826f9c52f67d2aa380fcefd4.js"></script>Ese script status_check.sh será el encargado de comprobar que el fichero existe y que ha sido creado recientemente (basándose en su fecha). En caso de no encontrar el fichero o detectar que no es reciente, el script saldrá con error haciendo que la sonda no se supere.</p><h3 style="text-align: left;">Recursos</h3><p>Os dejo un par de enlaces interesantes:</p><p>La <a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/">documentación oficial</a>, donde entran en detalle de todas las configuraciones que podemos definir en una sonda, como el periodo de ejecución y los umbrales para considerar una sonda como exitosa o fallida.</p><p>Por otro lado, os dejo un proyecto opensource, <a href="https://github.com/Xabaril/AspNetCore.Diagnostics.HealthChecks">Xabaril Health Checks</a>, que si trabajais en .net core (o net 5) os ayudará a implementar sondas (readiness o liveness) basándoos en los health checks ofrecidos por la plataforma.<br /></p><p>Espero que esta entrada os haya gustado. Saludos<br /></p>Sergio Navarro Pinohttp://www.blogger.com/profile/15840814836353973005noreply@blogger.com0Valdebebas, 28055 Madrid, España40.482641 -3.616334612.172407163821156 -38.7725846 68.792874836178839 31.539915399999998tag:blogger.com,1999:blog-7984577123741299127.post-85530748509293701732021-04-19T09:12:00.007+02:002021-04-19T14:11:14.425+02:00Readiness<p lang="en-GB" style="color: #767676; font-family: Calibri; font-size: 10pt; margin: 0in; text-align: left;"></p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;">¿Cuánto tiempo pasa desde que una aplicación es arrancada hasta que <b>es capaz de desempeñar su trabajo</b>
con normalidad?</p><p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;"> </p><p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;">En aplicaciones de escritorio hay mucha variación, tenemos algunas en las que prácticamente ese tiempo es nulo, mientras que en otras, es tan elevado, que se recurre a técnicas como la <a href="https://en.wikipedia.org/wiki/Splash_screen">splash screen</a>.</p><p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;"> </p><p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;">Sin embargo en aplicaciones web en general, y en API's REST en particular, no solemos fijarnos tanto en esto, ya que el tiempo usualmente es mucho más pequeño.<br /><br />Fijaos en esta sencilla API escrita en .net 5 (un solo fichero): </p><p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;"><br /></p><script src="https://gist.github.com/snavarropino/a35af4f73098ea2fd10fe275a1bf1b41.js"></script><p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;"><br /></p><p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;"><br /></p><p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;">¿Cuánto tardará en empezar a atender peticiones desde que sea iniciada? ¿Algunos milisegundos? Es posible. Fijaos que ese pequeño tiempo puede generarnos problemas, sobre todo si estamos en escenarios de auto-escalado o si buscamos despliegues sin parada (<a href="https://youtu.be/_BEklD8eOCI" target="_blank">Aquí</a> te dejo una sesión en la que hablo de este tipo de despliegues).</p><p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;"><br /></p><p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;">Por otro lado, imagina una aplicación que necesite descargar configuración o secretos desde un recurso externo antes de poder empezar a atender peticiones. En este caso ese tiempo será muy superior.</p><p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;"><br /></p><h3 style="text-align: left;">Mi definición de "readiness"</h3><p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;"><br />"Es el estado de una aplicación en el cual está capacitada para hacer su trabajo con normalidad"<br /><br />En el supuesto anterior, la aplicación arranca y necesita cierto tiempo para descargar recursos externos (secretos u otra configuración). Una vez hecho está <b>"ready", puede hacer su trabajo con normalidad y por tanto atender peticiones de usuario</b>.</p><p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;"><br /></p><p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;"><br /></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRhb5glXU8kMBnNTp_kE-W6Nt0CgvbVsow1KD9UaVxnU1zRW8hkGMUOoFk6Qs4uIQonUqoE35xfD9I7pOPkSZq5TMWdB01dlCZ0bNPYrePRFpzTLOkZzA67f_TuQtf39vp32b-sGidKjuM/s1010/readyness.GIF" style="margin-left: 1em; margin-right: 1em;"><img alt="Imagén explicativa de Readiness. Un pod arranca y necesita tiempo hasta que puede aceptar tráfico" border="0" data-original-height="862" data-original-width="1010" height="273" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRhb5glXU8kMBnNTp_kE-W6Nt0CgvbVsow1KD9UaVxnU1zRW8hkGMUOoFk6Qs4uIQonUqoE35xfD9I7pOPkSZq5TMWdB01dlCZ0bNPYrePRFpzTLOkZzA67f_TuQtf39vp32b-sGidKjuM/w320-h273/readyness.GIF" title="Readiness. Credit: Google Cloud" width="320" /></a></div><br /><p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;"><br /></p><h3 style="text-align: left;">¿Qué es un readiness probe?</h3><p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;"><br /></p><p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;">Es una sonda o verificación que nos indicará si una aplicación se encuentra en un estado de "ready" o "not ready". En base al resultado de la verificación, se enviará tráfico o no a la aplicación.<br /><br />Veámoslo en el caso de <b>kubernetes</b>. Allí, los pods se crean y destruyen por diferentes motivos, alguno de los cuales no requieren nuestra intervención (un pod es movido de nodo, o simplemente falla y es rearrancado). Por tanto <b>no sería nada deseable que un pod que aún no esté listo recibiese tráfico</b>, ya que generaría errores.<br /><br />Para conseguirlo, debemos definir una "readiness probe" en nuestros deployments, del siguiente modo:</p><p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;"><br /></p><p style="text-align: left;">
<script src="https://gist.github.com/snavarropino/f98c8ab933afa9ec843511b9d608bb2b.js"></script>
</p><p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;"><br />En este caso he definido un probe http, aunque no es la única opción disponible, ya que también podemos definir probes tcp o probes basados en comandos. En la <a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/">documentación oficial</a> podemos verlo.</p><p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;"> </p><p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;">Como veis, he definido un periodo de ejecución de la sonda de 5 segundos, además de un umbral de 2 (threshold), que os paso a explicar:</p><p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;"><br /></p><ul style="font-family: Calibri; font-size: 11pt; text-align: left;"><li>Inicialmente, un pod arranca en estado "Not ready"</li><li>Una vez que supera la sonda de readiness pasa a estado "Ready"</li><li>Si la sonda falla 2 veces seguidas (aquí está ese umbral), pasa de nuevo a estado "Not ready", y no recibirá tráfico.<br /></li></ul><p style="text-align: left;">Solo hay un matiz a lo anterior: <b>si no hemos definido el Readiness en nuestro deployment, el pod siempre será considerado como "Ready".</b><br /></p><p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;"> Con todo esto, mi deployment de kubernetes queda así:</p><p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;"><br /></p><p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;"><br /></p><script src="https://gist.github.com/snavarropino/df8c8c26fe02964d5de0a08c0db07777.js"></script><h3 style="font-family: Calibri; margin: 0in; text-align: left;"><span style="font-size: medium;"> </span></h3><br /><h3 style="text-align: left;">¿Por qué es importante el readiness en escenarios de auto-escalado?</h3><p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;"><br /></p><p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;">Lo mencioné de pasada antes, merece explicarlo:</p><p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;"> </p><p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;">Si tenemos reglas de autoescalado, tarde o temprano, y en base a estas reglas, <b>nuevos pods serán creados para atender la creciente demanda</b>. En esos casos, una vez que el pod es creado, empezará a recibir tráfico de inmediato, a no ser que definamos las probes de readiness adecuadas. </p><p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;"> </p><p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;">Si recibe tráfico de inmediato, y no tenemos reglas de readiness, vamos a tener momentos "problemáticos", que son esos tiempos muertos desde que un pod arranca hasta que está listo para atender tráfico.</p><p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;"> </p><p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;">Sin embargo si definimos sondas de readiness, el nuevo pod no recibirá tráfico hasta que éstas sondas son satisfactorias, evitando así cualquier tipo de downtime. <br /></p><h3 style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;"><br /></h3><br /><h3 style="text-align: left;">¿Hay más tipos de sonda?</h3><p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;"><br /></p><p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;">Si, tenemos sondas de Liveness y de Startup, las veremos en otras entradas.<br /></p><p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;"><br /></p><p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;">Espero que os haya resultado interesante.<br /></p><p style="font-family: Calibri; font-size: 11pt; margin: 0in; text-align: left;"><br /></p><p style="text-align: left;"></p><p style="text-align: left;"></p>Sergio Navarro Pinohttp://www.blogger.com/profile/15840814836353973005noreply@blogger.com0Valdebebas, Madrid40.482641 -3.616334612.172407163821156 -38.7725846 68.792874836178839 31.539915399999998tag:blogger.com,1999:blog-7984577123741299127.post-49578074122151597072021-01-10T19:19:00.000+01:002021-01-10T19:19:05.583+01:00Service containers en Azure Pipelines, parte 3: comunicando contenedores<p>Hemos visto en <a href="http://www.serginet.com/2020/12/container-services-en-azure-pipelines.html">esta entrada</a> como podemos
ejecutar contenedores en el ámbito de una pipeline, pero ¿y si levantamos varios contenedores y queremos que se comuniquen entre ellos?</p><p>Imagina levantar un contenedor con tu aplicación (un api por ejemplo), otro con un Redis (dependencia de la aplicación), y desde tu pipeline poder lanzar requests para realizar tests.</p><p> </p><p> <img alt="" src="data:image/png;base64,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" /></p><p> </p><p>Podemos hacerlo haciendo uso de Azure Pipelines Service Containers, empecemos por definir nuestros contenedores como recursos de la pipeline<br /></p><script src="https://gist.github.com/snavarropino/c1bc7261c6dd197b8e83d92bef40abcc.js"></script><p><br /></p><p>Como veís he mapeado puertos para los dos contenedores, además de indicar en una variable de entorno como la aplicación puede acceder al Redis (<a href="https://dev.azure.com/sergionavarropino/Yaml-docker/_git/yaml-docker?path=%2Fsrc%2FSamurai.Api">aquí</a> tienes el código de la aplicación por si quieres echarle un ojo).</p><p>Con estos dos contenedores definidos, podríamos perfectamente lanzar una request a nuestra api, que haría uso del Redis para dar una respuesta. Es importante destacar que <b>los contenedores se ejecutarán dentro de una docker network</b>, mientras que el agente de Azure Pipelines no, lo que tiene implicaciones:</p><ul style="text-align: left;"><li> Los contentedores se pueden comunicar entre si usando el nombre del servicio, y sin que sea necesario mapear puertos</li><li>El agente de Azure DevOps accede a los contenedores usando localhost y el puerto mapeado.<br /></li></ul><p> Con lo anterior podríamos lanzar una request al api (localhost puerto 80) y ver que status code nos responde.</p><script src="https://gist.github.com/snavarropino/58b33becea4f0be3f651f05409f6ff8d.js"></script><p><br /></p><p>Si la respuestra no es un 200, el step fallará, y por tanto también fallará nuestro pipeline.</p><p>Fijaos que como decía el api podrá "hablar" con el Redis a través del nombre del servicio (<i>redis</i> en mi caso). Se hace a traves de la variable de entorno <i>CacheConnection</i>. Si tratásemos de acceder con localhost a redis desde el contenedor del api no llegaríamos.</p><p>Os dejo el fichero <a href="https://dev.azure.com/sergionavarropino/Yaml-docker/_git/yaml-docker?path=%2Fpipelines%2F4-service-containers-communication-agent-job.yml">yaml completo</a>.</p><h2 style="text-align: left;">Comunicando contendores con "Container jobs"<br /></h2><p></p><p>Veíamos tambien en <a href="http://www.serginet.com/2020/12/azure-pipelines-container-jobs.html">entradas anteriores</a> como ejecutar un job de nuestra pipeline dentro de un contenedor. Si ese fuese el caso habría que tener en cuenta un detalle:</p><p><b>El job se ejecuta en un contenedor que está en la misma docker network que los otros contenedores</b>, por lo que:</p><ul style="text-align: left;"><li>Puede acceder a ellos usando el nombre del servicio</li><li>No es necesario mapear puertos</li></ul><p>En este caso el yaml resultante difiere un poco, aquí os lo dejo:</p><script src="https://gist.github.com/snavarropino/3f6e12b0014d433ff86be5d0f7c32304.js"></script><p> </p><p>En breve dejaré un video donde podréis ver en vivo todo esto :-) <br /></p><p>Espero que os haya resultado de interés. Gracias!<br /><br /></p><h3 style="text-align: left;">Recursos:</h3><p>Repositorio con el código: <a href="https://dev.azure.com/sergionavarropino/Yaml-docker">https://dev.azure.com/sergionavarropino/Yaml-docker</a><br /></p>Sergio Navarro Pinohttp://www.blogger.com/profile/15840814836353973005noreply@blogger.com0tag:blogger.com,1999:blog-7984577123741299127.post-18702687400771435342020-12-27T23:19:00.008+01:002021-01-02T18:42:46.666+01:00Azure Pipelines Container Jobs<p><img alt="Use Azure Pipelines for Free with Self Hosted Agent! | by Aadesh Jain | Medium" class="n3VNCb" data-noaft="1" src="https://miro.medium.com/max/1000/1*3HZJ37Txq--fXMRg80IYPQ.png" style="height: 246.12px; margin: 0px; width: 586px;" /> <br /></p><p>En
entradas anteriores hemos visto como utilizar contenedores durante la
ejecución de tu pipeline en Azure DevOps. Ahora vamos a ver como hacer que <span style="font-weight: bold;">tu pipeline se ejecute dentro de un contenedor</span>,
utilizando la imagen que te interese. A esto lo llamamos "Container
jobs", <span style="background-color: white;"><a href="https://docs.microsoft.com/en-us/azure/devops/pipelines/process/container-phases?view=azure-devops"><span style="background-attachment: scroll; background-clip: border-box; background-image: none; background-origin: padding-box; background-position: 0% 0%; background-repeat: repeat; background-size: auto;">aquí</span></a> </span>podeis ver la documentación oficial.</p><h2 style="text-align: left;">Motivaciones</h2><p style="text-align: left;">
</p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;">¿Por qué
ejecutar la pipeline dentro de un contenedor?</p>
<p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<p lang="es" style="font-family: Calibri; font-size: 11pt; margin-left: .375in; margin: 0in;"><span> </span>"Nuestra pipeline puede necesitar software que no tenemos
disponible en nuestros agentes"</p>
<p lang="es" style="font-family: Calibri; font-size: 11pt; margin-left: .375in; margin: 0in;"> </p>
<p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;">Si usamos <span style="font-weight: bold;">agentes hosteados</span>, sabemos que no podemos
personalizarlos, tienen instalado cierto software base y de ahí no nos podemos
mover. No nos quedaría otra que instalar el software necesario en la propia
pipeline, mediante scripts, lo que resultaría en pipelines más lentas, algo que
no es deseable (ya hablaremos de ello en futuros post).</p>
<p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;">Por otro
lado, si usamos agentes propios, podríamos personalizarlos, para que ya
dispusiesen de ese software base que necesitamos, pero ¿y si esos agentes se
usan para varias pipelines? Si cada una de ella tiene sus propios
requerimientos, puede resultar en una cantidad de software grande a manejar, y
lo peor: colisiones entre versiones de dicho software.</p>
<p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;">Debido a lo
anterior, <b>podemos hacer que uno o varios jobs de nuestra pipeline se ejecuten
directamente en un contenedor cuya imagen podamos seleccionar</b> (o incluso crear)
de acuerdo a nuestras necesidades.</p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<h2 style="text-align: left;">¿Cómo lo hacemos?</h2><p style="text-align: left;"></p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;">Empezamos
definiendo el contenedor, basado en una imagen redis por ejemplo, para a continuación especificar que lo usaremos en nuestro job:</p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<script src="https://gist.github.com/snavarropino/012ba4c821070459daffadbf3db0f91e.js"></script>
<p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;">Al estar nuestro job ejecutandose en un contenedor que tiene un redis instalado, si lo necesitásemos por ejemplo para pruebas de integración, nos evitaríamos instalar esta dependencia.</p>
<p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;">Fijaos, el yaml completo de un ejemplo en el que lanzamos un ping a Redis sería este:</p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"><script src="https://gist.github.com/snavarropino/dd00aeb86df039731a76d1511cd635ed.js"></script></p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;">Como se puede ver, ya que disponemos de Redis en el contenedor donde nuestro job se ejecuta, podemos arrancarlo y lanzarle un ping.</p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"><br /></p><h2 style="text-align: left;">
¿Podemos usar nuestra propia imagen?
</h2><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;">Claro, aquí la cosa empieza a ponerse muy interesante. Podemos crear una imagen docker y utilizarla para que nuestro job de Azure Pipelines corra en ella.</p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;">Imaginemos que necesitamos las siguientes herramientas en nuestro pipeline:</p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p><ul style="text-align: left;"><li>Redis</li><li>El sdk de .net 5</li></ul><p>Podemos generar una imagen con un dockerfile así:</p><script src="https://gist.github.com/snavarropino/21378d32895af91412fc081445bffac8.js"></script><p> </p><p>Una vez subida la imagen a un docker registry (yo he usado mi cuenta en DockerHub, y la he llamado <span class="pl-s">snavarropino/agent-redis-net5</span>), podemos usarla para ejecutar allí nuestro pipeline, en el que compilaremos una aplicación .net 5 y lanzaremos sus tests de integración, que utilizan Redis.</p><p></p><div><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"></p> <script src="https://gist.github.com/snavarropino/7730c1f7a798d49caf41164a4aa64da8.js"></script> <br /></div><div>Como puede verse, una vez definida la imagen donde se ejecutará el único job de nuetsra pipeline, los pasos que ejecutamos son:</div><div><ul style="text-align: left;"><li>Restaurar paquetes nuget</li><li>Compilar la aplicación .net 5</li><li>Levantar el Redis</li><li>Ejecutar tests de integración (que usan Redis) <br /></li></ul><p>Si queréis ver como implemento este último ejemplo visitad este video en mi canal de YouTube:</p><p> </p><p><iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/_zJJKCj-1BE" width="560"></iframe><br /></p><p> </p><p>Espero que
os haya resultado de interés. Gracias!</p><p> </p><h3 style="text-align: left;">Recursos:</h3><p>Documentación oficila sobre Container Jobs: <a href="https://docs.microsoft.com/en-us/azure/devops/pipelines/process/container-phases?view=azure-devops">https://docs.microsoft.com/en-us/azure/devops/pipelines/process/container-phases?view=azure-devops</a></p><p>Repositorio con el código: <a href="https://dev.azure.com/sergionavarropino/Yaml-docker">https://dev.azure.com/sergionavarropino/Yaml-docker</a><br /></p></div>Sergio Navarro Pinohttp://www.blogger.com/profile/15840814836353973005noreply@blogger.com0tag:blogger.com,1999:blog-7984577123741299127.post-67327046193933043822020-12-12T13:02:00.009+01:002020-12-20T21:36:59.334+01:00Service containers en Azure Pipelines, parte 2<p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;">Después de
lo expuesto en la entrada anterior (enlace), continuamos profundizando en los
Services Container de Azure DevOps Pipelines.</p>
<p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;">Veíamos como
podíamos ejecutar en contenedor en el agente de Azure Pipelines para, por
ejemplo, ejecutar un Redis del que hiciesen uso nuestros test de integración. Sin embargo,
¿y si quisiéramos esos test de integración contra diferentes versiones de
Redis?</p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p><h2 style="text-align: left;"><span style="font-weight: normal;">Matrix strategy + service container</span></h2><div style="text-align: left;"><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;">Empecemos
por definir varios container resources, uno por cada versión de Redis que
queramos ejecutar, en nuestro caso las versiones 4, 5 y 6. </p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<script src="https://gist.github.com/snavarropino/14ad6993c1538f06cd7987e2122221ed.js"></script>
<p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;">
</p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;">Ahora la
<b>estrategia matrix</b> de azure pipelines (si no sabes que es echa un ojo a <a href="https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=azure-devops&tabs=schema%2Cparameter-schema#strategies">este</a>
enlace y este <a href="https://docs.microsoft.com/en-us/azure/devops/pipelines/get-started-multiplatform?view=azure-devops">otro</a>) hará su magia, ejecutando un job para cada valor pasado como variable a
la "matrix".</p>
<p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;">Definiremos también un servicio, que usando el valor de la variable que nos pasa la estrategia matrix, "apuntará" a uno de los
contenedores. El resultado es que en cada job ejecutaremos lo mismo (tests de
integración por ejemplo), pero contra una versión de Redis diferente.</p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"><br /></p>
<script src="https://gist.github.com/snavarropino/798ec3d24515dd9137731a01b9a250fa.js"></script>
<p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> Y con esto, solo queda usar el servicio para "atacar" a cada versión de Redis, así queda el pipeline completo:<br /></p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<script src="https://gist.github.com/snavarropino/4518c2bdf12b8f953734377eb173b63b.js"></script>
<p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;">Cuando se
ejecuta el pipeline, veremos que los 3 jobs generados ejecutan lo mismo (en el
ejemplo que estamos haciendo lanzar un ping a Redis, que contestará con un PONG</p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> <br /></p>
<p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> <img height="640" src="data:image/png;base64,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AFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIkjORuAAAgAElEQVQOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMsTRcKC2R2eiqsC4UF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AMAM4g4AAAAAWIy4AwAAAMAM4g4AAAAAWIy4AwAAAMAM4g4AAAAAWIy4AwAAAMAM4g4AAAAAWIy4AwAAAMAM4g4AAAAAWIy4AwAAAMAM4g4AAAAAWIy4AwAAAMAM4g4AAAAAWIy4AwAAAMAM4g4AAAAAWIy4AwAAAMAM4g4AAAAAWIy4AwAAAMAM4g4AAAAAWIy4AwAAAMAM4g4AAAAAWIy4AwAAAMAM4g4AAAAAWIy4AwAAAMAM4g4AAAAAWIy4AwAAAMAM4g4AAAAAWIy4AwAAAMAM4g4AAAAAWIy4AwAAAMAM4g4AAAAAWIy4AwAAAMAM4g4AAAAAWIy4AwAAAMAM4g4AAAAAWIy4AwAAAMAM4g4AAAAAWIy4AwAAAMAM4g4AAAAAWIy4AwAAAMAM4g4AAAAAWIy4AwAAAMAM4g4AAAAAWIy4AwAAAMAM4g4AAAAAWIy4AwAAAMAM4g4AAAAAWIy4AwAAAMAM4g4AAAAAWIy4AwAAAMAM4g4AAAAAWIy4AwAAAMAM4g4AAAAAWIy4AwAAAMAM4g4AAAAAWIy4AwAAAMAM4g4AAAAAWIy4AwAAAMCM/zY5OSkAAAAAgDUCgQBxBwAAAIApxB0AAAAAsEggEGDlDgAAAADTuC0bAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIOAAAAAFiMuAMAAADADOIO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AAB+EHcAAAAAIGDEHQAAAAB+EHcAAAAAIGDEHQAAAAB+EHcAAAAAIGDEHQAAAAB+EHcAAAAAIGDEHQAAAAB+EHcAAAAAIGDEHQAAAAB+EHcAAAAAIGDEHQAAAAB+EHcAAAAAIGDEHQAAAAB+EHcAAAAAIGDEHQAAAAB+EHcAAAAAIGDEHQAAAAB+EHcAAAAAIGDEHQAAAAB+EHcAAAAAIGDEHQAAAAB+EHcAAAAAIGDEHQAAAAB+EHcAAAAAIGDEHQAAAAB+EHcAAAAAIGDEHQAAAAB+EHcAAAAAIGDEHQAAAAB+EHcAAAAAIGDEHQAAAAB+EHcAAAAAIGDEHQAAAAB+EHcAAAAAIGDEHQAAAAB+EHcAAAAAIGDEHQAAAAB+EHcAAAAAIGDEHQAAAAB+EHcAAAAAIGDEHQAAAAB+EHcAAAAAIGDEHQAAAAB+EHcAAAAAIGDEHQAAAAB+EHcAAAAAIGDEHQAAAAB+EHcAAAAAIGDEHQAAAAB+EHcAAAAAIGDEHQAAAAB+EHcAAAAAIGDEHQAAAAB+EHcAAAAAIGDEHQAAAAB+EHcAAAAAIGDEHQAAAAB+/L9isSgAAAAAQDBKpRJxBwAAAIAvxB0AAAAACEipVGLmDgAAAADfWJYNAAAAAAJG3AEAAADgB3EHAAAAAAJG3AEAAADgB3EHAAAAAAJG3AEAAADgB3EHAAAAAAJG3AEAAADgB3EHAAAAAAJG3AEAAADgB3EHAAAAAAJG3AEAAADgB3EHAAAAAAJG3AEAAADgB3EHAAAAAAJG3AEAAADgB3EHAAAAAAJG3AEAAADgB3EHAAAAAAJG3AEAAADgB3EHAAAAAAJG3AEAAADgB3EHAAAAAAJG3AEAAADgB3EHAAAAAAJG3AEAAADgB3EHAAAAAAJG3AEAAADgB3EHAAAAAAJG3AEAAADgB3EHAAAAAAJG3AEAAADgB3EHAAAAAAJG3AEAAADgB3EHAAAAAAJG3AEAAADgB3EHAAAAAAJG3AEAAADgB3EHAAAAAAJG3AEAAADgB3EHAAAAAAJG3AEAAADgB3EHAAAAAAJG3AEAAADgB3EHAAAAAAJG3AEAAADgB3EHAAAAAAJG3AEAAADgB3EHAAAAAAJG3AEAAADgB3EHAAAAAAJG3AEAAADgB3EHAAAAAAJG3AEAAADgB3EHAAAAAAJG3AEAAADgB3EH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3AEAANIQdwAAANpM3AEAANIQdwAAANpM3AEAANIQdwAAANpM3AEAANIQdwAAANpM3AEAANIQdwAAANpM3AEAANIQdwAAANpM3AEAANIQdwAAANpM3AEAANIQdwAAANpM3AEAANIQdwAAANpM3AEAANIQdwAAANpM3AEAANIQdwAAANpM3AEAANIQdwAAANpM3AEAANIQdwAAANpM3AEAANIQdwAAANpM3AEAANIQdwAAANpM3AEAANIQdwAAANpM3AEAANIQdwAAANpM3AEAANIQdwAAANpM3AEAANIQdwAAANpM3AEAANIQdwAAANpM3AEAANIQdwAAANpM3AEAANIQdwAAANpM3AEAANIQdwAAANpM3AEAANIQdwAAANpM3AEAANIQdwAAANpM3AEAANIQdwAAANpM3AEAANIQdwAAANpM3AEAANIQdwAAANpM3AEAANIQdwAAANpM3AEAANIQdwAAANpM3AEAANIQdwAAANpM3AEAANIQdwAAANpM3AEAANIQdwAAANpM3AEAANIQdwAAANpM3AEAANIQdwAAANpM3AEAANIQdwAAANpM3AEAANIQdwAAANpM3AEAANIQdwAAANpM3AEAANIQdwAAANpM3AEAANL4/0aV1FFI5ANqAAAAAElFTkSuQmCC" width="640" /></p> </div><div style="text-align: left;"><p style="text-align: left;">
</p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;">Si queréis verme hacer este ejemplo en vivo, os dejo un video en mi canal de YouTube:</p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"></p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/09tohwRjfmY" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
<p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;">Espero que
os haya resultado de interés. Gracias!</p><h3 style="text-align: left;">Recursos: </h3><p style="text-align: left;">Documentación oficial sobre la estrategia Matrix:</p><p style="text-align: left;"><a href="https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=azure-devops&tabs=schema%2Cparameter-schema#strategies">https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=azure-devops&tabs=schema%2Cparameter-schema#strategies</a><br /></p><p style="text-align: left;">Documentación oficial sobre Service Containers: </p><p style="text-align: left;"><a href="https://docs.microsoft.com/en-us/azure/devops/pipelines/process/service-containers?view=azure-devops&tabs=yaml">https://docs.microsoft.com/en-us/azure/devops/pipelines/process/service-containers?view=azure-devops&tabs=yaml</a></p></div>Sergio Navarro Pinohttp://www.blogger.com/profile/15840814836353973005noreply@blogger.com0Valdebebas, Madrid, España40.482641 -3.6163346-0.69317156026696836 -73.9288346 81.658453560266963 66.6961654tag:blogger.com,1999:blog-7984577123741299127.post-32925221914525451522020-12-06T22:55:00.005+01:002020-12-20T21:38:27.400+01:00Service containers en Azure Pipelines<p style="font-family: Calibri; font-size: 11pt; margin: 0in;">Es habitual (y
deseable) que nuestros pipelines ejecuten tests, que suelen habitualmente ser
unitarios o de integración. </p><p style="font-family: Calibri; font-size: 11pt; margin: 0in;"><br /></p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;">En el segundo caso
podemos requerir de servicios externos como bases de datos, servicios de cache,
colas u otros.</p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;">Puede que esos
servicios se encuentren ya disponibles en nuestro agente de pipelines, como es el
caso de Sql Server LocalDB en los agentes hosteados de Azure DevOps, lo que sin
duda simplifica las cosas, pero en otros casos esos servicios no estarán
disponibles. </p><p style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p><h1 style="font-family: Calibri; margin: 0in; text-align: left;"><span style="font-size: x-large;">¿Qué opciones tenemos?</span></h1>
<p style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<ul style="margin-bottom: 0in; margin-top: 0in; text-align: left; unicode-bidi: embed;" type="disc"><li style="margin-bottom: 0px; margin-top: 0px; vertical-align: middle;"><span style="font-family: Calibri; font-size: 11pt;">Podemos utilizar un <b>servicio
externo al agente</b>. <br /></span></li></ul>
<p style="font-family: Calibri; font-size: 11pt; margin-bottom: 0in; margin-left: .375in; margin-right: 0in; margin-top: 0in; text-align: left;"> </p>
<p style="font-family: Calibri; font-size: 11pt; margin-left: .375in; margin: 0in 0in 0in 40px; text-align: left;">Por
ejemplo, si necesitásemos acceso a un Redis, podríamos desplegarlo en Azure u
otro Cloud, y desde nuestros tests usarlo. Es una solución sencilla, pero
presenta varios inconvenientes que hacen que no sea la más optima.</p><div style="margin-left: 40px; text-align: left;">
</div><p style="font-family: Calibri; font-size: 11pt; margin-left: .375in; margin: 0in 0in 0in 40px; text-align: left;"> </p><div style="margin-left: 40px; text-align: left;">
</div><p style="font-family: Calibri; font-size: 11pt; margin-left: .375in; margin: 0in 0in 0in 40px; text-align: left;">Uno
de los problemas es el económico: tener provisionado un Redis 24/7 cuando solo
lo necesitaremos al ejecutar nuestras pipelines no parece lo más optimo.</p><div style="margin-left: 40px; text-align: left;">
</div><p style="font-family: Calibri; font-size: 11pt; margin-left: .375in; margin: 0in 0in 0in 40px; text-align: left;"> </p><div style="margin-left: 40px; text-align: left;">
</div><p style="font-family: Calibri; font-size: 11pt; margin-left: .375in; margin: 0in 0in 0in 40px; text-align: left;">Por
otro lado disponer de un solo Redis nos impediría ejecutar varias pipelines en
paralelo, ya que si dos se ejecutasen a la vez, nuestros tests podrían
colisionar, arrojando resultados erráticos.</p>
<p style="font-family: Calibri; font-size: 11pt; margin-left: .375in; margin: 0in;"> </p>
<ul style="margin-bottom: 0in; margin-top: 0in; text-align: left; unicode-bidi: embed;" type="disc"><li style="margin-bottom: 0px; margin-top: 0px; vertical-align: middle;"><span style="font-family: Calibri; font-size: 11pt;">Podemos <b>instalar Redis en
nuestros agentes</b>. </span></li></ul>
<p style="font-family: Calibri; font-size: 11pt; margin-bottom: 0in; margin-left: .375in; margin-right: 0in; margin-top: 0in; text-align: left;"> </p>
<p style="font-family: Calibri; font-size: 11pt; margin-left: .375in; margin: 0in 0in 0in 40px; text-align: left;">Sí,
es una opción, pero descarta los agentes hosteados y nos obliga a usar agentes
self-hosted, es decir que nosotros deberemos proporcionar máquinas virtuales o
contenedores donde se ejecutarán nuestras pipelines.</p><div style="margin-left: 40px; text-align: left;">
</div><p style="font-family: Calibri; font-size: 11pt; margin-left: .375in; margin: 0in 0in 0in 40px; text-align: left;"> </p><div style="margin-left: 40px; text-align: left;">
</div><p style="font-family: Calibri; font-size: 11pt; margin-left: .375in; margin: 0in 0in 0in 40px; text-align: left;">Es
un escenario muy común, pero aun así el hecho de tener que instalar software en
nuestros agentes complica su gestión y nos impide usar Hosted agents a futuro
en caso de necesidad.</p><div style="margin-left: 40px; text-align: left;">
</div><p style="font-family: Calibri; font-size: 11pt; margin-left: .375in; margin: 0in 0in 0in 40px; text-align: left;"> </p><div style="margin-left: 40px; text-align: left;">
</div><p style="font-family: Calibri; font-size: 11pt; margin-left: .375in; margin: 0in 0in 0in 40px; text-align: left;">Por
otro lado, ¿imagináis que no tenemos una dependencia sino varias? Las cosas sin
duda se complican.</p>
<p style="font-family: Calibri; font-size: 11pt; margin-bottom: 0in; margin-left: .375in; margin-right: 0in; margin-top: 0in; text-align: left;"> </p>
<ul style="margin-bottom: 0in; margin-top: 0in; text-align: left; unicode-bidi: embed;" type="disc"><li style="margin-bottom: 0px; margin-top: 0px; vertical-align: middle;"><span style="font-family: Calibri; font-size: 11pt;">Podemos <b>usar redis como
servicio en nuestra pipeline</b>.</span></li></ul>
<p style="font-family: Calibri; font-size: 11pt; margin-bottom: 0in; margin-left: .375in; margin-right: 0in; margin-top: 0in; text-align: left;"> </p>
<p style="font-family: Calibri; font-size: 11pt; margin-left: .375in; margin: 0in 0in 0in 40px; text-align: left;">Si,
podemos hacer que nuestro agente, hosted o self-hosted, ejecute una imagen
docker de nuestro servicio, de modo que nuestros tests puedan hacer uso de la
misma.</p>
<p style="font-family: Calibri; font-size: 11pt; margin-left: .375in; margin: 0in;"> </p>
<p style="font-family: Calibri; font-size: 11pt; margin-left: .375in; margin: 0in;">Vamos
a explorar esta última opción.</p><p style="font-family: Calibri; font-size: 11pt; margin-left: .375in; margin: 0in;"> </p><h1 style="font-family: Calibri; font-size: 11pt; margin-left: .375in; margin: 0in; text-align: left;"><span style="font-size: x-large;">Pipeline yaml </span><br /></h1><p style="font-family: Calibri; font-size: 11pt; margin-left: .375in; margin: 0in;"> </p><p style="font-family: Calibri; font-size: 11pt; margin-left: .375in; margin: 0in;"></p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;">Volvamos al
caso del Redis y pensemos en una pipeline que necesite usarlo al ejecutar tests
de integración.</p>
<p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;">Podemos
definir que nuestro agente ejecute un contenedor con la última versión de
redis. Para ello solo necesitaremos definir un elemento container como se ve a
continuación:</p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<script src="https://gist.github.com/snavarropino/df122ee83e1bafdb297b28fda2901302.js"></script>
<p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;">Con lo
anterior, definimos un servicio llamado redis basado en dicho contenedor, que
podremos usar en nuestra pipeline.</p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<script src="https://gist.github.com/snavarropino/7ac6c9c052886937b5a6c6b7562d95a7.js"></script>
<p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;">Como veis,
instalamos redis-cli para después usarlo lanzando un ping a nuestro Redis, al
que como es habitual en docker, podremos acceder a través del localhost y el
puerto mapeado.</p>
<p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;">La pipeline
completa queda así:</p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<script src="https://gist.github.com/snavarropino/017e726ad4ae984bf18a252ce35ebfbb.js"></script>
<p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;">No es
complejo: igual que hemos mandado un ping a Redis, nuestros tests podrían acceder al mismo utilizando
localhost:6379.<span style="mso-spacerun: yes;"> </span></p>
<p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;">A partir de
aquí las posibilidades son ingentes: ¿necesitas usar nginx, <span class="aCOpRe"><span>Postgres<i> </i></span></span>o RabbitMq? Busca su imagen docker y adelante!</p>
<p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<h1 lang="es" style="font-family: Calibri; margin: 0in; text-align: left;"><span style="font-size: x-large;">¿Quieres
ver lo anterior funcionando?</span></h1><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;">Te dejo un breve video de mi canal con un ejemplo un poco más elaborado: </p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" frameborder="0" height="506" src="https://www.youtube.com/embed/tdesA3st9FU" width="900"></iframe>
<p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;">En la siguiente entrada profundizaremos un poco más sobre estos <i><b>Services Containers</b></i> e introduciremos un nuevo concepto: los <i><b>container jobs</b></i>.<br /></p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p><p lang="es" style="font-family: Calibri; margin: 0in;"><span style="font-size: large;">Otros recursos:</span></p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;">Documentación oficial: <a href="https://docs.microsoft.com/en-us/azure/devops/pipelines/process/service-containers?view=azure-devops&tabs=yaml">https://docs.microsoft.com/en-us/azure/devops/pipelines/process/service-containers?view=azure-devops&tabs=yaml</a><br /></p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p><p> </p><p> </p><p> </p><p> </p><p lang="es" style="font-family: Calibri; font-size: 11pt; margin: 0in;"> </p>
Sergio Navarro Pinohttp://www.blogger.com/profile/15840814836353973005noreply@blogger.com0tag:blogger.com,1999:blog-7984577123741299127.post-69209989258520110882018-11-04T18:59:00.005+01:002018-11-06T07:58:12.180+01:00La cobertura de código, .NET Core y Azure DevOps!<br />
Martín Fowler tiene un buen <a href="https://martinfowler.com/bliki/TestCoverage.html" target="_blank">artículo</a> acerca de la cobertura de código:<br />
<blockquote class="tr_bq">
<span style="background-color: transparent; color: #303633; display: inline; float: none; font-family: "opensans" , sans-serif; font-size: 16px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px;">"From time to time I hear people asking what value of test coverage (also
called code coverage) they should aim for, or stating their coverage
levels with pride. Such statements miss the point. Test coverage is
a useful tool for finding untested parts of a codebase. Test
coverage is of little use as a numeric statement of how good your
tests are."</span></blockquote>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHR3nDKGgq5nRUHsB5FQntyhLV4orTJhF9w11s5awYDbreZb326_ohpGslRgbFn3v_oLxCFkXeVyngc5BrPbXakuZcl4U-4f2zqdRMwaxpIX6LnDXYGYF4UGFI0rZ0o_59AcXPtWpMGYyF/s1600/fowler.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="185" data-original-width="540" height="136" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHR3nDKGgq5nRUHsB5FQntyhLV4orTJhF9w11s5awYDbreZb326_ohpGslRgbFn3v_oLxCFkXeVyngc5BrPbXakuZcl4U-4f2zqdRMwaxpIX6LnDXYGYF4UGFI0rZ0o_59AcXPtWpMGYyF/s400/fowler.png" width="400" /></a></div>
<br />
Yo estoy 100% de acuerdo, es valioso conocer que partes de nuestro código no están cubiertas por tests, y para ello necesitamos herramientas que nos ofrezcan dicha información.<br />
<br />
<h2>
¿Cual es el estado en de la cobertura de código en .NET Core? </h2>
<br />
Cuando apareció .NET Core a algunos nos sorprendió que la capacidad de analizar la cobertura de código de nuestros tests había desaparecido, despues haber estado mucho tiempo disponible en .NET Framework.<br />
<br />
Varias issues en github pedían su vuelta (<a href="https://github.com/Microsoft/vstest/issues/579" target="_blank">579</a><a href="https://github.com/Microsoft/vstest/issues/981" target="_blank">, 981</a> y <a href="https://github.com/Microsoft/vstest/issues/902" target="_blank">902</a> por ejemplo) pero el tiempo pasaba, las versiones de .NET Core se sucedían y seguíamos sin esta feature. Afortunadamente hay varias alternativas, tanto de pago (<a href="https://www.jetbrains.com/dotcover/" target="_blank">DotCover</a>) como gratuitas (<a href="https://github.com/OpenCover/opencover" target="_blank">OpenCover</a> y <a href="https://github.com/tonerdo/coverlet" target="_blank">Coverlet</a>), por lo que la ausencia de soporte “de serie” no ha sido determinante. Numerosos artículos muestran cómo hacerlo, como <a href="https://www.hanselman.com/blog/AutomaticUnitTestingInNETCorePlusCodeCoverageInVisualStudioCode.aspx" title="">este</a> de Scott Hanselman que fue el primero que leí.<br />
<div>
<br /></div>
<div>
Finalmente con el SDK 2.1.400 de .NET Core recuperamos esta capacidad de disponer de datos de cobertura de código. Vamos a ver como explotarla, viene un post largo...<br />
<br />
<h2>
Cobertura desde Visual Studio </h2>
<br />
Vamos a empezar, veamos cómo obtener la cobertura de nuestros test en Visual Studio:<br />
<br />
<ol>
<li>Debemos referenciar, en nuestro proyecto de pruebas, el paquete <a href="http://microsoft.net.test.sdk/">Microsoft.NET.Test.Sdk</a></li>
<li>Una vez hecho, podemos pedir a Visual Studio la cobertura </li>
</ol>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjn6M2P2jv3yBLZ0mQwRZOEojwuYaqiWH3ccDadkNf7HcoukZHrIqB3qsBqb8uJQu-6ywAKN21K6s2noMP4iQ3QbvK-ly07Je9Gjv27aM63gHjpfOesJcdDbfqA41oeZPhIWZYiBfxI0JSY/s1600/Desde+VS.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="712" data-original-width="1161" height="392" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjn6M2P2jv3yBLZ0mQwRZOEojwuYaqiWH3ccDadkNf7HcoukZHrIqB3qsBqb8uJQu-6ywAKN21K6s2noMP4iQ3QbvK-ly07Je9Gjv27aM63gHjpfOesJcdDbfqA41oeZPhIWZYiBfxI0JSY/s640/Desde+VS.png" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Solicitar cobertura</td></tr>
</tbody></table>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfOpRLMIk_UUHDBE4k7F-ig9snM2t8rZCWZ5KxmZUuwda28nWAZML9gevTyoBp2VvDkXfcZrMrYjZAIT_K4pTWQlsYH7uqKTmD9D81Eef3Aahs3TUR1rAdmXFPVSAZMaun13GAU7iRuPNj/s1600/Resultados1.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="297" data-original-width="1088" height="172" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfOpRLMIk_UUHDBE4k7F-ig9snM2t8rZCWZ5KxmZUuwda28nWAZML9gevTyoBp2VvDkXfcZrMrYjZAIT_K4pTWQlsYH7uqKTmD9D81Eef3Aahs3TUR1rAdmXFPVSAZMaun13GAU7iRuPNj/s640/Resultados1.png" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Resultados de cobertura</td></tr>
</tbody></table>
<br />
<br />
Aquí merece la pena decir que la <a href="https://github.com/Microsoft/vstest-docs/blob/master/docs/analyze.md#coverage" target="_blank">documentación oficial</a> indica que añadamos algo más al csproj, y es el modo de debug Full. Debo decir que a mi nunca me ha hecho falta, aunque es algo que quiero analizar.<br />
<br />
Un detalle importante, esto <b>solo esta disponible en VisualStudio 2017 Enterprise</b> :-( <br />
<br />
Por otro lado, si os fijais en el resultado de cobertura veréis que se está teniendo en cuenta el propio código de los tests, ya escribí acerca de esto hace tiempo (<a href="https://www.serginet.com/2017/06/tip-codecoverage-vsts.html">https://www.serginet.com/2017/06/tip-codecoverage-vsts.html</a>), no voy a extenderme ahora.<br />
<br />
<b>Tip:</b> si no conseguis que Visual Studio os muestre información de cobertura, podéis probar a actualizar el paquete Microsoft.NET.Test.Sdk a una versión reciente.<br />
<br />
<h2>
Cobertura desde el CLI</h2>
<br />
¿Y si no tenemos Visual Studio Enterprise? ¿Quizás no trabajamos en Windows? ¿Y si preferimos Visual Studio Code?<br />
<br />
No todo está perdido, ahora empieza la parte interesante de esta entrada, y es que aún podremos conseguir resultados de cobertura, apoyándonos en AzureDevops. Empecemos por al principio: obtener datos de cobertura con el CLI de .NET Core.<br />
<br />
Nos situamos en el proyecto de tests y ejecutamos <br />
<br />
<div style="text-align: center;">
<b>dotnet test --collect:"Code Coverage"</b></div>
<br />
Esto hará que un fichero binario, de extensión .coverage, aparezca dentro de una carpeta de nombre TestResults.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2y-4P5VHQE1_qifukSsl6VCGBKamAvTVjDtGowK7eqkhF0pZw5r6an2mDuJYmc5gTfrNceT8oET-pXpADoBSQmBk9xwDrVl0_U9e_gBUf6bsZnuLZ83guiVzqOnvld24vJX-RrmsOOGrA/s1600/testResults.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="144" data-original-width="942" height="96" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2y-4P5VHQE1_qifukSsl6VCGBKamAvTVjDtGowK7eqkhF0pZw5r6an2mDuJYmc5gTfrNceT8oET-pXpADoBSQmBk9xwDrVl0_U9e_gBUf6bsZnuLZ83guiVzqOnvld24vJX-RrmsOOGrA/s640/testResults.png" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Fichero .coverage</td></tr>
</tbody></table>
<br />
Recuerda que igual que antes debes referenciar Microsoft.NET.Test.Sdk desde tu proyecto de tests.<br />
<br />
<b>¿Cómo explotamos este fichero?</b> Como comentaba es binario y podemos arrastrarlo a Visual Studio Enterprise para ver detalles de cobertura. ¿Perdona? Si ya se, hemos vuelto al principio, a la dependencia con Visual Studio Enterprise.<br />
<br />
Nos saltaremos esta dependencia con una Build de Azure DevOps, pero antes vamos a hablar de <a href="https://github.com/danielpalme/ReportGenerator" target="_blank">Report Generator</a>, una interesante herramienta que transforma ficheros de cobertura en diversos formatos a nuevos formatos legibles por un humano. ¿Suena bien? Pues la primera en la frente, no soporta el formato .coverage, si queremos un buen informe debemos primero convertir el fichero de cobertura obtenido a un formato XML soportado.<br />
<br />
Desde la propia <a href="https://github.com/danielpalme/ReportGenerator/wiki/Visual-Studio-Coverage-Tools#vstestconsoleexe" target="_blank">documentación </a>de Report Generator nos guían para conseguirlo: podemos hacer uso de una herramienta instalada por Visual Studio que nos permitirá la conversión, <b>CodeCoverage.exe</b>.<br />
<br />
Su uso para este fin es sencillo: debemos usar el comando analyze, pasándole el fichero .coverage y especificando el fichero resultado que deseamos:<br />
<br />
<br />
<b>CodeCoverage.exe</b> analyze
/output:informe.coveragexml informe.coverage<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipHVtyFNkJ1Cttslj8sgeZ277KNfOgT8VElKIAPhLApUx1_MVVRrlmPAj5Nr2rk0nLYDyLLBdX8MDG-awZG5drSWJM7zx2iOH2T77tgMLjwkYST4Jgw_jo3T-NlJn9g-7reqHNmPVJ1zWj/s1600/enXML.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="219" data-original-width="1101" height="124" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipHVtyFNkJ1Cttslj8sgeZ277KNfOgT8VElKIAPhLApUx1_MVVRrlmPAj5Nr2rk0nLYDyLLBdX8MDG-awZG5drSWJM7zx2iOH2T77tgMLjwkYST4Jgw_jo3T-NlJn9g-7reqHNmPVJ1zWj/s640/enXML.png" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">CodeCoverage.exe</td></tr>
</tbody></table>
<br />
<br />
Podeis encontrar esta herramienta en la instalación de Visual Studio, aquí:<br />
C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Team Tools\Dynamic Code Coverage Tools<br />
<br />
Una vez con el <b>fichero de cobertura en formato XML</b>, podemos finalmente utilizar <b>Report Generator</b> para obtener nuestro informe de cobertura:<br />
<br />
<ol>
<li>Añadimos la tool de report generator (<DotNetCliToolReference Include="dotnet-reportgenerator-cli" Version="4.0.2" />) en el csproj del proyecto de tests. <br /> </li>
<li>La ejecutamos:<b> </b></li>
</ol>
<b>dotnet reportgenerator</b> "-reports:informe.coveragexml" "-targetdir:CoverageReport" "-reporttypes:HTML;HTMLSummary"<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgixHCwY0U-eFMCo_G6liiIyzgsWMooeWK0zPJT9CoziWS7LpsZPUA191mvWaP0zlQpSMv7Nx8o-W_TmCrf0se3_gOp4gdYoPZBlWsu4dYoAzKKjn5EyPyCDIQuuRrvn6Vq-30SQ4GaB914/s1600/generator.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="381" data-original-width="1103" height="219" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgixHCwY0U-eFMCo_G6liiIyzgsWMooeWK0zPJT9CoziWS7LpsZPUA191mvWaP0zlQpSMv7Nx8o-W_TmCrf0se3_gOp4gdYoPZBlWsu4dYoAzKKjn5EyPyCDIQuuRrvn6Vq-30SQ4GaB914/s640/generator.png" width="640" /></a></div>
<br />
<br />
Con esto obtendremos un <b>informe detallado en HTML</b>, que incluso nos muestra el código por el que han pasado nuestros tests. Nada que envidiar a las capacidades de Visual Studio Enterprise<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFUN5VCaLZywR7RSRxEnJsxopzETCObeTvUh8XSDR6dJn6ZHlJsXnuu6MhKFQIw-8PkfbYd5vaS_fSrOGimwJSxmC3konZ8j0EcdeSqP5-pgbIHef2ySUJvH-tqS60aLc2nKezcwEx9boY/s1600/Informe+1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="878" data-original-width="1600" height="350" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFUN5VCaLZywR7RSRxEnJsxopzETCObeTvUh8XSDR6dJn6ZHlJsXnuu6MhKFQIw-8PkfbYd5vaS_fSrOGimwJSxmC3konZ8j0EcdeSqP5-pgbIHef2ySUJvH-tqS60aLc2nKezcwEx9boY/s640/Informe+1.png" width="640" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg9382JaakAyVdEc3QO0DNlHjXiHWpYqKRVYm07YIFBe8ycIczoMoW0dznNOIZq67OfbgsQY6y4x4Gza_RCoY2Y78vpq2ST6rlvcVX7q0FFro-U0GPTocvslbQzSUvofdIGf2C9JkqQjiSE/s1600/Informe2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="857" data-original-width="1600" height="342" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg9382JaakAyVdEc3QO0DNlHjXiHWpYqKRVYm07YIFBe8ycIczoMoW0dznNOIZq67OfbgsQY6y4x4Gza_RCoY2Y78vpq2ST6rlvcVX7q0FFro-U0GPTocvslbQzSUvofdIGf2C9JkqQjiSE/s640/Informe2.png" width="640" /></a></div>
<br />
<br />
<b>Nota:</b> si hay algún assembly del que no tenéis código (un paquete de terceros que no hayais excluido de la cobertura por ejemplo) la herramienta mostrará avisos, pero aun así generará el informe.<br />
<br />
<b>Resumiendo</b>, tenemos un buen informe de cobertura, pero aun tenemos una dependencia con Visual Studio Enterprise. Vamonos a Azure DevOps y consigamos el informe allí.<br />
<br />
<h2>
Cobertura en Azure DevOps</h2>
<br />
De entrada, conseguir datos de cobertura en Azure DevOps es muy sencillo. Tan solo tenemos que meter una tarea de tipo dotnet test, y <b>habilitar el check que indica que se publiquen los resultados de test y su cobertura</b>.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiU1lawHXz_sobVIjRknDFEFy652HyDDEXS2K2S2d5yImmfkKfbuqhAZ8a5d0giDk_gsB-7MOGedfrqReHWQ5IPSd71r8nB6UNSAHEYZtB7p4erHfPaNMKT3bPRQGcEQU-nua4PCFNTTHjd/s1600/Build1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="701" data-original-width="1528" height="293" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiU1lawHXz_sobVIjRknDFEFy652HyDDEXS2K2S2d5yImmfkKfbuqhAZ8a5d0giDk_gsB-7MOGedfrqReHWQ5IPSd71r8nB6UNSAHEYZtB7p4erHfPaNMKT3bPRQGcEQU-nua4PCFNTTHjd/s640/Build1.png" width="640" /></a></div>
<br />
<br />
Esto va a funcionar, pero el resultado que nos dará será más bien pobre. Simplemente un porcentaje de cobertura y el fichero .coverage para que lo descarguemos. <b>Ese porcentaje de poco vale, recuerda el artículo de Fowler.</b><br />
<br />
<h2>
¿Podemos publicar en Azure DevOps un buen informe?</h2>
<br />
Claro! para ello solo debemos saber tres cosas:<br />
<br />
<ul>
<li>Azure DevOps soporta informes de cobertura detallados en dos formatos: <b>Cobertura </b>y JaCoco (puedes verlo <a href="https://blogs.msdn.microsoft.com/devops/2016/06/29/browse-code-coverage-reports/" target="_blank">aquí</a>), así como <b>informes Html</b></li>
<li> En los agentes de build disponemos de <b>CodeCoverage.exe</b>, lo que nos habilita el uso de <b>Report Generator</b></li>
<li>Report Generator nos permite generar un reporte en formato <b>Cobertura</b>!</li>
</ul>
<br />
Vamos a ello:<br />
<br />
<br />
1. Nos debemos <b>asegurar que tenemos desmarcada la opción de publicar resultados de tests y de cobertura</b>. Queremos hacerlo nosotros.<br />
<br />
2. <b>Añadimos parametros a dotnet test</b> para que genere la información de test y de cobertura. Además indicamos que dicha informaciónla guarde en la carpeta temporal del agente de build. Los parámetros son:<br />
<ul>
<li> --logger trx</li>
<li>--collect:"Code Coverage"</li>
<li>--results-directory $(Agent.TempDirectory) </li>
</ul>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkBiybQR3fZTxrwyG79F_xvWFpP8qzWK4SlTHy7IYr0qXpB2HcSNYW1kd1uD4VHWq3vuYz99P5cKD1wUFKHyCfDFLPP7iGibB2baU43DoHt3Yia4kxdmiiRs9f8AoVYUTrUJdEW2pdR2RW/s1600/Build2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="675" data-original-width="754" height="357" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkBiybQR3fZTxrwyG79F_xvWFpP8qzWK4SlTHy7IYr0qXpB2HcSNYW1kd1uD4VHWq3vuYz99P5cKD1wUFKHyCfDFLPP7iGibB2baU43DoHt3Yia4kxdmiiRs9f8AoVYUTrUJdEW2pdR2RW/s400/Build2.png" width="400" /></a></div>
<br />
<br />
3. <b>Publicamos el resultado de la ejecución de tests</b>, no queremos perder esa información. Para ello metemos una tarea de tipo "Publish Test Results", configurándola como se ve a continuación:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfrgZ2FfkFdx1oeGimks6VfKDnv4_L3MRtukBy1I2t6nUOUqVS_8wd2HasFlZ45wljZzWaONmdyRMTlQhyphenhyphen5JfrY0Ff7mi2QmaA6mbPEmFJb1grC06XRL9sgFuEr7aVjpTkKD3iM8m6wfwR/s1600/publish+test+results.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="687" data-original-width="753" height="363" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfrgZ2FfkFdx1oeGimks6VfKDnv4_L3MRtukBy1I2t6nUOUqVS_8wd2HasFlZ45wljZzWaONmdyRMTlQhyphenhyphen5JfrY0Ff7mi2QmaA6mbPEmFJb1grC06XRL9sgFuEr7aVjpTkKD3iM8m6wfwR/s400/publish+test+results.png" width="400" /></a></div>
<br />
<br />
<br />
4. Ahora vamos a <b>convertir el archivo .coverage al formato xml</b>. Para ello metemos una tarea de tipo powershell, que ejecute el siguiente script:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZ1yNoOA9lrKhd21FTbTSqAQJw2T2YCbLCgDoFEnoHrQLyUpZbzMUQwC6_3__l-XUZIl3hN-qyzxPzC3b57jZXcCN3PfvHIyuIPJ4D9zkB3oQntbPZMYM2d53i72ImmqPnWge1P0x-RAEN/s1600/pw1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="324" data-original-width="1252" height="163" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZ1yNoOA9lrKhd21FTbTSqAQJw2T2YCbLCgDoFEnoHrQLyUpZbzMUQwC6_3__l-XUZIl3hN-qyzxPzC3b57jZXcCN3PfvHIyuIPJ4D9zkB3oQntbPZMYM2d53i72ImmqPnWge1P0x-RAEN/s640/pw1.png" width="640" /></a></div>
<br />
<br />
<br />
El script es sencillo, lista ficheros .coverage en la carpeta temporal del agente, y por cada uno lo convierte, del mismo modo que lo hicimos en nuestro equipo. Fijaos que los archivos de salida en formato XML los estamos dejando junto a los fuentes (usando la variable $(Build.SourcesDirectory)). Podríamos dejarlos en otro sitio, el caso es poder acceder a ellos.<br />
<br />
La tarea del pipeline queda así:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPIiIJxVFiT9BuSYIvKM0pm4EEa-DVeK3sjGcNR_5WJh1W7kwLYSPepKIZb20TYD7mC0YvsdDcZUqoojpiYrNgjWcMyb8JPcmVAf4ggxxO_8vJ4Cpch-16UPUd0PBI655EEU4jt_uiuNSd/s1600/taskPS.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="696" data-original-width="1056" height="419" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPIiIJxVFiT9BuSYIvKM0pm4EEa-DVeK3sjGcNR_5WJh1W7kwLYSPepKIZb20TYD7mC0YvsdDcZUqoojpiYrNgjWcMyb8JPcmVAf4ggxxO_8vJ4Cpch-16UPUd0PBI655EEU4jt_uiuNSd/s640/taskPS.png" width="640" /></a></div>
<br />
Aquí os dejo el powershell completo:<br />
<br />
<pre class="brush:ps">
Write-Host "temp: $(Agent.TempDirectory)"
$i=0;
Write-Host "Searching coverage files"
Get-ChildItem -Path $(Agent.TempDirectory) -Recurse | where {$_.extension -eq ".coverage"} | % {
Write-Host "Transforming"
Write-Host $_.FullName
$output="$(Build.SourcesDirectory)\coverage$i.coveragexml"
Write-Host "Output"
Write-Host $output
& "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Team Tools\Dynamic Code Coverage Tools\CodeCoverage.exe" analyze /output:$output $_.FullName
Write-Host "Done"
$i++
}
Write-Host "Finished"
</pre>
<br />
<b>Nota</b>: no tengo muy claro porque Azure DevOps me genera por cada proyecto de tests dos archivos de cobertura, tengo que averiguarlo ya que no sucede en local. Este es el motivo de que haga un bucle en el powershell.<br />
En cualquier caso solo vamos a coger el primero de los ficheros convertidos para acabar publicándolo (previas conversiones que vienen a continuación).<br />
<br />
5. Generamos el <b>informe</b>, primero en <b>formato cobertura</b><br />
<br />
Partiendo del fichero XML, metemos una tarea de tipo dotnet, que ejecutará Report Generator como se ve a continuación:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgugtOjU1vMQ3_m5kMmqIsX0s437kGuMCG6uJAgnzm_V7sCRDz2TTeyiMz-ueZTTv6dWIItpF8W45N6zMF8qCcMq0RRv6tcQ_-spv1-294d2X4uCOAoFnIEQ2_hB-tgV8KnlWcv6pBR3G_Q/s1600/convert1.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="764" data-original-width="1088" height="448" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgugtOjU1vMQ3_m5kMmqIsX0s437kGuMCG6uJAgnzm_V7sCRDz2TTeyiMz-ueZTTv6dWIItpF8W45N6zMF8qCcMq0RRv6tcQ_-spv1-294d2X4uCOAoFnIEQ2_hB-tgV8KnlWcv6pBR3G_Q/s640/convert1.png" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Convertir a formato cobertura</td></tr>
</tbody></table>
<br />
<br />
6. Generamos el <b>informe, ahora en html</b><br />
<br />
Partiendo del fichero XML, metemos una tarea de tipo dotnet, que ejecutará Report Generator como se ve a continuación:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXNxdPk6NJOLi2vwSnKOK9f0fc_mtySDZQnblSGj6Zi0XTIFkNeOnq2sHAtAXqhBt5Fh5v5KXgmTByU9F81mOB75b0prv9-7dNW7uoV82a1Yrd1P4Yhq06g8iQE1Tm4tQfpKVlV5FxEtN8/s1600/convert2.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="765" data-original-width="1099" height="444" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXNxdPk6NJOLi2vwSnKOK9f0fc_mtySDZQnblSGj6Zi0XTIFkNeOnq2sHAtAXqhBt5Fh5v5KXgmTByU9F81mOB75b0prv9-7dNW7uoV82a1Yrd1P4Yhq06g8iQE1Tm4tQfpKVlV5FxEtN8/s640/convert2.png" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Convertir a formato html</td></tr>
</tbody></table>
<br />
<br />
7. Finalmente <b>publicamos la información de cobertura</b><br />
<br />
Una tarea de tipo "Publish code coverage results", configurada como se muestra, es lo que necesitamos:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLjWlG8ciJQMNDWRNVAnlTN5zFyVUe6Us71X_AHTVho8pkgrLq7FOnULrYkV4ZiwvB1TaUZGeMxgFGFoOzPMBiLycJ87A7WyegJEJyhxqp5fZQ6x9UVHHGMMXIFPsSVGDnpa6WBlG-PJCY/s1600/publish+coverage.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="716" data-original-width="938" height="488" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLjWlG8ciJQMNDWRNVAnlTN5zFyVUe6Us71X_AHTVho8pkgrLq7FOnULrYkV4ZiwvB1TaUZGeMxgFGFoOzPMBiLycJ87A7WyegJEJyhxqp5fZQ6x9UVHHGMMXIFPsSVGDnpa6WBlG-PJCY/s640/publish+coverage.png" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Publicar cobertura</td></tr>
</tbody></table>
<br />
Con esto lo tenemos todo, mirad a modo de resumen como quedan los pasos de la build definition:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixG_U-6OiwD1dH59vAaRQU3R0d3JZ8CmNvjDt6XoXg16HFh2n_CkXVf_ZT-mmlYfODbbZ2lddxm0ElSX0IsAQ8v59QnjYp_vuWno5hDFldXIKEW7rtx09g9YkEuOzUdS1K02ubmFvNsUd5/s1600/resumenbuild.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="624" data-original-width="516" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixG_U-6OiwD1dH59vAaRQU3R0d3JZ8CmNvjDt6XoXg16HFh2n_CkXVf_ZT-mmlYfODbbZ2lddxm0ElSX0IsAQ8v59QnjYp_vuWno5hDFldXIKEW7rtx09g9YkEuOzUdS1K02ubmFvNsUd5/s400/resumenbuild.png" width="330" /></a></div>
<br />
<br />
<br />
<h2>
¿Cual es el resultado?</h2>
<br />
Con todo esto, si encolamos una build, al acabar veremos lo que buscabamos:<br />
<br />
Resultado de tests y porcentaje de cobertura (esto no es nuevo):<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhg9L6TW-NVMwCn0H1YYF_bR22YeM2ftCXRI3EjDYSUZARUcRmqOGgRWNAlsPQRgfXvdl2w82Ld-xVAF13N3xrpUYhnyrDq9CJo2If8MrQJvwN8O9eHndHOwOWqy7XSCMPuQQUU8pxvOa2w/s1600/informePublicado1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="823" data-original-width="1148" height="458" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhg9L6TW-NVMwCn0H1YYF_bR22YeM2ftCXRI3EjDYSUZARUcRmqOGgRWNAlsPQRgfXvdl2w82Ld-xVAF13N3xrpUYhnyrDq9CJo2If8MrQJvwN8O9eHndHOwOWqy7XSCMPuQQUU8pxvOa2w/s640/informePublicado1.png" width="640" /></a></div>
<br />
<br />
Y un <b>infome detallado, publicado directamente en Azure DevOps!</b><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYXFiemxaOIljCFIYYrJRDRZD7OEmuLx1iKyUJCOYQq4zqqevqhSkgFe-dcQpzBqFgAxQNKJtM-uur6U4aJDmEL-PXH2YNppQ9begilCz3eeFgxVTJGnTZRSZqaXhfJZgWQ80g1u5GTL5w/s1600/informePublicado2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="845" data-original-width="1519" height="356" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYXFiemxaOIljCFIYYrJRDRZD7OEmuLx1iKyUJCOYQq4zqqevqhSkgFe-dcQpzBqFgAxQNKJtM-uur6U4aJDmEL-PXH2YNppQ9begilCz3eeFgxVTJGnTZRSZqaXhfJZgWQ80g1u5GTL5w/s640/informePublicado2.png" width="640" /></a></div>
<br />
<h2>
Resumen y futuros pasos</h2>
<br />
Despues de todo tenemos lo que bucabamos. <b>Disponemos de informes detallados de cobertura, y además no necesitamos Visual Studio Enterprise</b>. Para ello nos hemos apoyado en Azure DevOps<br />
<br />
En otros posts quiero seguir trabajando con estos temas:<br />
<br />
<ul>
<li>Usar otra herramienta para generar la cobertura, de modo que podamos traajar en local 100% sin Visual Studio Enterprise</li>
<li>Automatizar esto para ser usado con Visual Studio Code</li>
<li>Explotar la información de cobertura desde SonarQube</li>
<li>Echar un ojo a <a href="https://marketplace.visualstudio.com/items?itemName=davesmits.codecoverageprotector" target="_blank">Code Coverage Protector</a>. Me lo recomendo Julio Avellaneda (@julitogtu) en twitter </li>
</ul>
<br />
Espero que os haya sido útil!</div>
Sergio Navarro Pinohttp://www.blogger.com/profile/15840814836353973005noreply@blogger.com2tag:blogger.com,1999:blog-7984577123741299127.post-37246211146602946622018-03-27T07:46:00.000+02:002018-03-27T13:58:50.240+02:00Todas las abstracciones fuganEsta frase la he escuchado (y leído) bastantes veces, y para ser honesto, nunca terminé de entender a que se refería. Hace poco volví a leerla y me dije: Entendámosla de una vez por todas<br />
¿De donde viene todo? Del artículo <a href="https://www.joelonsoftware.com/2002/11/11/the-law-of-leaky-abstractions/" target="_blank" title="https://www.joelonsoftware.com/2002/11/11/the-law-of-leaky-abstractions/">The law of leaky abstractions</a>, escrito en Noviembre de 2002 por Joel Spolsky.<br />
<br />
<div align="center">
<b><i>“All non-trivial abstractions, to some degree, are leaky”</i></b></div>
<br />
Viene a decir que toda abstracción, excluyendo aquellas que son triviales, tarde un temprano fuga, revelando aspectos que tratábamos de abstraer.<br />
<br />
<h3>
Veámoslo con un ejemplo:</h3>
<br />
Todos hemos visto una implementación del patrón repository como la siguiente:<br />
<br />
<pre style="background-color: rgb(255 , 255 , 255); margin: 0em; overflow: auto;"><code style="color: black; font-family: consolas,"Courier New",courier,monospace; font-size: 10pt;"> <span style="color: rgb(0 , 0 , 255);">public</span> <span style="color: rgb(0 , 0 , 255);">interface</span> IStudentRepository : IDisposable
{
IQueryable<Student> GetStudents();
Student GetStudentByID(<span style="color: rgb(0 , 0 , 255);">int</span> studentId);
<span style="color: rgb(0 , 0 , 255);">void</span> InsertStudent(Student student);
<span style="color: rgb(0 , 0 , 255);">void</span> DeleteStudent(<span style="color: rgb(0 , 0 , 255);">int</span> studentID);
<span style="color: rgb(0 , 0 , 255);">void</span> UpdateStudent(Student student);
<span style="color: rgb(0 , 0 , 255);">void</span> Save();
}
</code></pre>
<br />
El hecho de que el método GetStudents devuelva un IQueryable implica que debamos conocer detalles acerca de la implementación del repositorio, conocer que hay detrás, ya que el proveedor de Linq ejecutará algunas instrucciones en memoria y otras sobre una motor de bases de datos. No podemos ignorarlo si queremos no meternos en líos. <u>Podemos decir por tanto que nuestra abstracción IStudentRepository fuga</u><br />
<u><br /></u>
<br />
<h3>
¿Hay más ejemplos comunes?</h3>
<br />
Por supuesto, una rápida búsqueda por internet nos dará un bonito listado de abstracciones, que usamos comúnmente, y que fugan (¿todas lo hacen no? XDXD)<br />
<ul>
<li>El garbage collector: tarde o temprano, si tu aplicación realiza mucha presión sobre la memoria, o si es una aplicación que necesita un altísimo rendimiento, tendrás que entender su implementación para alcanzar ese nivel de optimización deseado.</li>
<li>Un sistema de ficheros distribuido, bajo SMB por ejemplo, trata de abstraerte de que los ficheros están en remoto, mostrándolos como si de un disco local se tratase. No obstante la latencia o los posibles micro-cortes de red pueden hacer que tengas que manejar dichos ficheros de un modo diferente (y conocer que de hecho esos ficheros no son locales).</li>
<li>La clase string de C# por ejemplo, que debido su implementación genera un nuevo string cada vez que concatenamos dos strings existentes previamente. Si no conocemos este detalle, podemos sufrir serios problemas de rendimiento y escalabilidad.</li>
</ul>
<br />
<h3>
¿Es esto malo?</h3>
<br />
No lo considero necesariamente malo, simplemente está ahí y lo mejor es que lo conozcamos y seamos conscientes de ello.<br />
Nuevas tecnologías y herramientas tratan de elevar el nivel de abstracción que manejamos al desarrollar software, nos obstante, ya que estas abstracciones fugarán de un modo u otro, debemos conocer sus detalles, cómo estas abstracciones funcionan y han sido implementadas, antes de pasar sin más a utilizarlas.<br />
<br />
<h3>
Recursos: </h3>
<h3>
<br /></h3>
<ul>
<li><a href="https://www.joelonsoftware.com/2002/11/11/the-law-of-leaky-abstractions/" title="https://www.joelonsoftware.com/2002/11/11/the-law-of-leaky-abstractions/">https://www.joelonsoftware.com/2002/11/11/the-law-of-leaky-abstractions/</a></li>
<li><a href="https://blog.codinghorror.com/all-abstractions-are-failed-abstractions/" title="https://blog.codinghorror.com/all-abstractions-are-failed-abstractions/">https://blog.codinghorror.com/all-abstractions-are-failed-abstractions/</a></li>
<li><a href="http://www2.parc.com/csl/groups/sda/publications/papers/Kiczales-IMSA92/for-web.pdf" title="http://www2.parc.com/csl/groups/sda/publications/papers/Kiczales-IMSA92/for-web.pdf">http://www2.parc.com/csl/groups/sda/publications/papers/Kiczales-IMSA92/for-web.pdf</a></li>
</ul>
Sergio Navarro Pinohttp://www.blogger.com/profile/15840814836353973005noreply@blogger.com2Av. de la Industria, 4, 28108 Alcobendas, Madrid, España40.534925478750736 -3.644703626632690440.534548478750736 -3.6453341266326906 40.535302478750737 -3.6440731266326902tag:blogger.com,1999:blog-7984577123741299127.post-52781209948047854852017-08-23T18:05:00.000+02:002017-08-23T19:15:48.125+02:00Cuestiónate todo: Throw vs Throw exHace unos días leía una entrada sobre excepciones en Campus MVP: <a href="https://www.campusmvp.es/recursos/post/GAMBADAS-Los-tres-principales-pecados-al-gestionar-errores-y-excepciones.aspx" target="_blank">GAMBADAS: Los tres principales pecados al gestionar errores y excepciones</a>. Allí nos daban consejos para trabajar con excepciones, y entre ellos nos recordaban como debemos relanzar una excepción, y cual es la diferencia entre relanzarla haciendo <b>throw</b> o haciendo <b>throw ex</b>. Me refiero a esto:<br>
<br>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgz6JluLyy6CtgFEVJ03-SjpFeURi8U-NyB_ds8c9sVFAy4GxxtE9AIR3XFzdaJTI37kK1Uvj35gNu-9gnfRYIear0xaez1leH2hKPapd9dyd3RFdh4UICtYRLCayoY_n94S8sS1KuaZvtP/s1600-h/image7"><img width="504" height="202" title="image" style="margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" alt="image" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_u2ZOuuwUOBBBo_USrUbsxiEexLlxXGYpzUjF_yE6MDDP4C8aNW3vufp_DZJeiryq77HhdNTKRE-6N_EVep3EjcmRV0-c7DbBebeCa9ORnD9pLnUinmQlXEVnsIPRTFTkZa3G342g9CaU/?imgmax=800" border="0"></a><br>
<br>
En el artículo se indicaba que cuando relanzamos una excepción haciendo throw ex estamos “reseteando” el call stack, por lo que un elemento superior que capture la excepción no va a poder saber donde se generó originalmente,y obtendrá como origen la línea en la que hacemos el throw ex.<br>
<br>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5uVLDE1nGoKburYfv3Tkypp-lFIiLSvaueG-6pfpTuotB_F1eJqQBnsYCMU2YY3tmXGINbrOpepMUKbZksMaNcqw7YXHZC5JQa0f-hKBa9ypfpn23NSR48pHAbb3GDo_CmCdVGDKMF_Vk/s1600-h/image11"><img width="804" height="217" title="image" style="margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" alt="image" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0eIJrqspf4oLe5cLO2FxlyulS7SQGqBlR1GqVwinXzMNuSkVRdZrNWv5CHypvPKr84HVWX-a5kU9KoIf6_0P4SSa5bpXNBMgAOurHYx5cXHIcsSg2idWDOGUCbZ0Jsl0KQqCVgilcsRge/?imgmax=800" border="0"></a><br>
<br>
Según lo leí la cabeza me jugo una mala pasada y me dije: “eso esta mal, hacer throw o throw ex es lo mismo”. Lo malo es que rápidamente me fui a Twitter a pregonarlo, donde me corrigieron a la primera de cambio: no no es lo mismo. Si nos vamos a la documentación oficial de C# (<a title="https://docs.microsoft.com/es-es/dotnet/csharp/language-reference/keywords/throw" href="https://docs.microsoft.com/es-es/dotnet/csharp/language-reference/keywords/throw">https://docs.microsoft.com/es-es/dotnet/csharp/language-reference/keywords/throw</a>) vemos esto:<br>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRNCeGo2LIGBzRwSmLPfdjEn8oUPGuv7uqP2LvLGLmp6foEc64ge32PTT-uwnfDoFATftPCgb50zUmlCl8bAipnDBDaX6RQAshB9wEthTTrz8SoWQuvEt71eMP3Mqlr-pyV9_fug_fjxct/s1600-h/image12"><img width="829" height="104" title="image" style="display: inline; background-image: none;" alt="image" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhf-QVu_57DcvOrmbfeUlZnPtSC21WrrCd7PJ4ZGQ4JmEIah2yksXahCnYFTJ_y5_sfjVnCjRXeBtFwosxI9yfL4D5_xeTpb0cbqNMcQb-PR1rDhjamTZLQ1z45H0P_B-Ul5sohtH1jgvBg/?imgmax=800" border="0"></a><br>
<br>
Parece estar meridianamente claro… ¿pero es así en todos los casos? ¿De donde había sacado yo las idea de que era lo mismo una cosa que la otra? <br>
<br>
<h1>
El ejemplo</h1>
Vamos a comprobarlo, para ello he creado un código de ejemplo, es sencillo. Tenemos un método que lanza una excepción, ya que hace una división por cero<br>
<pre class="csharpcode"><span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">int</span> FaultyCode(<span class="kwrd">int</span> number)
{
<span class="kwrd">int</span> divisor = 0;
<span class="kwrd">return</span> number / divisor;
}</pre>
Luego tenemos un segundo método (CatchAndThrowEx) llama al anterior, recubriendo la llamada en un bloque <i>try/catch</i>. Una vez capturada la excepción la relanza con <b><i>throw ex</i></b>.<br>
<style type="text/css">
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #a31515; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
</style>
<pre class="csharpcode"><span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">void</span> CatchAndThrowEx()
{
<span class="kwrd">try</span>
{
<span class="kwrd">var</span> result = FaultyCode(5);
}
<span class="kwrd">catch</span> (Exception ex)
{
<span class="kwrd">throw</span> ex;
}
}</pre>
También un tercer método(CatchAndThrow) similar al anterior y que también llama al código que falla. La llamada es recubierta en un bloque <i>try/catch</i>. Una vez capturada la excepción la relanza con <b><i>throw</i></b>.<br>
<style type="text/css">
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #a31515; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
</style>
<pre class="csharpcode"><span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">void</span> CatchAndThrow()
{
<span class="kwrd">try</span>
{
<span class="kwrd">var</span> result = FaultyCode(5);
}
<span class="kwrd">catch</span>(Exception ex)
{
<span class="kwrd">throw</span>;
}
}</pre>
Finalmente, desde el <i>main</i> de una aplicación de consola, invocamos a los dos métodos que relanzan la excepción, y la capturamos, trazando su <i>call stack</i>, para que veamos si este ha sido “reseteado” o no.<br>
<!-- code formatted by http://manoli.net/csharpformat/ -->
<style type="text/css">
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #a31515; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
</style>
<br>
<pre class="csharpcode"><span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">void</span> Main(<span class="kwrd">string</span>[] args)
{
<span class="kwrd">try</span>
{
Console.WriteLine(<span class="str">"=============================================="</span>);
Console.WriteLine(<span class="str">"CatchAndThrow:"</span>);
CatchAndThrow();
}
<span class="kwrd">catch</span> (Exception ex)
{
ex.ToConsole();
}
<span class="kwrd">try</span>
{
Console.WriteLine(<span class="str">"=============================================="</span>);
Console.WriteLine(<span class="str">"CatchAndThrowEx:"</span>);
CatchAndThrowEx();
}
<span class="kwrd">catch</span> (Exception ex)
{
ex.ToConsole();
}
}</pre>
Si ejecutamos lo anterior, obtenemos lo esperado: cuando se relanza con <i>throw ex</i> se pierde el <i>call stack </i>y no somos capaces de ver el método donde se originó la excepción… malo<br>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVBlFC3jF0ajPvH5jOkg64IiNpVR92cKyAwtgM6BuhI73WgHb0i4qPHbAfa_c4EUXZyYhCSA5hvS16497SZaLfwEXzGomGD5-Y6FYAmBAUI2rSCZlRIipn1Mkp4AShzVO4AKT5IZv9NhqV/s1600-h/Core+2%255B5%255D"><img width="854" height="185" title="Core 2" style="display: inline; background-image: none;" alt="Core 2" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgiIf7lpmK8erN4GH9YHQOuF3mAMS41VomGS0E-Tp7aSH2My7kyyFJnjiXfrTzgE0ccBXZ-CuWgRtVailxZ2roHAe93phTR4M-uXRa16AM3QKBnfqap4RVV5Jr6BNMFwuBEGizCs2ccd-UA/?imgmax=800" border="0"></a><br>
<br>
<h1>
Seguimos cuestionando</h1>
Antes de abandonar me dije, lo he ejecutado compilando en <i>debug</i>… ¿habrá cambio en <i>release</i>?<br>
Si ejecuto en <i>release</i> (<b><i>dotnet run -c Release</i></b>) las cosas cambian! Como podéis ver a continuación <b><i>throw</i></b> y <b><i>throw ex</i></b> se comportan exactamente igual. <br>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFzHjA2KFX-l4y223DAdcKCAFqw-eHLUBijpMCombGVMBEYiSddNpcdaesUR_iPuHBcJ4NmVhnB5d2rsd04wFF0eIUB-kSj8NPMqhlrI1jG3zXqGdu1tbWu0nayvAm_k3ky82-B7uJ3gHv/s1600-h/image%255B4%255D"><img width="885" height="265" title="image" style="display: inline; background-image: none;" alt="image" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgen0tzBl6BU52OuLE69SgqyA7LIf9jWLVqwgt46RFoCjhj1gXqyWJUqaDjrnRAHzBJK3czrdz18M__CDcnHBEeZW11W8w3HzBCJMom8wo_wpc8iAcjuDyQ-DcmAYdPQKT-nEpmD0n-OteZ/?imgmax=800" border="0"></a><br>
<br>
<b>¡Esto no estaba en el guión!</b><br>
Visto lo visto me dije, será cosa de .Net Core 2? Vamos a comprobar en otras configuraciones. El resultado está en la siguiente tabla, y siempre es el mismo: cuando se ha compilado en <i>release</i> <b><i>throw</i></b> y <b><i>throw</i></b> ex se comportan igual:<br>
<b>
</b><b>
</b><br>
<table border="1" cellspacing="0" cellpadding="2">
<tbody>
<tr>
<td valign="top"><b>Plataforma</b></td><td valign="top"><b>Compilación</b></td><td valign="top"><b>Resultado</b></td>
</tr>
<tr>
<td valign="top">.Net Core 1.1</td>
<td valign="top">Debug</td>
<td valign="top">Throw mantiene el call stack. Throw ex no lo hace</td>
</tr>
<tr>
<td valign="top">.Net Core 1.1</td>
<td valign="top">Release</td>
<td valign="top">Throw y Throw ex tienen el mismo comportamiento</td>
</tr>
<tr>
<td valign="top">.Net Core 2</td>
<td valign="top">Debug</td>
<td valign="top">Throw mantiene el call stack. Throw ex no lo hace</td>
</tr>
<tr>
<td valign="top">.Net Core 2</td>
<td valign="top">Release</td>
<td valign="top">Throw y Throw ex tienen el mismo comportamiento</td>
</tr>
<tr>
<td valign="top">.Net Framework 4.6.2 </td>
<td valign="top">Debug</td>
<td valign="top">Throw mantiene el call stack. Throw ex no lo hace</td>
</tr>
<tr>
<td valign="top">.Net Framework 4.6.2 </td>
<td valign="top">Release</td>
<td valign="top">Throw y Throw ex tienen el mismo comportamiento </td>
</tr>
</tbody>
</table><p>
<br></p><p><br>
</p><h1>¿Donde está el truco?</h1><p>Suele haber un motivo, siempre lo hay, que explique comportamientos extraños, comportamiento que contradicen la documentación oficial como es en este caso. <p>Yo ni lo había imaginado, pero Juanma (https://twitter.com/gulnor) rápidamente intuyó que pasaba: <u>El método FaultyCode era demasiado sencillo</u>, tanto que <strong>al compilar en Release se convierte en un inline</strong>, por lo que pasa a ser código del método que le invoca.<p>Si complicamos FaultyCode, el compilador no lo convertirá en un inline, y <strong>finalmente veremos que el comportamiento vuelve a ser el esperado</strong>, y el documentado:<ul><li>Throw mantiene el call stack</li><li>Throw ex lo resetea</li></ul><p><br><p>
Saludos, espero que os haya resultado interesanteSergio Navarro Pinohttp://www.blogger.com/profile/15840814836353973005noreply@blogger.com0tag:blogger.com,1999:blog-7984577123741299127.post-38627833371257072242017-08-19T19:31:00.000+02:002017-08-19T19:44:24.441+02:00Entrada vintage parte 2: .Net Core 2.0 y Visual Studio 2015Continuando con mi entrada anterior (<a href="http://www.serginet.com/2017/08/core-vs">http://www.serginet.com/2017/08/core-vs</a>), y una vez que .NET Core 2.0 ha sido liberado, vamos a ver como podemos crear, compilar y ejecutar aplicaciones basadas en .NET Core 2 con proyectos tipo project.json (los de Visual Studio 2015).<br />
<br />
Lo primero ver la situación inicial, tal y como hacíamos en la entrada anterior:<br />
<br />
<ul>
<li>La versión del runtime de .NET Core, ejecutando el comando <strong><em>dotnet. </em></strong></li>
</ul>
<ul>
<li>La versión del CLI, con <strong><em>dotnet --version.</em></strong> </li>
</ul>
<ul>
<li>La versión del SDK (que debería ser la misma que la del CLI), con <em><strong>dotnet --info. </strong></em></li>
</ul>
<div>
<br /></div>
<div>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7wluB6CU4aOHadQlwCKDZ-33ciRzJpM2ow30lLoxmpozklR_T8DuI7lYUBMh4JYWyYFQxMcZUj9etzbxdy9AwyUGIcTrNBEnMP6QJF569FJ6WWUDt_waUYADHojruC81DbDQsQrCVA_N8/s1600-h/image14"><img alt="image" border="0" height="550" id="id_ee90_15b4_27da_f563" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiE9oW5sfI8yD56Aj4G__yFOkqOVeLJiXnkWm_5KB-1yQ0ngAQ6OTD4OAlCh1yNnziFmwuM4fXczh-ekVBhb0nO_ZZkdTeaYjOyOVSb_UlV6pHA9Oxnt1LUaI-IS7BaIc1qMXbqW2QI3Clc/?imgmax=800" style="background-image: none; display: block; float: none; height: auto; margin-left: auto; margin-right: auto; width: 656px;" title="image" width="656" /></a></div>
<strong><em></em></strong><br />
<br />
Como veis hay un runtime 1.0.1 con un SDK (y por tanto CLI) 1.0.0-preview2-003131<br />
<br />
<ul>
<li>¿Hay otros SDK's instalados?, Para ello miramos directamente en la carpeta <em>ProgramFiles\dotnet\SDK </em>y vemos que en esta máquina no hay más SDK's</li>
</ul>
<div>
<br /></div>
<div>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxf3pS9-HM5BEqwqpQ-ZWpxmCGDEMOliR4YSOE0hZGKDYcNkh3XHwYuKJyXyNCY37yPcrkBKoXLsfnx0_1QWz0xUIkhdO78NhrSS1pLxYBW4QW1Gr86tIlbNa4k1XyJny6INrvWJwCz_FW/s1600-h/image22"><img alt="image" border="0" height="131" id="id_3b2b_aca2_ead2_4734" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPPVTF5FnqjbmMqeJ3054MT3EIpCkTwOJthsmzZ-VzDvkAUDo2ytJGLZveO92K-d200SD-hDdZwuUvlCCFNzjZ70125yMn_QnXJ6bTwAXJ5gYwFn9uK-QUN8C2jxwsr1R2Y1BqU2uWYQOw/?imgmax=800" style="background-image: none; display: block; float: none; height: auto; margin-left: auto; margin-right: auto; width: 504px;" title="image" width="504" /></a><br />
<br /></div>
<ul>
<li>¿Qué dice Windows que tenemos instalado? Miramos a Agregar/Quitar programas, y reconfirmamos que no hay nada más instalado.</li>
</ul>
<div>
<br /></div>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXpBufiDgQe8a-xz6XgI_1Y3vgZ9EQvsxUTxCkp8HQ-2FiehSbbeFgXX1UjDBsJxCdtxlAlVlfAS-cCXW_lv5zeQj5pfaBU62qJdze3qRj8LAFHHBz1taxSWw7Il8Od1Jf38GqsKbt0Xf-/s1600-h/agregarQuitarAntes3"><img alt="agregarQuitarAntes" border="0" height="321" id="id_241d_4363_d5a4_d377" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoxdyBQYgo4-nZQuHDxqUgIIfnbcYx2EFeIBtLo6R-xSzmfODE9XKdaC08DFDtm9pybFjJDpKsjzwuM_5HegeEBrZ7_PowEcteM-nhJmNUaysPcW_S2H-3AUb-x07DEXNeU7xmR7Pze5pE/?imgmax=800" style="background-image: none; display: block; float: none; height: auto; margin-left: auto; margin-right: auto; width: 404px;" title="agregarQuitarAntes" width="404" /></a><br />
<ul>
</ul>
<b><br /></b>
Como veis parto de una situación standard de Visual Studio 2015: SDK y Tools en preview, además de un runtime de .NET 1.0.x .<br />
<br />
<strong>
Empezamos descargando el SDK desde la página de descargas oficial ( </strong><a href="https://www.microsoft.com/net/download/core"><strong>https://www.microsoft.com/net/download/core</strong></a><strong> ) e instalándolo.</strong> <br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLvaNJPOl8Tllq4wzAMXArN4eKiHjjQ1Az_p8XFt8ryYJrjcc8SgTvp3iK-6TaskKsKEk4hXTa68FSRBWwYi1ws9p94339y5M2Rn9enzz-T56f1Lp-y1tZ2ODI4_nprGr37wUV1srZpw0I/s1600-h/image15%255B1%255D"><img alt="image" border="0" height="336" id="id_66eb_9c8_c2fb_7b07" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhzInxscpTdwIr-kL-k7xDY4slzYrKbUiGhC3fmwE0sMEu1xDJFOF8TEpwwO89BKo70cUTY1mE7vFHnQyudlZ_0ERT1E9Q4hofTzSu5qFs0OEbeHtHnkoS5tfQU1A3G4AKFQejec0bfyu2w/?imgmax=800" style="background-image: none; display: block; float: none; height: auto; margin-left: auto; margin-right: auto; width: 454px;" title="image" width="454" /></a><br />
<br />
Una vez hecho, vamos a ver de nuevo como ha quedado el sistema, consultando:<br />
<br />
<ul>
<li>La versión del runtime de .NET Core, ejecutando el comando <strong><em>dotnet. </em></strong></li>
</ul>
<ul>
<li>La versión del CLI, con <strong><em>dotnet --version.</em></strong> </li>
</ul>
<ul>
<li>La versión del SDK (que debería ser la misma que la del CLI), con <em><strong>dotnet --info. </strong></em></li>
</ul>
<div>
<b><i><br /></i></b></div>
<div>
<b><i><br /></i></b></div>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPj017zxA6YvVBf0qEbXBi8LOCgM04MdlIAxbma62Ln7nrmaJJdbmOUClJSDWznQA0xCAv2BxcC_lW0D427zTDIz8Opt0EgWMcMIsFf5kJJiC_d2-5dUzePC9hv6-epkyd8GIURqNKc3f1/s1600-h/image29"><img alt="image" border="0" height="464" id="id_f905_ad59_aa68_2920" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLzoF5_YOWgkOBWUlExsKEQICCuB2cYyYBhRergg2OxXVOymVLYzEKhdulX967gVEQDFJepiKHkclhZo0uqjNOoFvK12O6rsA2X1GE3H3xoX0WQpFCSPC2Ppv0pO_QxxAZbHfiB7r8Eh9g/?imgmax=800" style="background-image: none; display: block; float: none; height: auto; margin-left: auto; margin-right: auto; width: 504px;" title="image" width="504" /></a><br />
Aquí vemos que ha habido cambios en el comando dotnet: ya no vemos el runtime ejecutando dotnet. Si vamos a la ayuda, vemos lo siguiente:<br />
<ul>
<li><strong>dotnet --version</strong> nos da la versión del SDK</li>
<li><strong>dotnet --info</strong> nos da información general de .net Core, incluyendo la versión de CLI (que debe coincidir con el SDK) y el runtime</li>
</ul>
<div>
<br /></div>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBhBgJ453LgxSrQf-GMZMOfpEi-_YABy6YHPipSqnJEo4y5IhEk_e5RyOgV-zpBDXaJNqhIv2pK5OnI9pSpx1h3Uw5wP-PdnJptUeBogxCDLl8HyRRNA_iuSSSPtMtlKwpHCJiIy2NgmWr/s1600-h/image%255B34%255D"><img alt="image" border="0" height="339" id="id_8b91_3e39_4f90_c373" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNaPf8Et0a7yM6RslM9_zx4Bl05O2RvjRsLVIk3RYmHj9nUPNJ3rZYnfPa-QMNm6afhUXkf4Z5uzW5AKe5nF6G7mYEmEh4LNq0QPiMvsOjQlplKCazi04pOGBlM-hLcZ93WfqegcWzmy2w/?imgmax=800" style="background-image: none; display: block; float: none; height: auto; margin-left: auto; margin-right: auto; width: 504px;" title="image" width="504" /></a><br />
<br />
Ahora vamos a compilar, nos ubicamos en un proyecto tipo Visual Studio 2015 (project.json) y ejecutamos dotnet.build.<br />
<u>Es importante fijarnos que no tengamos un fichero project.json que nos cambie el SDK a usar</u>, de momento queremos usar el último SDK que acabamos de instalar<br />
<br />
La primera en la frente, tenemos un error. <br />
<br />
<a href="https://lh3.googleusercontent.com/-hDfnHCrAQf4/WZh0nGjojXI/AAAAAAAAA9Q/RyQAQmSbeDMthh-jPTE2ld467Hlnq2o7gCHMYCw/s1600-h/image%255B60%255D"><img alt="image" border="0" height="60" id="id_312c_75f0_9cf4_178d" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMhRSyorGY7Hbh4I4gjpkOclXA9HAZV2R6QYfBvWOJxlV3_eqrnQfxD0Lkzay6w275xWmbLzRMXy74VxLOnZh8CXTRveKunX1UZEtLEPt-5BZarsPDU4AsNmj3ZmfdF5_IKuAulKdbnkyQ/?imgmax=800" style="background-image: none; display: inline; height: auto; margin: 0px; width: 754px;" title="image" width="754" /></a><br />
<br />
Estamos en la misma situación de la entrada anterior (<a href="http://www.serginet.com/2017/08/core-vs">http://www.serginet.com/2017/08/core-vs</a>): se ha eliminado el soporte a proyectos tipo <em>project.json</em>, por lo que si queremos compilar necesitamos usar un SDK “viejo”. Ahora es el momento de hacer uso de nuestro fichero <em>global.json</em>.<br />
Ubicamos el fichero una carpeta por encima de la carpeta donde está en <em>project.json</em>, con el siguiente contenido:<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgI6r0UQo6rdq58CDjuj705aCETfw6DWzO5AU3VIPPuEiPseDB3NBe3xmU3tq3LGLQT53g7p6zJ5DrJ5kjv9lUm03paEljFx3WLUaQFeFSpbSdYurRShxha-h6ZrynUpQy1MRHfQJs7ebOY/s1600-h/image%255B44%255D"><img alt="image" border="0" height="124" id="id_dda7_466f_a8f5_197f" src="https://lh3.googleusercontent.com/-NNCLN8E-pDk/WZh0oygBBvI/AAAAAAAAA9c/mg8-D49QBYMvyv4IMPaRqSfpSZyqI9q0ACHMYCw/image_thumb%255B23%255D?imgmax=800" style="background-image: none; display: block; float: none; height: auto; margin-left: auto; margin-right: auto; width: 404px;" title="image" width="404" /></a><br />
<br />
y compilamos y ejecutamos sin problema. <br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjibY08DUcaz5LqEHdM08N6UWyONkIyBeAJTnHsBDYw7Nr5qK3VTnh8qrp14s1KzkXkFTcB5qFgX7Vi2ndBMcEmPL-q9jGkdIFEF59aU-Bl0Fok-AE0y_PWTU2kzP1TKCMI0SXY4wB9RLUQ/s1600-h/image%255B56%255D"><img alt="image" border="0" height="197" id="id_8072_43be_dd4f_dba9" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7ZFbZKKC2DAUUcGvz8iJ0vl6U6CddLQ61nukdslQAHz5-3Folk-jkzusCuGZDnY8URrzjwtZ_7hUUn-RztXDw4RBe1jjw00wtArPqcqGrTIBkHuoOM5r1CIpbuLwwNe97vg0RO1VNwD49/?imgmax=800" style="background-image: none; display: block; float: none; height: auto; margin-left: auto; margin-right: auto; width: 804px;" title="image" width="804" /></a><br />
<br />
Ahora estamos ejecutando una aplicación .Net Core 1.0 con un runtime 2.0, vemos el runtime ejecutando <strong>dotnet --info</strong><br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCUjqpvKfs_lx-zwmOWyZsFELhLum6-xV_LZ7eFUOhuV8BU_Am0ikVOEd9l2x4-0EdGN7giCtYadhodRg6HyhsV6AJjYl0kmsbSbJNWI1kfuj4181lQSmoWxKF9aaRhnFzy6uLxbofp7_x/s1600-h/image%255B21%255D"><img alt="image" border="0" height="211" id="id_b7e2_7085_93df_605c" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgG0Kkwlgf2RSnrO7dOhklFCp2uucIqCc6RL9HUoS2U86oBSYazHBbEQzxIuJSQeZPDR-sLzBCuSgKHKF7QmpFpYp8-WRvAa9aYc7OOSdQAME5dTO6LfNwrkwozxgL9zO7sJ-RJ5KWI_GlS/?imgmax=800" style="background-image: none; display: inline; height: auto; width: 804px;" title="image" width="804" /></a><br />
<strong><br /></strong>
<strong>Me sigue sorprendiendo ver una “mezcla” curiosa: el último runtime 2.0 side by side con un SDK antiguo, versión preview anterior a 1.0</strong><br />
<br />
<h1>
Actualizando a .Net Core 2.0</h1>
<br />
Vamos a actualizar a 2.0, modificando directamente el fichero project.json para dejarlo así:<br />
<br />
<div align="center">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpBmxpb7OLCffFq54pDs7PG-d4xZpkJ9JlbNRwAu25Q-Lxm5R2W-IUvgAw0uYvNuoOhQype0AymIdEpk2wpw0ZcXyryNr864nqc9O-nutdM7xR_jgmd7J7wG1nvaIIxQmcfkPsXNtdy6UZ/s1600-h/image%255B71%255D"><img alt="image" border="0" height="461" id="id_3e26_8cf4_f1b6_bd05" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnT-WwXbltcGJKQMoh_2nn68omufotbYoFuRyYo4CTVMMWCTLR7brtzcgPRPOiMzD8IQ3Yjh8ckfOthkkxoJ1JgUWut4VQZf0ZyAIuvos6QVf7Pmtr0vJEhao85yumlBGVYBzivktroYNh/?imgmax=800" style="background-image: none; display: inline; height: auto; width: 404px;" title="image" width="404" /></a></div>
<br />
Para compilarlo, nos basta con <strong>dotnet restore</strong> seguido de <strong>dotnet build</strong>, y sin eliminar nuestro fichero global.json, ya que seguimos necesitando el SDK viejo que nos da compatibilidad con project.json<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwsTnznf-qVD4fnVeQ2-q2jT4v0O8m3E2aj4_R0vB6hobDQSYoOZB6v8TBYPeTVPMAPCX3PUFwhMUEgAaD0qoboZQWaqagkw7lRy2hEiLbdCgSlQt5npe-r58W3IFkCWBKIcJhOdZgx3h6/s1600-h/image%255B40%255D"><img alt="image" border="0" height="207" id="id_ef09_d46a_7315_d956" src="https://lh3.googleusercontent.com/-vM4-A-K2rrA/WZh0unEtMHI/AAAAAAAAA98/fmOdtOLBD8gQYxZPItM72sHRzqpRQ0E0QCHMYCw/image_thumb%255B21%255D?imgmax=800" style="background-image: none; display: inline; height: auto; width: 754px;" title="image" width="754" /></a><br />
<br />
<br />
Y vemos que… <b>¡compila bien, sin problemas!</b> Hemos compilado una aplicación con target .Net Core 2.0 con un SDK compatible con Visual Studio 2015 ya que sigue usando <em>project.json </em><br />
<i><br /></i>
Reto conseguido! <em>A disfrutarlo!</em>Sergio Navarro Pinohttp://www.blogger.com/profile/15840814836353973005noreply@blogger.com028055 Madrid, España40.483224 -3.619220199999972516.0096065 -44.927814199999972 64.9568415 37.689373800000027tag:blogger.com,1999:blog-7984577123741299127.post-15444066832330906532017-08-15T17:11:00.001+02:002017-08-15T17:11:54.909+02:00Entrada Vintage: problemas de versiones con .NET core y Visual Studio 2015Esta entrada describe un problema que sufro de vez en cuando, y que de una vez por todas he decidido investigar, para documentar la solución y no volver a perder el tiempo. Quizás llegue un poco tarde… mas aun cuando acaban de anunciar .NET Core 2<br />
Empecemos por el principio:<br />
<br />
Dispongo de 2 equipos con Visual Studio 2015, y un tercero con Visual Studio 2017.
Si, se que debería pasar todos a 2017, pero me resisto: Visual Studio 2015 funciona bien, y mi querido project.json en core me gusta, bastante.<br />
<br />
El caso es que acostumbro a intercambiar los equipos con 2015 a la hora de programar, manteniendo el código en una carpeta sincronizada en OneDrive, por lo que siempre está al día en ambos aunque no haya hecho commit/push a mi repositorio remoto (llamadme temerario).<br />
<br />
Más de una vez, en uno de los cambios de equipo, me he encontrado con un error a la hora de abrir una solución que contenga proyectos .NET Core, y además no pudiendo compilar a partir de ese momento ningún proyecto .NET Core.<br />
El error al cargar la solución es el siguiente:<br />
<b><br /></b>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghvkTwCcfRHAYuMGkn2RfuOYGZ5zVXtp_AYviO_ihIb-j5YJ0N7hFk3eVItaHe-n633l7H5oO9WA0oeef_mLDKtfMOxtYH0mFz-apUu7UcUs-gcjDqwx0ISP4lqugMHBnt3rfIEpU44l_3/s1600-h/Error+arranque%255B3%255D"><img alt="Error arranque" border="0" height="371" src="https://lh3.googleusercontent.com/-93NOfgrUh4Y/WZMJOA8EVfI/AAAAAAAAA4s/MkbZA9Bvjrc0uoqArOBraWuG90XEuGzwwCHMYCw/Error%2Barranque_thumb%255B1%255D?imgmax=800" style="background-image: none; display: inline; float: none;" title="Error arranque" width="644" /></a><br />
<b><br /></b>
Al compilar este:<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-qzHGHMy8q6uJt84IGefJFcnFSNZuiwxdGNcEzs-usrRvjnz_vKNuU2V1JSzWXnSNtKs3lZZ6YVtgdn9kGUANXQ5-ASs3-ntq0XLRE2sOrP5YvS1q79xuTVnF6TZEGA5ovyxMXa3AQedb/s1600-h/ErrorCompilar%255B53%255D"><img alt="ErrorCompilar" border="0" height="496" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhc3exA_bAncT0x44v-OlXoQlkZd3qQ-6y4-Go9dyTjnJCyxXRPHo31ulUKmYv5YWjHTih-qbavPF1tAd0DGxIu3b06bez9xiwKjPngsBwaOkgds5_Falh29ecnMVpwGHFERc-L9fwG9nOZ/?imgmax=800" style="background-image: none; display: inline; float: none;" title="ErrorCompilar" width="694" /></a><br />
<br />
<br />
Parece que hay algún problema en el SDK de .NET Core o bien en el tooling para Visual Studio 2015. Siempre en el pasado lo he arreglado desinstalando SDK's y tooling e reinstalando el tooling para Visual Studio de nuevo (que también instala el SDK). Esta vez sin embargo quiero ver que genera el error para resolverlo y no volver a sufrirlo (espero).<br />
<br />
Vamos a empezar viendo que tenemos instalado:<br />
<br />
<br />
<ul>
<li>La versión del runtime de .NET Core, ejecutando el comando <strong><em>dotnet. </em></strong>Tengo el runtime 1.1.0</li>
</ul>
<strong><em><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEin1Q9cxjuvBDMhhkCN7MGaP2Jb3Qr4-1cCGYkeVpWpbQrVPP9RlvI1gmo56R8MgMXWw5oJNubv0kA0VP_FxyfkLNf8xC87KPl19IE5KHXID5PbSP3rICRNB1XzXtxOhjp_uPShpmKw1Osm/s1600-h/dotnetAntes%255B3%255D"><img alt="dotnetAntes" border="0" height="280" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEganzil-XMgye5lHC8fKq7HoxiXI-Xpu69B538g1JSGWj_vfzUuNw38JGsRQXEZcevafZF7M6tx2wNG2PwNBAotTO8Lfw2zwb3EzfA-xOvP2ym1pPlsTKXJwlWgnvgq1Znw3TT1EJggmBNj/?imgmax=800" style="background-image: none; display: block; float: none; margin-left: auto; margin-right: auto;" title="dotnetAntes" width="644" /></a></em></strong><br />
<br />
<ul>
<li>La versión del CLI, con <strong><em>dotnet --version.</em></strong> Tengo la versión 1.0.4 del CLI</li>
</ul>
<strong><em><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwzI2mX499LYwZjTaX8KkF_eOPCGN9gdHUbqGdQSCm51oBSVXcPj9vGVztTxLvosRPGnAoLIzWSmuWmhEOyW6GNDayGU2F-tQ04y5ffGKiPXp5thKoZ7wmdU6Fu2svEOh_t4GCrDpyRLmO/s1600-h/dotnetVersionAntes%255B151%255D"><img alt="dotnetVersionAntes" border="0" height="114" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi61ZCaTkFWRk3hgzy1PsRGgRRdlvWOFyQoSu1lu-djmiMXBViwu2z0BcT_1vd4bhF_Xql1DBcvaSb3YsXOQPGoH5zs6kOGXA-qzfKheEqZaB_R1_111VCX9FcddN0xZuryP9y_4t6eGS1_/?imgmax=800" style="background-image: none; display: block; float: none; margin-left: auto; margin-right: auto;" title="dotnetVersionAntes" width="404" /></a></em></strong><br />
<br />
<ul>
<li>La versión del SDK (que debería ser la misma que la del CLI), con <em><strong>dotnet --info. </strong></em>Efectivamente es el 1.0.4, como el CLI</li>
</ul>
<strong><em><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhb98WOTSPPLQqdSVXNBMJmaqvyk2ZFA_su9nQmho86CDbFfHBCC-QhOUyYqL-B61WvA5Z4ZlZYkC2AuAGJIjyQya3tDG-BVNcav71vVa53CXNSfl6-XO6WNJWZ0oO6ZdJMotGpdllPgVfd/s1600-h/infoAntes%255B4%255D"><img alt="infoAntes" border="0" height="202" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiy-Diyq7AZm8tWIm_qIWynK3wCXQWT9QeevZFXboNFE3e8YzgW5zNWY1YLh7KYCZqt3XibdAiMxqRS9TrAxYjLE2gdZklT3ky3Nt0lI4eK_JmmKkx2Dfu2dtG-F7FGtBRLBvzUIk_Fh5cd/?imgmax=800" style="background-image: none; display: block; float: none; margin-left: auto; margin-right: auto;" title="infoAntes" width="404" /></a></em></strong><br />
<br />
<ul>
<li>¿Hay otros SDK's instalados?, Para ello miramos directamente en la carpeta <i>ProgramFiles\dotnet\SDK</i></li>
</ul>
<em><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgH_OYAnRr4piHkzsVJEvim7RR0tXyTVbfpE8QFK4xOfeAVgiMjkycWlBq_k5ojVKvA2TtwZUC2DkRIrsnhkUWfBj2YidV8LyEIky3WVKoy5wn3P7zX6Of24d4FKIqhyphenhyphenEbDAqPpN4o4EJpu/s1600-h/Versiones%255B4%255D"><img alt="Versiones" border="0" height="237" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2v7SBOW7r9mrsV4NDgyVOwWdrLJiiYgsQ0T1sUbzOyZ5GzRfNwQ5gODFqgIC3R_qot36rEiUkUAeK4nazZMz_P0b52k45Ro9V0xskTtgB3NWlru-QZmaDi0mKRxrAyBugyfLbqhJ5cCpD/?imgmax=800" style="background-image: none; display: block; float: none; margin-left: auto; margin-right: auto;" title="Versiones" width="504" /></a></em><br />
Vemos que dispongo de dos SDK’s,un preview y la versión 1.0.4. <strong>Aprovecho a recordar que .NET Core siempre usa la versión más alta que tenga disponible.</strong><br />
<br />
<ul>
<li>¿Qué dice Windows que tenemos instalado? Miramos a Agregar/Quitar programas</li>
</ul>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgypzFxsksLzjvla3FKL2AoEZVeOLaVl-Duz4niAElWi8Oz-u1ODB24kUUcFk0jNGNg4-QSEGtS_dxfIw5s_oWqWK8iw8QxmWMfG6Qx5HL6n7WX9tGYW91lnmf9N-2MGFx-DxoGxRkt5kBG/s1600-h/ProgramasAntes%255B3%255D"><img alt="ProgramasAntes" border="0" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEicWGoRcWUg-8xVG2PawwrlEBKWYz4SsnQ4NHE0_Ictgxb8xt1aieKgV-56DvWCgZTne0wuy6oVM7XKQsE55GUVzVINGDz_hvIXtmAo2gkJu5OsrNZ498659OE_vY6Ao5MxM6lR-LE3mRJ-/?imgmax=800" style="background-image: none; display: block; float: none; margin-left: auto; margin-right: auto;" title="ProgramasAntes" width="354" /></a><br />
<ul>
</ul>
<br />
Como se ve tengo dos SDK, uno preview y el 1.04, esto coincide con lo que vi directamente en disco. Además tengo las tools para Visual Studio, que son las que parece no funcionan.<br />
<br />
¿Como lo comprobamos? Compilando desde línea de comandos con <b><i>dotnet build</i></b><br />
<strong><em></em></strong><br />
<br />
<b></b><i></i><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixUB7rKYxB2GmBJyuTSM1cnWE8yUcnl7b8e7wr0CfSAY9MlINuRZpiyokC3ERUj7JT4qb7mlrpDEKuNjiBkUNI3feWM6PeXk2eNxlkWAx5pjccp1dh_0FMX1F-cbqv4BSAGELeMUOK_8Vn/s1600-h/error+dotnetbuild%255B9%255D"><img alt="error dotnetbuild" border="0" height="260" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrT-YpRt0MUkPo09xI6-pUhO3jLGK8BAmz9zXYa9SMhButTsB5w4cLn1Y3ljSDKfXnuD0-k2PkxXCbzjg7ZssXT_6FjJHyJXCsj9XXQ9uuBg7J4xFJcXRO-e609ZMp_JJ3nHguGJZZrG1T/?imgmax=800" style="background-image: none; display: inline; float: none;" title="error dotnetbuild" width="754" /></a><br />
<br />
Vemos que no funciona, y para esto no interviene Visual Studio, ya que estoy compilando con el CLI. <br />
<strong>Primera pista: el problema está en el SDK</strong><br />
<br />
<br />
<br />
<h2>
Diferencia entre SDK y tools:</h2>
<br />
Aquí se puede ver: <a href="https://docs.microsoft.com/en-us/dotnet/core/sdk">https://docs.microsoft.com/en-us/dotnet/core/sdk</a> <br />
<br />
El SDK nos da:
<br />
<br />
<ul>
<li>Las herramientas de línea de comandos (CLI)</li>
<li>El runtime y librerías necesarias para compilar</li>
</ul>
<br />
En cuanto al tooling para Visual Studio, nos permite trabajar con .Net Core en Visual Studio, añadiendo plantillas de proyecto y permitiendo compilar, depurar, etc...<br />
<br />
<br />
<h2>
Primer intento: reinstalamos el SDK</h2>
<br />
Vamos a irnos a la página de descargas de core (<a href="https://www.microsoft.com/net/download/core" title="https://www.microsoft.com/net/download/core">https://www.microsoft.com/net/download/core</a>) y ver que tenemos disponible<br />
<br />
Allí podemos descargar el último SDK (actualmente descarga el SDK 1.0.4) y el tooling para Visual Studio, que para 2015 es un preview (nunca dejó de serlo y ya no lo hará).<br />
<br />
Voy a descargar el SDK y reinstalarlo, ya dijimos que todo apunta a que es el problema. Realmente al ejecutar el instalador ve que ya lo tengo y me pide reparar:<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7lFDdULJRbGSABEOWnsHjO79wyrFv994gFglMsn6EhnP2tBiC9j-fL2U7b5EZDFieus_nHkvQxKNLIw2nAq1JNnuzB-_yw5zxOEsVHmYinwbw7JgRl7nnCIc9ALWwcOhl-OmLVamLyABZ/s1600-h/repararSDK%255B4%255D"><img alt="repararSDK" border="0" height="255" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRBcelqeEzGjLmkZQSGX0Bnj3-LKiAgNGwHe6JTSMmuhkRvLIiZ_bTDN0crmVJ5DArm1tl-7wCBGC350V6H98EaoE_fbfPdByCBnaOr-l0R3m7pPfh8bUEuEFkCylHt1nab9wZzf0eUDym/?imgmax=800" style="background-image: none; display: inline;" title="repararSDK" width="374" /></a><br />
<br />
<br />
Después de hacerlo... nada, <i><b>dotnet build</b></i> sigue fallando, y la compilación desde Visual Studio también.<br />
<br />
<br />
<h2>
Segundo intento: reinstalamos el tooling para visual studio</h2>
De nuevo se encuentra en la página de descargas de .NET Core (<a href="https://www.microsoft.com/net/download/core" title="https://www.microsoft.com/net/download/core">https://www.microsoft.com/net/download/core</a>) <br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvIepaisDA3WGC379EjSLLCtBUk_DbN37ASxfPK2yKTq7y2AKwCg31kG7VsWhgI88PuYwAsEeEOg8DLo2RTXO7c_iVDNOU-WJDQdcw6a3Fc9sm9CPHWALIuSxChdsUu9KVeKaE-SJNWK4n/s1600-h/tooling%255B5%255D"><img alt="tooling" border="0" height="570" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWv1jiwjiofcX7SLj676UfHrM9SdoqKePfIMS5S5T_C1g8NE6tHiuCbEe2DKu6LbuJP7ThfJqJ-vwnXnvpSVCBq810ogcq4OIzuWGCX4j4u-UH-XugoMFjxsyw18yTWyjUF9VSl5uGhUUK/?imgmax=800" style="background-image: none; display: inline;" title="tooling" width="654" /></a><br />
Después de hacerlo (de nuevo detectó que ya estaba instalado)... sigue fallando la compilación<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhsdPB0sl71bQKlsT2s-C8f6K10e_6hjSL3jE5brrb1d3-J3RKO7lp8k4ekxltlwkfzajC38ryRdvOdRmxe_FO98bLOva4l4chatYViBLMuM4tupeTv0ASrUsholWsBfj1sgNw1b0BAIm9N/s1600-h/repairtools%255B3%255D"><img alt="repairtools" border="0" height="221" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxriqySLlR1AvA_-OooRRp6TsdrGhmHnt2AI_C3YZgsbVnTMbTcaC8Cel0Ola9hiICxmLKYeM4P77aE20BQmTlgVDOElBKlLsGf24imEPllTOgEwpVkejjYIEgWL40oshzzpWblhhOhJQn/?imgmax=800" style="background-image: none; display: inline;" title="repairtools" width="354" /></a><br />
<br />
<h2>
</h2>
<h2>
Tercer intento, desinstalamos todo y empezamos de cero</h2>
<br />
Después de desinstalar todo solo voy a instalar las tools de Visual Studio 2015<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEilOhPVNsvGsbWOLrwOLvjVI1WhKe25URjOyR-6VblVfBzuoZcIHHKoYOgrNaW-zPE7Q8MYk8sLdFM59f4ACcAG0K1JAiOz1xJh0vwl86DQe-PBzSODM1Ipo2lGvFR-UcpK5gYRmqhvxcpp/s1600-h/image%255B41%255D"><img alt="image" border="0" height="218" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEillD_Mb05Odpm074nG7dUXaa3kts-D3RNgqyjS9C68FsJ_U4K5IRgJJbfqssAihyphenhyphend9813iQdLLE1Q9GA8Js_Jui2pJ3AavPcyyagoeI2F_Rnp_Tw-UIs6pXo93xx1F2V8lDTD2Ls44JGAz/?imgmax=800" style="background-image: none; display: inline;" title="image" width="354" /></a><br />
<br />
<div>
<b><br /></b></div>
<div>
Ahora puedo compilar, tanto desde Visual Studio como con <b><i>dotnet build</i></b>, <u>Por fin… el paciente está estable</u></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
Vamos a ver como tenemos todo ahora (que versiones): </div>
<div>
<br /></div>
<ul>
<li>Versión del runtime de .NET Core, ejecutando el comando <strong><em>dotnet. </em></strong>Tengo el runtime 1.0.1</li>
<li>Versión del CLI, con <strong><em>dotnet --version.</em></strong> Tengo la versión 1.0.0-preview2-003131 del CLI</li>
<li>Versión del SDK (que debería ser la misma que la del CLI), con <em><strong>dotnet --info. </strong></em>Efectivamente es el 1.0.0-preview2-003131, como el CLI</li>
<li>¿Hay otros SDK's instalados?, Para ello miramos directamente en la carpeta <i>ProgramFiles\dotnet\SDK</i></li>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEga4dp7BiwN0VKlquDSnt2FgSXNtlw8PswvlpC0sdBJyijmqAZFqT9KiDsWAy7pTQ5J6q75asFr9trGMEBWEpUf5pz6hH1accccm-jDn-TyNFwNZKRWS-BKbrkxLIhbunZVA0riyPy5xc70/s1600-h/image%255B43%255D"><img alt="image" border="0" height="212" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEicsyadT5TvRrCpwhA7_EodgCq9aEjbfUhte6Ehaai5QzlWIjJkTSLGnadnrnYga9CLe_sJTeC6amOeDtsqV765Pkrv8bHnATTTfSSbB7Eg3ntW9zpzWnYaUhSN182T15CZ4YHOUzHK2Aer/?imgmax=800" style="background-image: none; display: block; float: none; margin-left: auto; margin-right: auto;" title="image" width="504" /></a></ul>
Parece que no, solo hay un SDK, el instalado por las tools como era de esperar.<br />
<ul>
<li>¿Qué dice Windows que tenemos instalado? Miramos a Agregar/Quitar programas. Tenemos un Sdk preview y el tooling preview</li>
</ul>
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggFKGjeqGo-bjMm6mNiHlOoBbHUupA7rtlokFdLCv7kqkIBPc1IfZ1qpOtePhBT2-puFir8aCWMtGf2CQUximVRPexgqVgB0YTLRnfXpHcxETKpHjFB54UaYnFcCEsC1lKQvioNSe_6uZq/s1600-h/image%255B44%255D"><img alt="image" border="0" height="197" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJIpe1rVw2VzQyn9xsSxkOWQmZbaGsOMa2YToYlm_L3rsNYpHf-BqS2HFzyRb6fhm59o6ah5-DN1iVrDv4YH2v8TcAdb5cpUhUS172EORPi2MutVzXVWzOC6T55bl-bzqQvSrD4D2zM4qO/?imgmax=800" style="background-image: none; display: block; float: none; margin-left: auto; margin-right: auto;" title="image" width="404" /></a><br />
<b></b><br />
<b><br /></b>
<br />
<div>
Como decía el paciente esta estable, pero claro, solo dispongo de .NET Core 1.0.x , no tengo opción de 1.1 (de 2.0 ni hablamos , eso será otro post). Y lo malo, mi aplicación, que ha compilado bien, no debería poder ejecutar ya que su target es .NET Core 1.1. </div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiX2sHIwljADjRbTiIIhVizbqeAKXK6SXsgYrEifm7jeEuD088Fgt_Slv7rEElRVBfBwbIvRPkUele2ElJ6V9wf2KZZO8juraNLQhgXL_d3hKa5pPD51EOcYR3WNa3-UWywfeImfZuJNmK0/s1600-h/image%255B45%255D"><img alt="image" border="0" height="334" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8ikcfmpmUBcbI81xXg574V8M_j604D9615_2-OzQbkSO7E_dBSOTdUDwfpEF0i7llMr-41D5vKHU_EaD_M52grHBgsCnGYXJBN_nlX_ZBlnBQyuJ5xNIwUfFFtQGB0DsZl7RK6PXvtBoV/?imgmax=800" style="background-image: none; display: inline;" title="image" width="404" /></a></div>
<div>
<br /></div>
<div>
Como esperaba, obtengo un bonito error al tratar de ejecutar. Me indica que el runtime .NET Core 1.1 no esta disponible </div>
<div>
<br /></div>
<div>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXXVM0QSRFznX59y5Qbb3dr5udTcF6xoSpBYL1ZcOY56s_cbXiVG3B2bPh8XnXNKE1_na3UoRcHMY0Wer2wu57tDdiqZAyUhULOx8e8akDHglzfghcdV4-zNIme4OTaX3yo3jiivlxaLbg/s1600-h/image%255B48%255D"><img alt="image" border="0" height="132" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjs9Xn1Y-uqVengDVJgV7ouN-tNuGL40jot7oe5qNAJYR2qg4b4XVMw_yccRNpSZhaxRVkpJMRxcxupPLa97PT4t4bl3fz5gRReO2618hso4T4XMeJaIr7sUJkHoHML9vwtSku1LHc75kVS/?imgmax=800" style="background-image: none; display: inline;" title="image" width="754" /></a></div>
<div>
<br /></div>
<div>
<strong>Es curioso, aunque no puedo ejecutar, puedo compilar .NET Core 1.1, sin que haya runtime ni SDK apropiado en mi equipo</strong></div>
<div>
<br /></div>
<table border="0" cellpadding="2" cellspacing="0">
<tbody>
<tr>
<td valign="top"></td>
<td valign="top"></td>
</tr>
</tbody>
</table>
<div>
<br />
Ahora vamos a instalar un SDK reciente,que nos permita trabajar con .NET Core 1.1. La página de descargas me ofrece el SDK 1.0.4, que incluye soporte para .NET Core 1 y 1.1. Lo voy a instalar</div>
<a href="https://lh3.googleusercontent.com/-ZCB1jgv6PyA/WZMNDg5i1tI/AAAAAAAAA64/Cg1LVuUGej4eSDRALTQc-KcuJNoCtvcFwCHMYCw/s1600-h/download%255B7%255D"><img alt="download" border="0" height="554" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9GW_QkflQPFv53G2Zll_x1hh0zF3fpPz83lvIM6uKuaFs9FoEPNJ10Kc5s_7dux7rFPw-cukaQxkBXFKzpUqUlT-tqCGcv63I_rPxF1q8dpvBjl095yOHJQiJUPw4JGeHmIJ-LyWprmUe/?imgmax=800" style="background-image: none; display: inline;" title="download" width="829" /></a><br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKgwaxGMsUaoaacYN0AEboLOwKw9nXCz8PIToLTJZHUPfcyLKI5l-PbL56uEa0XnQr_p4uEycfx4USXEkiqeVrfOWtJRSUQyynwqm7DAEMISCMzMFO7o_NSKYXfKLbHHHIfsbkXCbkY5pf/s1600-h/image%255B50%255D"><img alt="image" border="0" height="310" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9Alh_YU9mLhB9n3Yd9qN38XbPTfRn6SpUmES0Wdvu6RlVCkrLNUi83gpA9lA8O0XGB9NeeH-gh0l5RfPtJ9aUoKz2-slDd83_Ty5eSIBJqS5-3Pk5VkcUYgu9HxWbpCDa_CUTTOUPe_vN/?imgmax=800" style="background-image: none; display: inline;" title="image" width="454" /></a><br />
<br />
<br />
<strong>Una vez finalizada la instalación, problemas: A partir de ese momento... si intento compilar de nuevo obtengo errores!</strong> Se ha vuelto a romper. <br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiW5G0srS6AQAqsoeseYBGYo1qpPc0eENSH_ri58c7ufTL-VZeeeN1HqC6Kp9Xl5MRTukcBFrOKyxHx3RqXlzlw-PR3Yy2uSMCg0zEqyaCTMPYufUXVQ1by8YCaHWpoCVHREIZr-cKDBIr2/s1600-h/image%255B53%255D"><img alt="image" border="0" height="191" src="https://lh3.googleusercontent.com/-SZ1fhMN39qM/WZMNGkeJ4pI/AAAAAAAAA7M/0MX-dk-YoLko3YsV1ov2mneuRxm6wpHXwCHMYCw/image_thumb%255B26%255D?imgmax=800" style="background-image: none; display: inline;" title="image" width="804" /></a><br />
Vamos a ver de nuevo que situación tenemos:<br />
<br />
<ul>
<li>Versión del runtime de .NET Core, ejecutando el comando <strong><em>dotnet. </em></strong>Tengo el runtime 1.1.0</li>
<li>Versión del CLI, con <strong><em>dotnet --version.</em></strong> Tengo la versión 1.0.4 del CLI (como en el punto de partida)</li>
<li>Versión del SDK (que debería ser la misma que la del CLI), con <em><strong>dotnet --info. </strong></em>Efectivamente es el 1.0.4, como el CLI</li>
<li>¿Hay otros SDK's instalados?, Para ello miramos directamente en la carpeta <i>ProgramFiles\dotnet\SDK</i></li>
</ul>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjk9QrI2kQwHgEuc0wQcLi6NuvTULUVdXwcCk1ILDzkPm-FKK-0zlahNBEw0R9RA-3Sfz4TODaBaSlHe2uKEBKLEJbAjz_kK7J8EQuQ5sZigIF0FtGPBytmhrW7XeIQNEgrSPZgUsag_QbO/s1600-h/image%255B55%255D"><img alt="image" border="0" height="249" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmVF-1PSEo0YRAVSS64Tw7GcdhMKg55JsiMsLRAH5txUplb13K2uIhel8VvX9djC12cIh2rD4cOA3H5xSzAUD-Blx4r_CYs5IPzDsB2azxmpVvijBDpXM42wVgu8rLroJfIkyGh09HtTIG/?imgmax=800" style="background-image: none; display: inline;" title="image" width="604" /></a><br />
Vemos que si, ha aparecido el SDK 1.0.4<br />
<br />
<ul>
<li>¿Qué dice Windows que tenemos instalado? Miramos a Agregar/Quitar programas</li>
</ul>
<a href="https://lh3.googleusercontent.com/-YAfh9kUw8TQ/WZMNHySDXPI/AAAAAAAAA7Y/25bUHb6NVcgvk_pn_Yjx3NKWBDKXp4bHwCHMYCw/s1600-h/image%255B56%255D"><img alt="image" border="0" height="193" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSK5xEXva2DWcQXNRx-h3JWqQ-8xEBkPVZTO2OxtCZ9SSpajlVz-ckG5iCspoRYdHyGhKqu-p8bWjrs2BMR4zfr9ddYTfyM6C9rMdioBRsqM9KvV-waHslWUyWC-2eaG7nBSMZ4KvkQR8_/?imgmax=800" style="background-image: none; display: inline;" title="image" width="454" /></a><br />
<br />
Tenemos lo esperado: Tooling de Visual Studio 2015 y dos sdk’s, el instalado por el tooling y el mas reciente (1.0.4)<br />
<br />
<br />
<h1>
¿Qué ha pasado? </h1>
<br />
Pues parece que <strong>el SDK 1.0.4 no trae soporte para Visual Studio 2015</strong>, o más bien para los ficheros de proyecto tipo json.<br />
Con el lanzamiento de Visual Studio 2017, Microsoft por fin liberó un SDK en RTM (versión >= 1), que como ya soportaba el nuevo (¿nuevo?) fichero de proyecto csproj “mataba” el soporte al fichero de proyecto tipo json.<br />
Fijaos, fue aquí:<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5BumUpx-eBCAm9cPFJLmYDiUdYZtvml9SbPgXcHxUy5Dsiz7axc34P5dOwlh-bmdEfwi91cYvvvCkETKm2V1liFLQnYdRqVfzAw3y_sKpFfGVqL5_kNkMYNWy4zdq74tPrUpr8u_DCr6J/s1600-h/image%255B60%255D"><img alt="image" border="0" height="472" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhuF3-ghNz9opjDUZ0_qdzl-D9ockaS_a7HzeWOaH9z79rnTzfg95Z7Nvr7l9j8wmk9OLuaiRFkoL_YFATw5ZHZvHGhv8jQBpXXCR875Qg0A8n1i1NjPy8OMcknFuobm6MXOTWJwVd1Xnsi/?imgmax=800" style="background-image: none; display: inline;" title="image" width="604" /></a><br />
<h1>
¿Como lo arreglamos?</h1>
Ya hemos mencionado que siempre se usa por defecto el SDK superior disponible, pero tenemos que hacer que el SDK a usar sea el que queramos, es decir uno con soporte a project.json. <br />
¿Cómo se hace? Debemos crear un fichero llamado <strong><em>global.json</em></strong>, ubicado en una carpeta anterior a la que contiene el fichero project.json (y por tanto nuestro proyecto) y allí indicar el SDK a usar. En mi caso será algo así:<br />
{<br /> "projects": [ "1.Queues", "2.Topics" ],<br /> "sdk": {<br /> "version": "1.0.0-preview2-003131"<br /> }<br />
}<br />
Como veis indicamos el SDK y las carpetas dentro de las cuales queremos que aplique. En mi caso las dos carpetas donde tengo proyectos. Indicar las carpetas no es obligatorio, podríamos solo indicar el sdk, que aplicaría a todo el árbol de subdirectorios.<br />
Una vez que tenemos el fichero todo vuelve a la normalidad, y podemos compilar sin problema, además de ejecutar aplicaciones con .NET Core 1.1<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi707YN9YAMdd_1JtMRd4p4Oc_mi42G5qgM3wajG0mp7bElBh_YW-bxHZvFXt3QsgV-GLmiJbg3tOTlMK5KFCc8_XUvpZJ6VjrKTa2XhsbBZoPw-bLvFDTVyk9yMCT5eu1CsWh1l9FL8gmD/s1600-h/image%255B61%255D"><img alt="image" border="0" height="586" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfi564zpomzcaVcMHm-RLzTrPJbOjAuG9uUy2xnnziwmdHoASBFhwRvbc469nUNjbkUkeYBUO1uPQrvdh2ir3MFJ_DaeeWN_6pozAwo1jf_T7fHr9UzzD7Dzo2jB4L9KGdzc2aDR8POBEe/?imgmax=800" style="background-image: none; display: inline;" title="image" width="1104" /></a><br />
<br />
<br />
<br />
Espero que os sea de utilidad!<br />
<br />
<br />
Recursos:<br />
<ul>
<li>Post de John Hilton hablando sobre esto, que fue escrito cuando salió .NET Core 1.1: <a href="https://jonhilton.net/2017/04/17/making-sense-of-the-different-versions-of-net-core-runtime-and-sdk/" title="https://jonhilton.net/2017/04/17/making-sense-of-the-different-versions-of-net-core-runtime-and-sdk/">https://jonhilton.net/2017/04/17/making-sense-of-the-different-versions-of-net-core-runtime-and-sdk/</a> </li>
<li>
Historial de versiones del runtime y el SDK de .NET Core: <a href="https://github.com/dotnet/core/blob/master/release-notes/download-archive.md" title="https://github.com/dotnet/core/blob/master/release-notes/download-archive.md">https://github.com/dotnet/core/blob/master/release-notes/download-archive.md</a></li>
<li>Documentación oficial sobre el fichero global.json: <a href="https://docs.microsoft.com/hu-hu/dotnet/core/tools/global-json" title="https://docs.microsoft.com/hu-hu/dotnet/core/tools/global-json">https://docs.microsoft.com/hu-hu/dotnet/core/tools/global-json</a></li>
<li>Mas información sobre el global.json: <a href="https://stackoverflow.com/questions/34791682/explanation-of-the-projects-section-in-global-json-in-asp-net-5" title="https://stackoverflow.com/questions/34791682/explanation-of-the-projects-section-in-global-json-in-asp-net-5">https://stackoverflow.com/questions/34791682/explanation-of-the-projects-section-in-global-json-in-asp-net-5</a></li>
<li>Entrada de Jonathan Mezach acerca del versionado en :NET Core: <a href="https://blogs.infosupport.com/net-core-versioning/" title="https://blogs.infosupport.com/net-core-versioning/">https://blogs.infosupport.com/net-core-versioning/</a></li>
</ul>
Sergio Navarro Pinohttp://www.blogger.com/profile/15840814836353973005noreply@blogger.com228055 Madrid, España40.483224 -3.619220199999972514.9611895 -44.927814199999972 66.0052585 37.689373800000027tag:blogger.com,1999:blog-7984577123741299127.post-32466371521435537672017-06-18T20:30:00.000+02:002017-06-20T21:52:57.285+02:00All about Entity Framework migrations<h3>
<a href="https://draft.blogger.com/null" name="_Hlk485574660"><br /></a></h3>
El pasado miércoles día 24 de mayo asistí a un meetup de MadridDotNet (<a href="https://www.meetup.com/es-ES/madriddotnet/events/237898951/" title="https://www.meetup.com/es-ES/madriddotnet/events/237898951/">https://www.meetup.com/es-ES/madriddotnet/events/237898951/</a>) en el que Sergio León (@panicoenlaxbox) nos habló en profundidad de Entity Framework 6.x, sobre todo de esas cosas poco conocidas que solo alguien que se ha bregado en las trincheras de EF puede conocer.<br />
<br />
En la charla surgió un interesante debate sobre las migraciones automáticas, con diversas opiniones y referencias, entre otras a un comentario de Luis Ruiz en un reciente video de PlainTv (<a href="https://www.youtube.com/watch?v=D38K7URXKZg&t=45s">https://www.youtube.com/watch?v=D38K7URXKZg&t=45s</a> ): nadie debe activar las migraciones automáticas en producción.<br />
<br />
¿Qué significa migraciones automáticas? No quedo del todo claro el día del meetup.<br />
<br />
Pues bien, pretendo con este post aclarar un poco las cosas, ya que creo que hay cierta confusión, que parte de que en general pensamos que hay dos modos de tratar las migraciones: manuales y automáticas. Sin embargo, la realidad es que hay 3 modos de funcionamiento, vamos a verlo:<br />
<br />
<h4>
<b>- Primer modo: Migraciones automáticas</b> que se aplican con el comando Update-Database, sin que previamente sea necesaria la creación explicita de la migración</h4>
<br />
Las conseguimos mediante un flag específico que debemos indicar cuando habilitamos las migraciones: <br />
<br />
enable-migrations -EnableAutomaticMigrations<br />
<br />
<h4>
- <b>Segundo modo: Migraciones por código, o “explícitas</b>”, aplicadas bien con el comando Update-Database bien con la ejecución de un script en la base de datos</h4>
<h4>
</h4>
<h4>
- <b>Tercer modo: Migraciones por código, o “explícitas”, aplicadas de modo automático </b>con un inicializador concreto de bases de datos</h4>
<br />
Aviso a navegantes: entiendo que estáis familiarizados con las migraciones de Entity Framework 6.x, no pretendo que este post sea una introducción a las mismas, sino una “discusión” sobre las opciones que tenemos y cuando usar cada una.<br />
<br />
<h2>
<b>El ejemplo:</b><b> </b></h2>
<b></b><br />
Vamos a trabajar sobre un ejemplo, lo más simple posible, que nos ayude a entender bien las opciones disponibles.<br />
Partimos de dos entidades, superhéroes e identidades secretas, que se relacionarán de un modo “one to many”.<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTkzTpL0OoEBfeWUxOMZeLltqmFlAeH1QyW5UkE-_VhtPRnTT0PnCZ7ZQ5RKiOveKNhtiBNf_eogHh16VoeFSIhTuf3MGoIxzpbTFScwSn6PcFg4wS9B7jnjNItEeb0JuEoA6lUWZJOuL3/s1600-h/clip_image002%255B4%255D"><img alt="clip_image002" border="0" height="540" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbeJJDPXrmaOpIl_QBwxMA3REu4PZpMXaCpIaP1H_yNxtEFMjm65RlfYMIB6JVU3ZTfFuq_Cls9qRF_Z2wCz6yI8rJiCaO2twyl5jI3oQdx0YQiqcj7URC-s-dT3ohDLVgtwtL6qZO6l05/?imgmax=800" style="background-image: none; display: inline;" title="clip_image002" width="723" /></a><br />
<a href="https://lh3.googleusercontent.com/-7p8XQYF2F0c/WUbBVGAZ5KI/AAAAAAAAAwI/rd2LmDZ1gJkVkHJvd_LzEKm4Pz-6iEUPACHMYCw/s1600-h/clip_image004%255B4%255D"><img alt="clip_image004" border="0" height="294" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhr8dlYwzq0jrfX1VpFLgHxH_wbuAONHGi35viIehPjtEWLxqClMZaP0J53mStilmbB2Y1Q1sRxb7r2DNLQu6Q-25ShDGXk-AVDgtWwILFrkYZbz7iJhX9xID4r89mHW-msKQr9OjBarj4j/?imgmax=800" style="background-image: none; display: inline;" title="clip_image004" width="684" /></a><br />
<br />
Creamos nuestro contexto en Entity Framework:<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhboJzblKSXGLXVjkj6ii_jg2zcV4Dvty7ZGdiOS5fgBZCpWMGuNxZlrOwWbokKWGoyFdQczYFTBw2BhLy6_BfRx_8iAWKWFyh-O0BfTcAt957dRS8fxkmsLdSrvHJV3bUN2ak0Bsq_CJ6E/s1600-h/clip_image006%255B4%255D"><img alt="clip_image006" border="0" height="162" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgaYiw7axO5wAMwN1isgqft6axZIX1TXZYwIEXoFu6QudbYo1K6WOWTEANblVZWGMdH3LJLbZsIes-3gIUvqNN1R3ReoqqXKB7NPzZ-H2YmGn7JhwK2_o_2Ev33WRy_qrNt2WOo_uskz040/?imgmax=800" style="background-image: none; display: inline;" title="clip_image006" width="760" /></a><br />
<br />
Y finalmente una aplicación de consola que muestra por pantalla la información leída, es decir los superhéroes y sus identidades<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieQXzpstQ1MjbRBBFY_wVXf2zKUVue55Tr5TkpZ5O3H3dZ0q7ikFHOGqvgM2KL6zAHv2zgjf07MDwJFMS_S2xJcJLmYLNqdaadz0_7MAvxJTP3yIoqe8Z13gacVmL3VDoWLs0rmGhYt8sv/s1600-h/clip_image008%255B4%255D"><img alt="clip_image008" border="0" height="510" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJXpNHcUGRvPM8MJVxzOacg0z7qsUzCn0y1TTk3TV3QgueNLOj4Bs8t2ZbG3I_mFUfF8P6wYUzfMz3RpW6ldJQnhlB-9fXGhlYbLaAQuulHecHGDSs9NivjPskDvbb9o2GksDmJhS5tIwO/?imgmax=800" style="background-image: none; display: inline;" title="clip_image008" width="722" /></a><br />
<br />
Si ejecutamos, vamos a ver como entity framework crea la base de datos, usando LocalDb, pero claro, no tenemos datos, así que vamos a crear un inicializador para poblar nuestra bbdd. Vamos a heredar de la estrategia por defecto, que crea la bbdd si no existe.<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8GUYiNsHSKopeCADnOCcPNawUmxzvfD6eCZ_BznQZA3E51z4d9BndPkigU2Ntc_EWfZEP_4ZKn2age1-Pj_YA7mlH4R5vRV-5QfnbVt0j25iyQfl4IgnfIY6WVTCZrkWCzWWfgmvXwmOL/s1600-h/clip_image010%255B4%255D"><img alt="clip_image010" border="0" height="674" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNlVGANJ8L1glAYxRRJA0LKh8NyFX-ayvAFiJnm2NTVHwE33sI-i_gC3G4Ool20CEgKHP1czMedkTt4nqEdDhp35Ns3EJ9NoV5w7BxUdnPZK27rtZv7HMNGGpkYcNneWKg_kacjRXisXCW/?imgmax=800" style="background-image: none; display: inline;" title="clip_image010" width="760" /></a><br />
<br />
Con esto ya podemos ejecutar nuestra aplicación. Vemos por pantalla los dos superhéroes, así como las dos identidades secretas de Superman<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgz6fGpLAGXOpKdZlsBklUFdT7j592b52mFevuzwu3jr2f0gVGOwVmw7jJg2oP21NOFlO39dax50kDKoYBewXuchkP63LoX_mSuBqKLkHh_tC9Owp4xINmWDOeLFiwjghVoD8sn8DJXrvxY/s1600-h/clip_image012%255B4%255D"><img alt="clip_image012" border="0" height="360" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgps8S5MGUawcAjB8OUiS4g4KPmTCnAeS3EJod0CZcfqhr_fahfaoat2_GkPNrSFBIBHZekauXk8Zb0bl3X2z41Xu6D-_fMPIps0NoKHriU7cNNRk5f6RcIFhsy4kWK8pBizWfH8UJxerFC/?imgmax=800" style="background-image: none; display: inline;" title="clip_image012" width="684" /></a><br />
<br />
Como era de esperar la bbdd ha sido creada ya que no existía previamente, y los datos insertados. Lo que llama la atención es la existencia de la tabla de migraciones… ¡pero si no las hemos habilitado aun!<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhNNj2TciIps89yZAkdF30LwE_0YXnF_G5VKbfCdmkpycuP57Tz-hYdFYaxptkQLA0a6wnKdm9OB1-mnfrHMZpYQF5zn6ibkpfEvzDGcJ517lReOJ_oY9jZZrXFYSjasuPU3B1OukHMTfUv/s1600-h/clip_image014%255B4%255D"><img alt="clip_image014" border="0" height="356" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHkOF-cd-WxynPP8k7sz7Spa2I_jW3SesEHJ6NNSmezmnWwld5t96On9UrgOJ7CCaYnqhkMOU_w-9dUS8lGSpeQuNsUQnt0pihoiZzjUwWPTLfZsX3xMKzGWSLFpPw5bZHssR60ohC3JfR/?imgmax=800" style="background-image: none; display: inline;" title="clip_image014" width="495" /></a><br />
<br />
Parece ser que Entity Framework crea la tabla en dos situaciones: cuando se habilitan las migraciones (hecho que aún no ha sucedido) o cuando Entity Framework crea la base de datos, lo que sí ha sucedido debido a nuestro inicializador. Aquí hay más detalles:<br />
<a href="https://blog.oneunicorn.com/2012/02/27/code-first-migrations-making-__migrationhistory-not-a-system-table/">https://blog.oneunicorn.com/2012/02/27/code-first-migrations-making-__migrationhistory-not-a-system-table/</a><br />
<br />
Con este punto de partida, vamos a empezar a trabajar con migraciones... en los 3 casos que estamos estudiando.<br />
<br />
<h1>
<b>1. </b><b>Migraciones automáticas</b></h1>
<br />
Lo primero inicializarlas con el comando<br />
<br />
enable-migrations -EnableAutomaticMigrations<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8mV91Sc6shglZkO2zxIJx5O5QqWTad-S5a2bA6dJ41yC2p4qwtZzwN21jNQTZt0Huw2EYH0H3nMElmf7MpwZJ8qmK2mOAOdgI6aTOUS1zxylEQg8G_alqMM9CAJyHQFlqJb-pf1LLKfVN/s1600-h/clip_image016%255B4%255D"><img alt="clip_image016" border="0" height="107" src="https://lh3.googleusercontent.com/-zpw7bennqAc/WUbBbqj7g1I/AAAAAAAAAw8/Djw7afWWeHM-p6cW0zB2ahwGXXtXJhj6wCHMYCw/clip_image016_thumb%255B1%255D?imgmax=800" style="background-image: none; display: inline;" title="clip_image016" width="684" /></a><br />
<br />
Una vez ejecutado, vemos que en nuestro proyecto ha aparecido la carpeta Migrations, conteniendo un único fichero, con la configuración de las migraciones<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmvNEbHJ-uQu22VDjqFRaotk6la5iThH9WzwvWGuA1N9-40pm-4wtCJ2QX_OPGPDdYL_3miEuz38C1LSSJXC4Lq2FZAe7oulyJUFNj1LC03r2w6GdUksb1WguW-0dqpttK4jE9OFCMBEF1/s1600-h/clip_image018%255B4%255D"><img alt="clip_image018" border="0" height="450" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-BTUx-HDMlHtQ125bcb6n3B7x_9Vhh0bbDCWv3fmdtNPBVyvh6MU6uFAlJkBcfa93nGa91d5ZlyYF4QhFXxlEnek9CNQ0-JKECmCXjCEzRN_XV_-fWLLRNZtpzT-Db9Dvy2jxRFqQpe5n/?imgmax=800" style="background-image: none; display: inline;" title="clip_image018" width="798" /></a><br />
<br />
El fichero tan solo indica que trabajamos con migraciones automáticas.<br />
Ahora vamos a modificar alguna de nuestras entidades… por ejemplo para añadir una nueva propiedad a la entidad de superhéroes. Añadiremos un booleano que nos indique si el superhéroe es de DC Comics:<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvr9_05alkEzZq3r03JeXSHyLJ83sGideMwjliSR71Xwso9PmstH7OhlOKYKFsovyKq6UngiMh3DYhM14jFmWWthLXz5le_oxSXHZfUw8F_vRRNBPGdHR_1kCpjgCDrthrGyWCXcR93E2u/s1600-h/clip_image020%255B4%255D"><img alt="clip_image020" border="0" height="359" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFPdi2XX_RhqMcxZmas2WYy9-nLaXX1L9JGtFMTvWewZRK2cU_1hS886S47VK5qtx6NlChcPJb9Rw45H8N7a4gmB-RiBWee3fsBMmVG3iad9Onh4nVzuhEuSvvWevu11ZrL34d9NJLUp2T/?imgmax=800" style="background-image: none; display: inline;" title="clip_image020" width="760" /></a><br />
<br />
Y ejecutamos (nótese que no hemos creado una migración a mano) obtendremos una excepción indicando que nuestro modelo no corresponde con la bbdd… lo esperado<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhIK0Jkp9Cn3EXrrWH1zbtGq70DVUVtHnYdXDzF7-3qBwSRk6t1YfYuiqvYPXTdgaCgMRDK_RtKYVH41Kk2mQlqM3ja_y760xND339z04179Hm159GSeWssKaL8mnCtwZtsHjwoN6EqSIHM/s1600-h/clip_image022%255B4%255D"><img alt="clip_image022" border="0" height="365" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQVvVdhJhFcRcIKdaOKrBd76L17yYr_lf4lPxxGWOit7ExL0vO3yj72oOqPdP_n0nWbrjQvGWjmrTqZR_ijYmY7U8Flv_oOBPm165iypLjWwmGl3YO6ldlLWnf7fXV7mmtrdEYnrwYkeIT/?imgmax=800" style="background-image: none; display: inline;" title="clip_image022" width="684" /></a><br />
<br />
Debemos actualizar la base de datos, <b><u>sin crear migración alguna previamente, no hace falta en este modo</u></b><br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhT5_m02TOq_TFo_CUZru4M0YWgkl_4_ENy8jRCv5Q8U2Y5Bbi_rz5QO4mw_GAsLA-GfEvsH5k8Q4HFy73IKR4bo2MWUEA-sBAIE8cc_yJqcgcUv_gjx78H8wMPNypCgE7pWKJcJbMjB5lU/s1600-h/clip_image024%255B4%255D"><img alt="clip_image024" border="0" height="73" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIl1Yf7UBOy40THx0PLYe1kuBKjU8erLPU-c6cOUg13H2KSx1mfMC9IP1X9NHAGu_HnYRb3QV85WSUy7Qoe4mg36CyalJ2mqjwEWuMxFEepKUQ7QnVAfG939EC-Ain_DQlvNDD8keDIud8/?imgmax=800" style="background-image: none; display: inline;" title="clip_image024" width="593" /></a><br />
<br />
Y ahora nuestra aplicación funcionará sin problemas, aunque claro, la columna nueva no tendrá datos.<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjiYD7Hi8-mSwGiiNn05N5qz3I4HsY4qvKXotJNB464kLVHeROt2-o-gBZP4lTlcX8nA1HDjNZsUh45uLEIdqINjLDu6RivLzcJTjOEVXSd4YoT7S3u8bjiiNuAKKL44eYlHnAs1p3sCN1y/s1600-h/clip_image026%255B4%255D"><img alt="clip_image026" border="0" height="310" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtj0tkoaMH9q8RKpV9cVy7CzI76VA-iMbZX-jf_mYRcczf6Mm5iff4p1IuCUJlBii4KdgyWzPrYEmxOuYL9zO3ViA8SOhWpw-9JQZkklRQsFHUh_f_JV2SiZnUyAA_AT5_l6Az_RIO6tLD/?imgmax=800" style="background-image: none; display: inline;" title="clip_image026" width="593" /></a><br />
<br />
¿Cuáles son los problemas de esta aproximación?<br />
<br />
<ul>
<li>Falta de control. No sabemos que se está haciendo, lo que genera cierta inseguridad.</li>
</ul>
<ul>
<li>Debemos ejecutar el comando update-database contra las bases de datos en todos los entornos, en staging, en producción etc. Este comando permite que le pasemos una cadena de conexión, pero no parece una gran idea, y seguro que habrá sitios donde no nos dejarán conectar nuestro equipo a ciertas bases de datos.</li>
</ul>
<ul>
<li>He escuchado que podemos tener pérdidas de información, aunque no lo tengo tan claro. Vamos a verlo</li>
</ul>
Podríamos de nuevo hacer un pequeño cambio, hacer que la nueva columna no admitiese nulos. ¿Qué sucederá?<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJoEPc4DarvkoG3fHmB6R4mmAbKMTKN8KMSlgMVfwIJYzPTq04SukfPlT1CHWdGyF6O1S6Kt-z60x_bCdyAVquFS8x9UPCOXI_LN83K2qh6ORK7-TrB51wRVXK5Q83ECxZDLGtpCrtFdFh/s1600-h/clip_image028%255B4%255D"><img alt="clip_image028" border="0" height="352" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjI-p0mC2DGoRZLdWrYrnnqR7OuAeLC2QtHlKM0FJkw6eLusGBAglMSd9qPi24xLneytbzogJ8i24WheFPNDY25OLnUL_DXz5d6UuIXviOd6DqBSnqV1yjVkqucg67x5MYeYbEIyt_QHQ1/?imgmax=800" style="background-image: none; display: inline;" title="clip_image028" width="760" /></a><br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgGXsZrnMz45ffM9hgmB74rUFH57EDPUovozYTs4do3Q6uQGbJqhDSA9BSb6h53YSlFq33h10HopaL4DFLLa4M4GE-KAfktV_Vn1FFmYT7AEx7kr3EfTYoQqEPC-HsuhUI7LKpJ7XNZ_YdM/s1600-h/clip_image030%255B4%255D"><img alt="clip_image030" border="0" height="114" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgX5haj73jX3KbWEKQGbNE9j-EQGfIj0gQS6fCkbSRk0ROJEof5ry6-awD2CW8EgmfpQIxndDNLpD71mPgBlaR2VjDEHfiIIsx0Y1m-QC6HRzhLkfO6Ucx5Ys44R0vgz6pVsH1I4nmddz4T/?imgmax=800" style="background-image: none; display: inline;" title="clip_image030" width="1316" /></a><br />
<br />
Como veis somos avisados de la posible pérdida de información… y nos dice que hay que hacer para forzar la situación, hagámoslo:<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_6aX4qGyOkRbtbRsx8mmasl2WH78wHOuNlUo-NcgFotS-8zq7Bx289dWngATcxKjkokfvbu0CimDtjr_IQIgHlvZ3vxdmrtFRabKZNbtiiw_SaEomYfzEesMBW-oOUI5J2JTPc1vC1ngf/s1600-h/clip_image031%255B4%255D"><img alt="clip_image031" border="0" height="126" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_gGpmqsizAnAyl5rHLiBX8oi0ds9IRfOuFB9RbWHsEldAWuWUjY9i_6Yd2r1UBNxwStCjtry4SgEOfD-aclKi1JIUWNkZVs6Zj6fq9bXDamjznF8eWpQ_KApZ4OZ72O9QjEjTpaD0Kh_w/?imgmax=800" style="background-image: none; display: inline;" title="clip_image031" width="904" /></a><br />
<br />
Tampoco, el servidor arroja un error, ya que no sabe qué valor meter en la nueva columna que no admite nulos, y por tanto pone un NULO lo que provoca el error.<br />
<br />
Lo bueno: ¡<b>No se borran filas</b> de mi base de datos!<br />
Por mi parte esta <b><u>opción (migraciones automáticas) no debería contemplarse para proyectos destinados a producción</u></b>, pero si podría ser válido para pruebas de concepto, pequeños desarrollos personales etc etc…<br />
<br />
<h1>
<b>2. </b><b>Migraciones por código (o explicitas)</b></h1>
<br />
De nuevo partimos de nuestro modelo inicial, dos entidades, superhéroes e identidades secretas, que se relacionarán de un modo “one to many”. También tendremos un contexto, nuestro inicializador que creará la base de datos en caso de no existir, rellenando además datos de prueba… y finalmente nuestra aplicación de consola.<br />
Esta vez activamos las migraciones del siguiente modo:<br />
<br />
enable-migrations<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoE6hRHzbLS-QuoK8-6ewOH4cpjSGfgoq9cwiex9m4QLOWJ9AGak2IAifGI58SrGF7AHPBJbtFrTNKB6HlFAV-B9nJ1hbv-YgUU1yZFimx9dxHsP6iUKmzElhVJK1Vfgpcy_ZgsZqzza4r/s1600-h/clip_image033%255B4%255D"><img alt="clip_image033" border="0" height="111" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTvsHk1XWGn9pgWYgLgQAzjEAszyFZl5oC6kqcN2EStaf_opwOOSKFzURZEOBpJ5-_RqWQ7xeNLkVyHY6Y-t2NksiZFz-leHVziFOaLLABr5ru1Nqrj4sm5_51CQXhMYBuE8xJpwQ85DSd/?imgmax=800" style="background-image: none; display: inline;" title="clip_image033" width="966" /></a><br />
<br />
Vemos q una nueva clase de migración ha aparecido. Es la migración inicial, que crea el modelo:<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWUN0ryGWFy-LSpWmUCSh0zPNT-NJszLLYu1JzfoMuTQcssU3Yh4ICqr_rgaswkBanbt1ZyKZ6y8OuAAHgf8THAe4sHwRt-lpoNaBiulWl0IDnly7nJZbGGcolioDTfN2FoXcM8qGqL6gE/s1600-h/clip_image035%255B4%255D"><img alt="clip_image035" border="0" height="618" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhsQd0UCvCI0KlD9Ub1XhPzyucGh2ImkaCO_1wIx8Zvb-wkv2BM0giSOHQLgi_CSV1iAt6z_34h_edM9gIB8Wcr82o8Kw2xMtLaFJx7OxgjJjztVSohf-GQXPH5KoEjbct3LS48uTyIuwZI/?imgmax=800" style="background-image: none; display: inline;" title="clip_image035" width="609" /></a><br />
<br />
Y de hecho podemos ver que la tabla de migraciones ha aparecido en la base de datos, con una fila que incluye esta primera migración:<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfz9oSIXjIEWSkJYDnY0nGjW8MQMbx40UgQLISH5zVD3G7zg1xuEUsbGu_p3QdQWT-bu4YLc4UvywJm4z5DrjW0lkKtcXXMzrkhKHseSEO3D5UjO4tq6MWLRDZu3_91NFwxjxi7ljuRD9e/s1600-h/clip_image037%255B4%255D"><img alt="clip_image037" border="0" height="285" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6mKtdUA04x2LyITAYVlJRnvRHi3Hl7AB9Uw7UziUg9OEeYBdLJ4m1hQPfUTrnIFx6YkC-HnuwHT-peJQBGw7gidenWQaGux5x0huMBlHwa4vYOM_EJ9kY4J3WFa3hsVN-Ez1SOuz4VWOJ/?imgmax=800" style="background-image: none; display: inline;" title="clip_image037" width="760" /></a><br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSv9iCEHZ9PpQaZWycQ5wH6qmYWCGtIw1pcNshqmWAukwoMb9p9eCS6nslMxcUztjB1lzP_d_FFmUFwHU32FD8A2ILk6JgRIvgeE1S6rc3XPZChKrj0OMN41P1b3yZflsy5rNaOCzJjP-p/s1600-h/clip_image039%255B4%255D"><img alt="clip_image039" border="0" height="73" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggHDNiCgc28SU8BrVVbOlOAP5rn3eBXnfTbWcpZ0QEmPufeahSmfTjrtMuPEKVeiuIdO3pbO6kLR8s_T3i-qyyAlamNQaHBXmoI01-vwXtZFy3hb0xfRUKrDaryrCg3eybM-e8OSgKO4xZ/?imgmax=800" style="background-image: none; display: inline;" title="clip_image039" width="594" /></a><br />
<br />
También es interesante hacer notar que la clase que configura las migraciones difiere un poco del caso anterior, ahora se indica que las migraciones automáticas están deshabilitadas:<br />
<br />
<a href="https://lh3.googleusercontent.com/-7BYGxHswHas/WUbBpIzO8tI/AAAAAAAAAyg/whGCcgQvoXs6i_4XKzQhPecaqKzveOLNgCHMYCw/s1600-h/clip_image040%255B4%255D"><img alt="clip_image040" border="0" height="184" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkUeAP93xfon_RXg9Au0K3GxBK3afPloGxowhMUmeOIGYxkgGPZ9E60SMQ6n-SYese1EPTwhRr_A9jpMV0r_G2W1GtpLYsBMVhR4uYfhyphenhyphenqEH37ETKkx9sXpTNFMjs2EdJbzDJcoGOMH3eR/?imgmax=800" style="background-image: none; display: inline;" title="clip_image040" width="848" /></a><br />
<br />
Y realizamos una modificación en nuestro modelo, de nuevo metemos una nueva propiedad a la entidad superhéroe, como en el caso anterior incluimos un booleano, admitiendo nulos, para indicar si el superhéroe es de DC Comics.<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiARPJqPWvHPGEck7SReBfODiXYHERhp1z1492VShzGYdgGDlErQXIp4SgjNFX9sJcKKKV8Bgl9AWU3NXfBmEQuXHqzw4Kp_oh9Bs1HCHfmPe4DASQI_DVoV8lAEm6ZvlrPInbnptSNNyfG/s1600-h/clip_image041%255B4%255D"><img alt="clip_image041" border="0" height="359" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQulQ4r-WlKhjTo2e0YMJ_NABd78omcVKlYPcVzv0GQQbctdT1-o5KTVJofQqCjSf9rdhAkmLo5_LQUolUyRTpTGp65oMbW_47bM3LXPKCN-7NmHeOipZLQhPCr9W9Eh8pPROAxLQD9XnV/?imgmax=800" style="background-image: none; display: inline;" title="clip_image041" width="760" /></a><br />
<br />
De nuevo, si ejecutamos la aplicación obtendremos un error, ya que la bbdd no coincide con nuestro modelo. Hay que trabajar con migraciones.<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipcoCHmgdJVf8ptqikGCalLvviDlVOk1p43Vy15aVIgZAYd7w2eYpLZ2Ud_ijYcCnSyUbTF8KYLHXd1_W_pMUolu23hXnZD0AxHx0DIqXyKho5TYkAmESd8Wj8BK7txGUCPANDWqmlgSUi/s1600-h/clip_image043%255B4%255D"><img alt="clip_image043" border="0" height="341" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMHmJyq_XassdCU7FSPcnFy3sao4cbrzdusX6HKxnJrQBRGNX7iH5Dx5rjCFwq2oKanz4Z3GPOCPaiir9D4L8aK27ZYci6hvFEp1tyJyy9wGUm7IWO8upF5PyUP4T6I7bLTHis_T6lPDBT/?imgmax=800" style="background-image: none; display: inline;" title="clip_image043" width="647" /></a><br />
<br />
Si tratamos de actualizarla mediante update-database como en el caso anterior… no va a funcionar, <u>ya que nuestras migraciones no son automáticas.</u><br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhp3uFd75qdGNKNcBFCC6w6WP3-pP4GJhneNHbr48D5G46ulNRIuUoS7GrE3MHN3tq0qjMyPNvPa8evgMb_P3OxVnMHDsI3sedZS2Beyw1e-m-IdqNqYSByANW-FLb_dThF84lM258iHjWl/s1600-h/clip_image045%255B4%255D"><img alt="clip_image045" border="0" height="104" src="https://lh3.googleusercontent.com/-981yoVRabkI/WUbBsyUa7-I/AAAAAAAAAy8/x8bjIGzeGZkAEI2Lt0QvU2u4ldwz3BfzQCHMYCw/clip_image045_thumb%255B1%255D?imgmax=800" style="background-image: none; display: inline;" title="clip_image045" width="684" /></a><br />
<br />
Es necesario crear una migración, y esta vez debemos hacer nosotros con el comando add-migration. Esto generará un nuevo archivo de migración, una clase C# que podemos examinar (punto a favor, ganamos en confianza)<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYiJxX5O1szatJ_t4LkuausSSKDLcEq7w26u7ZFQuFJLHzQVXKK5Y0wonPp5zzr5Ddr6m2fvvDUYnyJIE7cRlqm7LPQfSWBOTyfXEmXGXdmpVbqBTrU5Arm-Oa5WUV_GPadfs3_w0E4k2f/s1600-h/clip_image047%255B4%255D"><img alt="clip_image047" border="0" height="74" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjf2lnZ67djA5bYiT-EcrM3ruV7tjxdi8pioae_UOvtzcjy9yGKURIWKfGrFu6bklEQzaTDf3DxAsbXiamTDJ_l_0wXWDGkSTqx1v4OEE0tbiZE2XNF-8WzPxf4Uaij9oXLYHmQhy4F7P_s/?imgmax=800" style="background-image: none; display: inline;" title="clip_image047" width="685" /></a><br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjAuEVGiLzmDvGulUQJonuIbvJogvZDqI2vJ1AeKmYT5wBwK7B7GNbTbtRNVsYFj-0hWYwbg-SaexUoRSii1VKmdxdpTfUEWG03eCmuKLQSLXV75Cpggj8owlygFKcE8cqggwpqROq3-1uX/s1600-h/clip_image049%255B4%255D"><img alt="clip_image049" border="0" height="367" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjF52_CcZK-G8cAsjkMwZ48-27V5tzpXq4ir-yRs7QoWaxzAljzGldRmX4peVmczw_IJ-orQUGSVCdIR8SCnHjAXrxiZ8KObbzn18wDGTBr2W6x8oZlJ9QV4Iba3NSUA2Ae59as1vK1r32Y/?imgmax=800" style="background-image: none; display: inline;" title="clip_image049" width="760" /></a><br />
<br />
Una vez hecho esto si ejecuto la aplicación sigo obteniendo un error, ya que hay que aplicar la migración explícitamente. Tenemos dos opciones:<br />
<br />
<ol>
<li>Ejecutamos el comando update-database </li>
<li>Generamos un script de migración y lo lanzamos a la base de datos, mediante el comando update-database -script</li>
</ol>
<br />
Esta vez voy a seguir la segunda opción, así que ejecutaré “update-database -script“<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkIF9JTpjBQC3Wk9p5-o9su0DmGW0RiZ42jUUxi7xI5n7tmLlxy1eIlPf5VGds4ZUpT9VP9HXY1VsotkAgHVIH1VJ5ggb8Ngt6qyzPvE4vdkl0hLZkEPFsTxHmo_hAoQHgeVRhFRLnnL8A/s1600-h/clip_image051%255B4%255D"><img alt="clip_image051" border="0" height="442" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0K1t53T3Sx4InGUfsJtaA_NshzE-t9jaoDfNFsQyBV7DDZ_xF8mL1KSYRaSSVW0w-lUVxjCjMmKBGUIYtggQSICmu-BOADobHGuEGZ9PbyNJyYhdcaEOCgXk4ultcLykgzyOyOR9DOJFC/?imgmax=800" style="background-image: none; display: inline;" title="clip_image051" width="761" /></a><br />
<br />
Me parece interesante el log que vemos en la package manager console: dice que está <u>aplicando migraciones explícitas</u>… es un buen nombre<br />
<br />
Por otro lado, podemos ver que el script hace lo esperado: añadir una nueva columna a la bbdd y actualizar la tabla de migraciones<br />
Si lo ejecutamos en nuestra bbdd conseguiremos actualizarla, y a partir de ese momento nuestra aplicación volverá a funcionar<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGZeBICzhyaKExQdubcfQE1PtB8M2vimJ3PnRApyhSGV20IZqeCxZv2ugKOXDb_a9pYGpwt7v_vVk3hUulECrLtnAishvqce-kMLAwl45oy84OHfJYTntOyN5Lw9uXTdVkHMjRet7xYmug/s1600-h/clip_image053%255B4%255D"><img alt="clip_image053" border="0" height="319" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDwozSjiuLQySw6Qb1ystJT7Z1MEAa2hmYHfsvmhhNrtoIQGiryrN9B0QTd13TSz1B0KVgoNxUAvEu5ei0iIjGa9GPsC2scGzWKI3NocYBBU5UT-u8kdk3YcMQjGcx87k9iofW7oztg3Xi/?imgmax=800" style="background-image: none; display: inline;" title="clip_image053" width="593" /></a><br />
<br />
De nuevo nuestra nueva columna no tiene datos… lo esperado ya que es nulable, y las filas existentes antes de meter la columna tendrán este dato a NULL<br />
<br />
Siguiendo la secuencia del primer caso, nos preguntamos: ¿Qué sucederá si incluimos una migración que implique posible pérdida de datos?<br />
Vamos a hacerlo: reduciremos la longitud máxima de una columna de texto<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijDQ6CNc59CWmThsGaBknaTV21S_d-ZgbgEttWiwJrHx2lLRIq9KDq9oHeXfMlzLP2iw6OfJgkvRpLs5tgwmjuckEi4Mp5y7Zzq4kIV8zTaAoOzNfnopmpvFJy9AmaLtqtnSnZJu85jN0Y/s1600-h/clip_image055%255B4%255D"><img alt="clip_image055" border="0" height="357" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbY_1W82J6nOkEWa-lZNNQWnx2-9-C4_s7WsRIQWbo5LeF9Cg5gpEuhCj60oGJrlPBWxxPSPRj8CDLOtlquh3jTCca0qKYM0Wo5zslSu1NX5vjpieVAy7DwNMR19OuiU0Cqc7zHl4AZOsx/?imgmax=800" style="background-image: none; display: inline;" title="clip_image055" width="760" /></a><br />
<br />
Ahora debemos crear una nueva migración, y aplicarla.<br />
<ol>
<li>Ejecutamos add-migration NameLenght</li>
<li>Ejecutamos el comando update-database</li>
</ol>
<br />
Después de hacerlo vemos que el comando update-database falla, ya que tengo en la bbdd datos con longitud mayor a la nueva longitud máxima<br />
Sin embargo, no hemos recibido ninguna clase de aviso anta la posible pérdida de datos, como sucedía con las migraciones automáticas<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGliDY4RE3rXhs75FqLo-Xzvm6MDlRPfay6Pf8agZ9Uox51YOQwbHPkZ3gCLq3uUk-kWu8C6Vk07rAEOyja1JxhQnjqpndfQt1xpCvYatkFOcscDp9O0ERyDahyphenhyphendJh9nsdKcPDIVCcewWJ/s1600-h/clip_image056%255B4%255D"><img alt="clip_image056" border="0" height="146" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhO1rzmC0lkt4wlaqTsk47jJ1XcN6vxcvRBu7w2fksUzIqLCxGIlIi9fmAYZjqFbypKP_ZTtHNFZc35JUdqtcHhklPLBhFaoQVBF4D8EqLj3MlkvBxjCkGiR8OvwXTTgom7EeYdcneXDAs/?imgmax=800" style="background-image: none; display: inline;" title="clip_image056" width="804" /></a><br />
<br />
Si hubiésemos generado y ejecutado el script, en vez del comando update-database, también tendríamos un error<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfz3l-H4oCK-NYwVhfjjt0RxEs25shfOtPxqE7N9hpQ4FhoDUEfN6bWNT2UL4Bw3IBioUNjpuCqNr_OAjjuOFKSzULz-Cs2FwDaljAyqp2LFdK2ZRczqNpN2xzAPvu3WM7EbdwVZ_yG0sX/s1600-h/clip_image057%255B4%255D"><img alt="clip_image057" border="0" height="193" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGgYwAfqFmajfu8d9eflxY6CUpQ6mZkrShcGRqEwIkoAi35-hOaK6XtL9AJbY6MWBUK_lg05oFFKioaNBxae1RVt6s_mC2BB7DYfLCcvDvpVt2-LiuI6QhmN4LqOXEzsSDeFiqGBs75eG5/?imgmax=800" style="background-image: none; display: inline;" title="clip_image057" width="930" /></a><br />
<br />
Como vemos no hay ningún mecanismo que nos avise de la posible pérdida de información, pero sin embargo Entity Framework no hace “trampas” para sortear posibles errores, lo que es en cierto modo tranquilizador.<br />
<br />
¿Cuáles son los problemas de esta aproximación?<br />
<br />
<ul>
<li>El hecho de tener que actualizar la bbdd a mano puede generar cierta sobrecarga de trabajo en equipos de trabajo que realicen muchas migraciones</li>
<li>Las migraciones deben ejecutarse en orden, por lo que en caso de que tengamos varias sin aplicar, debemos averiguar y seguir el orden correcto, cosa que no sucede con migraciones automáticas</li>
</ul>
<br />
Considero que esta opción <u>es adecuada para proyectos cuyo destino es ser puestos en producción</u>, ya que nos da el control que necesitamos, y nos garantiza que las migraciones se aplicarán cuando nosotros queramos. Por cierto, en producción el método adecuado es la ejecución del script SQL generado por el comando update-database -script<br />
<br />
<h1>
3. <b>Migraciones por código (o explicitas) ejecutadas automáticamente con un inicializador</b></h1>
<b></b><br />
Se trata de migraciones de código explicitas, como en el caso anterior, paro van a ser aplicadas directamente en la base de datos en tiempo de ejecución, cuando EF detecte que hay una migración sin aplicar.<br />
<br />
De nuevo partimos de nuestro ejemplo, lo ejecutamos con lo que la base de datos y los datos de prueba son creados, y finalmente procedemos a activar las migraciones en este tercer modo de funcionamiento. ¿Cómo lo hacemos?<br />
<br />
<ul>
<li>Primero habilitamos las migraciones: Enable-migrations</li>
<li>Después debemos modificar nuestro inicializador, para indicar que las aplique automáticamente</li>
</ul>
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOyloYRfAfBrT_TivwRX07QsauUUBK84hB0dFdmoMsfuh4gC5wPqzoNRYfikY79KZjlC-RzW0_IxbLrQXFGh2Bsih83DiqQ8UZLAEDBebtPxJs7T61TZ6XyXlTj-3IBE4Dvn1X0D3j-iCj/s1600-h/clip_image058%255B4%255D"><img alt="clip_image058" border="0" height="144" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEircP7NaN-Yos6nQRL8Ddetd-YyjRqOpxSLUgYrt6zcAtYJDMdSPhFflGuxObNzWc_B-cVEljPBRyrENRjq08T6mF6FZwbrIAqAlDZZpePUduj1X8ZyrWFt-DKch1IxxCcFULLzG3pl0QrQ/?imgmax=800" style="background-image: none; display: inline;" title="clip_image058" width="846" /></a><u></u><br />
<br />
Un detalle a tener en cuenta es que este inicializador no nos va a permitir rellenar información inicial con el método Seed, como hemos hecho en los dos casos anteriores. Vamos a mover esta creación inicial a la clase Configuration de las migraciones, que también nos permiten esto.<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4KWiEJf5fZKrYOYXt8nBepffzN2EZjJl8-o4YJ-Xzk8vEMnxj3TkK5eVMpEVMTkV3-J7nehb1S-dXjQmKLRp0f2szboxH0PEKIubignCZw6h4uLat1QGMeR_Y_tTP9i4iKCySvkyJeetG/s1600-h/clip_image060%255B4%255D"><img alt="clip_image060" border="0" height="583" src="https://lh3.googleusercontent.com/-G_pze63u_b4/WUbB2FBT3EI/AAAAAAAAA0E/LglKa5hnL-ckS7t0NYJ3yU4CEFxfP4P7gCHMYCw/clip_image060_thumb%255B1%255D?imgmax=800" style="background-image: none; display: inline;" title="clip_image060" width="723" /></a><br />
<br />
Lo siguiente será realizar la <b>primera modificación en nuestro modelo</b>, y crear la migración pertinente. Vamos por tercera vez a incluir una propiedad booleana a la entidad de superhéroes, indicando que admitimos nulos. Seguidamente creamos nuestra migración:<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjm8l4eTe94nb4f90TJCD_slnxPtTZTKK4WMOGE5tTA-N1W8p6FhzxNyidfFW_NlWgRV0SGYm4mDoP46A1dLojQoHJECWiJHrb-hwIN9H7j53nwjmejTmdD6vXeX0u1U4Gt0htBYR7VjpQ-/s1600-h/clip_image062%255B4%255D"><img alt="clip_image062" border="0" height="341" src="https://lh3.googleusercontent.com/-6B-LDXA5K-E/WUbB3Q2MIAI/AAAAAAAAA0M/m4DgvqEMRNQGE9wzW0f4fghftys0txQPACHMYCw/clip_image062_thumb%255B1%255D?imgmax=800" style="background-image: none; display: inline;" title="clip_image062" width="723" /></a><br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijISGamIoNIVIpgPwzyBSowyT4DDwOzF36ztfF0Oq7nTCt-2U1aBJMiNrJhT4yafcb5VhO8zm1p2GrPaaDkd-xKKSefKNsHVs6UdceELdj63xHTsqD-HCY0xWMNmSLy84B3OICTM1P-c7d/s1600-h/clip_image064%255B4%255D"><img alt="clip_image064" border="0" height="115" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbDrevcLiM9fdoFC4ZfanCd21XXN6mZGx-PHOACZUdyIyXJ2PeErQqON3NgxAtWDdTeeYWb-ryPr6h4UDlkqn3HtGV43ESKya5CG8kxdHL23AGb0bVArWVcnhJa65rgUZW5M-AhdkVLYmB/?imgmax=800" style="background-image: none; display: inline;" title="clip_image064" width="723" /></a><br />
<br />
Y sin aplicarla compilamos y ejecutamos nuestra aplicación, que funciona ya que nuestra migración es ejecutada por nosotros.<br />
<br />
Tratando de nuevo de generar una migración que implicase pérdida de información, vamos a hacer que la propiedad que indica si un superhéroe es de DC no admita nulos. Tenemos varias filas en la bbdd con dicha propiedad a NULL así que o bien se borran las filas o bien la migración no puede aplicarse.<br />
<br />
Cambiamos el modelo y generamos migración:<br />
<br />
<a href="https://lh3.googleusercontent.com/-JlyP5X5lZyo/WUbB41pmZsI/AAAAAAAAA0Y/dgaplW6VOIArXVDnWRSVcDvlC4k_yx2QQCHMYCw/s1600-h/clip_image066%255B4%255D"><img alt="clip_image066" border="0" height="346" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEje_2JSPdUA5tMKNJD-6wNlVpJUnKsSCruCcFKyewHJdjP6-qQNy59qxOifPl4V8TJN7yHUTKpl2EFrU9-YdqxsdeColaBfiLMfzjGZbRbA18lO7bKaZVbT2gR43GkmVhRTT-IupJ2L1JAR/?imgmax=800" style="background-image: none; display: inline;" title="clip_image066" width="760" /></a><br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEl5LnHw4qiQVkhXgKRuVAk993eED9ajGeC9qvzChH81LLBTMD7RvRmuLw9Q9V_kjUuKX1WbmBoiPIaDSSinkVKymGgxwOXqPLfFnr9uicZ__6uHgKN1YHPh2EFwNlLRaRPKbMKC6mhKxZ/s1600-h/clip_image068%255B4%255D"><img alt="clip_image068" border="0" height="119" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3KwtRmV7xgPgMon2ERVNOR6RMov-u3KvZOwX4Fu9NAdMcQ2vLwVnVI74AhLOYO8ukOOA7C4y_KXZbbdK__OPPXligNTQgu5CS1PE3SsSl1ljh74vmql2IpY4A5iiLX_2jQwhMvUgsdEMy/?imgmax=800" style="background-image: none; display: inline;" title="clip_image068" width="760" /></a><br />
<br />
Hecho esto podemos ejecutar, a ver q sucede: Excepción, que indica que la columna no admite Nulos. De nuevo la migración no se aplica y afortunadamente no hay perdida de datos<br />
<br />
<a href="https://lh3.googleusercontent.com/-MC9IPoYpbFQ/WUbB7LsFjyI/AAAAAAAAA0o/ks5QXdHo7ewKMjiF_blqOU0K8B-hAA0KgCHMYCw/s1600-h/clip_image070%255B4%255D"><img alt="clip_image070" border="0" height="420" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZFioIeq122sfviM9CeHClNKFcmB3vUK2_8N1jqjlMse9K3L0vh0ui9KEckigA7LjZ5sBnE7CMJjlUb2cnH3Zjdxi2bfqT3HsqlbckLoYTrh0qPflQJ9WAWuzjJu-p5Apc_4WaA7wCssgJ/?imgmax=800" style="background-image: none; display: inline;" title="clip_image070" width="759" /></a><br />
<br />
¿Qué opinión me merece este modelo?<br />
<br />
Creo que <u>aúna las bondades de los dos modelos anteriores, ejecuta automáticamente las migraciones, ahorrándonos trabajo, pero somos nosotros quienes las creamos explícitamente</u>, pudiendo verificar que la migración hace lo que queremos (viendo el código C# generado).<br />
<br />
Sin duda es la opción que recomendaría para la mayoría de los casos.<br />
<br />
<h1>
Resumen:</h1>
<br />
Como hemos visto hay 3 modelos posibles, que recomiendo elegir de acuerdo a las siguientes premisas:<br />
<br />
<ul>
<li>Si estamos trabajando en una PoC, prueba o similar empezaría con <u>migraciones automáticas</u>, que solo cambiaría a manuales (también llamadas por código o explívitas) en caso de necesidad.</li>
<li>Sin embargo, para un proyecto destinado a producción elegiría migraciones explicitas, de entrada ejecutadas automáticamente mediante un inicializador (el caso 3 de este artículo). Tan solo en caso de querer ser muy estrictos con la actualización de la bbdd y querer el 100% del control me iría por la opción 2 (migraciones explicitas ejecutadas manualmente). </li>
</ul>
En cualto al código, aquí podeis encontrarlo:<a href="https://github.com/snavarropino/EF6Migrations">https://github.com/snavarropino/EF6Migrations</a> <br />
<br />
<b>Siguientes pasos</b>:<br />
<br />
Me gustaría escribir más acerca de este tema, ya que hay una cuarta opción que quiero explorar: migraciones automáticas aplicadas automáticamente con un inicializador. No tengo referencias de ella, pero quiero ver que sucede.<br />
<br />
Además quisiera revisar todo esto sobre Entity Framework Core, y finalmente, quisiera escribir en profundidad acerca de los posibles problemas y conflictos que suceden al trabajar en grupo con migraciones.<br />
<br />
Hasta la próxima, espero que esto sea de utilidad.<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />Sergio Navarro Pinohttp://www.blogger.com/profile/15840814836353973005noreply@blogger.com4Madrid, España40.4167754 -3.703790199999957640.0300434 -4.3492371999999575 40.8035074 -3.0583431999999577tag:blogger.com,1999:blog-7984577123741299127.post-49892069990235741432017-06-01T08:42:00.000+02:002017-06-01T10:41:34.675+02:00Tip acerca del code coverage en una VSTS Build<p>Estaba hoy “jugando” con las builds de Visual Studio Team Services, quería ver la cobertura de código que tenía después de ejecutar mis tests… es sencillo, tan solo hay que activar un check en la configuración de la build para que la ejecución capture la cobertura.<br>
Supongo que sabéis que una vez que VSTS os muestra el resultado de la build, incluyendo la cobertura, podemos descargar dicha cobertura para realizar un análisis más en profundidad en nuestro Visual Studio</p><p><br>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3pLweD5hmtu16GlVQ4OX6A10rQ8MBED6hap81Sy_NKuYv82btR4tALWvq4hDPN2mCsPs9GpMcIgdfBKiEdisEJyKfxrhvtE4I3vRfB4z8vkoThsWUGgeZJhztcNwZXMecNJLxrgst_gLC/s1600-h/clip_image002%255B8%255D"><img width="766" height="402" title="clip_image002" style="display: inline; background-image: none;" alt="clip_image002" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgW7ymtleYmHU1u4CmX7Fy5kAd_XijyTfA6g0XgR8lEKpTAXhw9uZJd7vsqUQC6Nh2sr97aMgijxSHcwes6dr62PiUMmd-Y2WzH7Nvw_LRvM33GFg0XwySR9un5gX50kWalmFbK2czD-WzK/?imgmax=800" border="0"></a><br>
</p><p>Abriendo el archivo en Visual Studio me llevé una desagradable “sorpresa:</p><p><br>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDel2Iamu9vnZllVhRQLCxPg3b33QTgWhmomn67zRFyjXOVnVm0bud-St0U5C_hx4_hMhwZrmbmZJzassjyKe9wzqO-nSv52FHGyRX-40ekjeCH8piQtqTjvo0NI1871GE_wMhkEoZy4rb/s1600-h/clip_image003%255B1%255D"><img width="644" height="153" title="clip_image003" style="display: inline; background-image: none;" alt="clip_image003" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxK-I5-9ujdtiXhC0oNJojOwGiTa7O8vOEd1kG5OG9OPNC5gWKgILettAuYOM4Prd0jaux76UkRfNEaca858_sVMY_ihPF4SN0lhCXwGEWFFHOnq_ZdcX2Lk7Myc2P2Z0LQDY5Q2MKwwCi/?imgmax=800" border="0"></a><br>
</p><p>Podéis ver como la cobertura está teniendo en cuenta 3 asemblies que en principio no deberían estar ahí, ya que son dependencias a través de paquetes NuGet. Qué demonios, ¡Yo sólo quiero ver la cobertura de mi código!<br>
</p><p>Lo primero, entender la causa: <strong><font size="3">¿Por qué pasa esto?</font></strong><br>
</p><p>Parece que por defecto VSTS va a tratar de analizar la cobertura de todo assembly para el cual tenga símbolos. Buscando en mi carpeta packages, o en mi bin, efectivamente, los assemblies “malignos” tienen además de su dll un bonito archivo .pdb, es decir símbolos. Parece que VSTS hace bien su trabajo L</p><p><br>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXDfMasRvTpWBuCZvnD9ah_0632btDHVvvE_bT6X4BZcUckUct5_Qf_mzOzwjUXUN1NfFxHmjqAc0SR2fNEcqIY4fMj_IUsxLlPKhAsKb11Cqx0ic2vmNi4w9zBdzkpgU6hN0Nj-Rq6BOy/s1600-h/clip_image005%255B1%255D"><img width="544" height="484" title="clip_image005" style="display: inline; background-image: none;" alt="clip_image005" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXNFrodT6vAPumayCnw6-vZMD9MqWofFq1XgztmCCAN_brCIcdJLIADI1_kEshWEiurSLa3IYtbxVY1EuLAw295EyIqI9G0K1Kr061PPt09lvlvZYYEoLDxzSU6xsXgN6QPTh7n69iv9lv/?imgmax=800" border="0"></a><br>
</p><p>No quiero entrar en la discusión ahora mismo, eso daría para un post entero pero el caso es que hay paquetes que lo hacen, incluyen símbolos, aunque la recomendación parece ser tener dos paquetes, el “normal” sin símbolos, y el que incluye símbolos, por si se necesitan….<br>
</p><p>Aquí vais a encontrar información:<br>
· <a href="https://github.com/elastic/elasticsearch-net/issues/436">https://github.com/elastic/elasticsearch-net/issues/436</a><br>
· <a href="https://docs.microsoft.com/es-es/nuget/create-packages/symbol-packages">https://docs.microsoft.com/es-es/nuget/create-packages/symbol-packages</a><br>
</p><p>En cualquier caso, si no lo remediamos vamos a “ensuciar” nuestros datos de code coverage, ha<br>
<strong></strong></p><p><strong><font size="3">¿Que podemos hacer?</font></strong></p><strong></strong><p><br>
De momento solo he encontrado un modo, mediante el uso de un archivo de tipo .runsettings, en el que podamos excluir ciertos elementos de la cobertura. En este caso vamos a excluir los 3 assemblies:</p><p><br>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRoIUHPsLEc4ebJ_5rCQNoKieCygthiuq1z3gRwIJDj_9x4ItZJZOMl2sLFX-NwQ9lMgvVROJXpgurzMLddjp163yZsh1GP1GAB16OCGtPxSiH_9RQQbt1Zcy3CdLbOBgDFplVPZ0pjQ83/s1600-h/clip_image006%255B4%255D"><img width="769" height="437" title="clip_image006" style="display: inline; background-image: none;" alt="clip_image006" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipi36HSUM6-75171gLK_2v-m_5aEJNSRvt9g39u4Kv3y981UpUiL9HbyAR1gVAHji1NhTMsmRuYBRBJcE2gnOOQ9067PgHHyoQrrf_bozuo9O3Bpl6nFjgcldZSq2DC1NxNY8jBAy5PoY6/?imgmax=800" border="0"></a><br>
</p><p>Podéis ver <a href="https://docs.microsoft.com/es-es/visualstudio/test/customizing-code-coverage-analysis">aquí</a> documentación oficial, hay opciones para incluir y excluir assemblies, funciones y atributos<br>
</p><p>¿Cómo lo uso en visual studio? Tan solo debo seleccionar el archivo, en la siguiente imagen indico como se hace. A partir de ese momento el archivo “aplicará” siempre que lo tengamos marcado, como se ve en la imagen anterior</p><p><br>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgdguV17SXHUNv94AnqiU_6oWxBU_4BNyqfewkeHImtgIPZTe7QGeW34NmaGrMxSsm3p7VkpCJejQz_MFr3KyI6FGTC_QtJECQjj0zaYPSDs91ohAGiaCRo0Vr36JtFOxibtECwNNnHlRiC/s1600-h/clip_image007%255B2%255D"><img width="644" height="229" title="clip_image007" style="display: inline; background-image: none;" alt="clip_image007" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhaVAK4mTxhL7-yFaG8JiIalxyASSyhUprJAPXtt0eIKUJI_Fg-nNvzecuafjvNupYsDpSSKXkejy6xw1v2x8Is7ufti0Wb0EsbeVYqLMIfLMbzBIhoWn7HjQNvmHf7SjWzNtTweuv-5-II/?imgmax=800" border="0"></a><br>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTMq0mKoYq661hV9DIZSvb1u3rtOojiNcNaAj_vP85QZaJXlWa7p3VUy-2DHtxE1kf-CImZVhWxB7gbeozaDJrdYQXeKyM5RBtjaP5GL6KlhDIKENGk_r5hAenmo2PyplWfCg1hCzGPri3/s1600-h/clip_image008%255B4%255D"><img width="898" height="167" title="clip_image008" style="display: inline; background-image: none;" alt="clip_image008" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiu6uGXV8kTh3pfiq-S8ZgF7MDQIbDTLB1NyGhlz1vW9dbYteylOcgOjXBnZSbfb3AcsdFFWl7ojz3hfjsSK_s5e7VXaUEPe6R0wzOF3LCobNi8j3ap6gWuSFlpo3NJOiAA05-ZayRtonnc/?imgmax=800" border="0"></a><br>
</p><p>Parece que está funcionando, ya no vemos assemblies externos, pero vemos el assembly de tests, y este tampoco lo queremos. Aquí debemos jugar con los atributos, hay que modificar nuestro fichero para que también excluya el atributo [ExcludeFromCodeCoverage] con el que decoramos nuestras clases de tests (<a href="https://msdn.microsoft.com/en-us/library/system.diagnostics.codeanalysis.excludefromcodecoverageattribute(v=vs.110).aspx)">https://msdn.microsoft.com/en-us/library/system.diagnostics.codeanalysis.excludefromcodecoverageattribute(v=vs.110).aspx)</a></p><p><br>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_Krk22bcT6ZRNWo5eb3GjxnwR5Io8uU7Vc0VWY7l0XXgQKgBYxljBegXaDe7wg3cZXoTByVqErJSIZC5qI0yEe7yaPODBJob8Yz2af7L1v0-KH0JSc3Di_q2nhPx5ULRexAokkFM4zn2J/s1600-h/clip_image009%255B4%255D"><img width="787" height="544" title="clip_image009" style="display: inline; background-image: none;" alt="clip_image009" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAgchSuOR0GA1teER_plcmk_YdVtD2ieIiTiliDt1xIUocxvdEkNFZXMMeXuEEdiQ27OyXzGbpquGAWcHZj2PDdAsFfz2sfOlH_J-aOCx4OJzwd6xXEnC0d7Nqno6CpusVUFKyRKsCOHJa/?imgmax=800" border="0"></a><br>
</p><p>Mis clases de tests son así:</p><p><br>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbatEyDajSOX0UycXVMz4kFWTI9qNgGvVUXMORP7SCTJvaomLJYxiF65BDLIVkpzE8dVErGOsVrlpHwPqmK547excCLFYB0uoGpzgZOe6BY2yBYqEcxED_wOOVivcWgTDgIHKSOqwHwr8X/s1600-h/clip_image010%255B4%255D"><img width="547" height="200" title="clip_image010" style="display: inline; background-image: none;" alt="clip_image010" src="https://lh3.googleusercontent.com/-efqIzeKtMxY/WS-1BI_Z4nI/AAAAAAAAApI/-s6cv1PbTNA8TkVHtKNXqG4Jr-ttacW6wCHM/clip_image010_thumb%255B1%255D?imgmax=800" border="0"></a><br>
</p><p>Ya tenemos todo lo necesario entonces, ¿cómo lo llevamos a VSTS? Pues bien, debemos subir el fichero a nuestro control de código fuente de modo que VSTS lo pueda encontrar. Además, hay que indicar en la build, en el paso de ejecución de tests, que utilice dicho fichero</p><p><br>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPrr8nC9DOff-j14P87LwFLJrA9gPJp6IB8bJhO37ypBCjKs2uz8Go0inrgIFyMf__857ytWzplIqaUL9XHsLjffW5ZGfuM61jtW-3FLq6vxiku9paux05kfmGJqPpfJLciIIJruWQgtp_/s1600-h/clip_image012%255B1%255D"><img width="644" height="305" title="clip_image012" style="display: inline; background-image: none;" alt="clip_image012" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPZujy8nvZt43ZGKNcm3ERz6ro03jCes3c98nqDuyV19K1_ImZWqhizRDovqA0E5vlAZyU2ETyA57-wgfGKYSfBXuN4DrJpEhBal45altqBbtbBuUdx6nLNcqOhCh39b91hY0ulGw5kxKP/?imgmax=800" border="0"></a><br>
</p><p>Una vez hecho esto podemos lanzar una nueva build y ver que la cobertura de código ya solo incluye lo que queremos.</p><p><br>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMJpfvxW-t_wPH7rOt0lF-m1jxditp60xoqdUQXwPpurGAS74GB6ZhnA6q5uXZtGjUdZL9fd3neUh9QGCAh69NShqWYvwi2dg0uceFNqwb7PPbeB1PH1_F-jy-7pJsr3RTEjQKVtq5JKOf/s1600-h/clip_image013%255B1%255D"><img width="644" height="400" title="clip_image013" style="display: inline; background-image: none;" alt="clip_image013" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnZDmNs1OoCzhbrUUHjxIiJrdJElbP5l0ymcQ8LojBjdbhkFVHckj37T1XoXAFKhnaW0lopbN7kInddKkKcW_fwxSYMF_dUvwxXF5Q_mIIJwkTAlw44h0AJ8HtaxYHcDTioHx-Wxl8hb59/?imgmax=800" border="0"></a><br>
</p><p>Objetivo cumplido, <a href="https://1drv.ms/u/s!AjQc6plnnx0zo81FxFruwbUJ7vDYRw">aquí</a> podéis descargar mi fichero .runsettings</p><p><br>
Espero que os sea de utilidad.</p>Sergio Navarro Pinohttp://www.blogger.com/profile/15840814836353973005noreply@blogger.com0tag:blogger.com,1999:blog-7984577123741299127.post-89430999365035303022016-11-11T20:10:00.000+01:002016-11-11T20:10:29.044+01:00Off topic: conservando el espacio en discoAcabo de reinstalar mi equipo de casa, compre un nuevo y flamante SSD (<a href="https://www.amazon.es/gp/product/B01B4NUKMY/ref=oh_aui_detailpage_o07_s00?ie=UTF8&psc=1">detalle</a>) y ahora tengo una configuración con 2 discos:<br />
<ul>
<li>SSD de 240 GB para el sistema </li>
<li>Disco tradicional 1 TB para datos </li>
</ul>
No se a vosotros… pero a mi normalmente (tengo 2 equipos más con este tipo de configuración) me dura menos el espacio en C que las cervezas en la nevera, así que he decidido ponerme manos la obra: ¿Como conservar y liberar el espacio en disco (Sobre todo de la unidad de sistema)?<br />
<h2>
</h2>
<h3>
¿Alguna vez hibernas tu equipo?</h3>
Si la respuesta es NO mejor que sepas que estás perdiendo algunos GB<br />Para verlos cambia la configuración por defecto de explorador de archivos para poder ver los archivos protegidos:<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjopq4HokOTGOUDIgDQ1hko66rgHHoVrbEuq7mnDKkFbB9PnpQ7tIB9aOyuszvo-RLU3myZ3hc6ilRlvBlpB3gi5eS6lp-_EDJ6TiwGvxyEssxRE9Sh3hAnwCRNI1_VkmsHvjDaLd_GF9dT/s1600-h/image%25255B3%25255D.png"><img alt="image" border="0" height="484" src="https://lh3.googleusercontent.com/-Dtw323cF7LY/WB9gLCy9QFI/AAAAAAAAAkc/sgn7yhlopdg/image_thumb%25255B1%25255D.png?imgmax=800" style="background-image: none; border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="394" /></a><br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj10fJdjSOCJ12skjFCKWTI3HANj1wF-clbzgLbIf5KEAoPal2MKS0LU5SGkiOV_WpfbpHEqCcNgIzEE1BAWKYxMM3p-KWmR7EvIR-ukedhm2bT2iV87UyaZtvtjUippSt-YF_8UwXagB7a/s1600-h/image%25255B7%25255D.png"><img alt="image" border="0" height="471" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi47f70N0jM_m9Ms8ucHmGPwhFyYosT_mu2PfYQEprwmqsul0wx6K4LkE0Dnn8vvZ5a4zJdDElHbWYXdUD6kLfSQlN-ZIi5A_4bfBFQvXD18J4nDYwmdUVMOgwS1GtjtC3Vz_MZdM-tOBDt/?imgmax=800" style="background-image: none; border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="644" /></a><br />
<br />
Veras un archivo hiberfil.sys, comiéndose algo de espacio (más de 3 GB en mi caso). Desactiva la hibernación ejecutando el siguiente comando en una ventana de comandos con privilegios elevados:<br />
<strong> powercfg.exe -h off</strong><br />
Después de reiniciar el fichero habrá desaparecido<br />
<h2>
</h2>
<h3>
¿Los archivos temporales ocupan demasiado? </h3>
Pues podemos sacarlos de C y moverlos al disco de datos. Para ellos vamos a Control Panel => System & Security => System => Advanced System Settings, y nos vamos a la sección de variables de entorno. Allí encontraremos 2 variables de sistema, que podemos modificar para nuestro fin<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhiAynH5acTqMjrAx_3O-cZ5wMxscl5kKlvqIB-py5WzjIcyUiOQotQMh1yn-mQ9gAh22dBkNWAd4ttFF91iv3hEHZM8E8Mjn122K-cPhCGirUNumrsrYtJ75lRo-u4bFOATSRe8yql69um/s1600-h/image%25255B4%25255D.png"><img alt="image" border="0" height="484" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHu_uUtjg1oDPWSxRV4FULUNpgPzy4v8VQvHJKzfHYI6E4-fIQR3EF9Uh76j-CgcAy_45I2_Irhgn8NOLqW44H-ltF49fdr500VNFn0iFvorxGDzRh_au0VkI0aI7FVv40HazAebjeu2tC/?imgmax=800" style="background-image: none; border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="511" /></a><br />Yo las he movido a mi disco tradicional, que está en D.<h2>
</h2>
<h3>
¿Demasiada información de recuperación? </h3>
Los puntos de restauración pueden ahorrarte más de un disgusto, pero tambien pueden estar consumiendoi demasiado. Puedes llegar a de nuevo ellos desde Control Panel => System & Security => System => Advanced System Settings<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3e8pSHI-thITcCbL5Q2-iG01JIkUDpBVwdRj8TWbis6ooLjSn9Kt2sdVDi7u85_1DqWlaRA2ZTJP80HsI-0zEYlYVPoOP_IkNuSMDuE4hpPAhoguTaaCN73-mLjM8VnNjjo8q7Y-W-cxS/s1600-h/image4%25255B1%25255D.png"><img alt="image" border="0" height="484" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiP9Fm_cpEdCWn2DjlXLL-zxYmdWlylm0XACb9QyHJ5wykBGEmy4qBf0MeKzdWPfA-fwRCr11N84o1xwVFsNl8V9Tvd7rwg8vjlZl8FHtknKpdRB2sIhW8xZJD0ltN_I5fUR9Z_FUzHnU1a/?imgmax=800" style="background-image: none; border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="431" /></a><br />Mi consejo: deja el ultimo y cárgate los demás. Además asegúrate de limitar correctamente el máximo tamaño que permites ocupar. Como veis yo lo he limitado al mínimo: 1% de mi disco<br />
<br /><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7jDIHx9IVU-kara1ZlmcFnaYpYii6zNthGUFIYg7WOcQGU1DAiHvGvwNuqdlz_1ZFRwK1tfWKKhnUXStzBpUc_du6rrPbQ_b9olef6Kt8JsHLrB9As_BpQkdJzvQWMEn_H6-M-C4Au4eX/s1600-h/image10%25255B1%25255D.png"><img alt="image" border="0" height="484" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirQYIpWezeTTXqRmU98NpyKXOhhIVUSYHcyQOFFTpoE_ZEKIw9KhLmjNn0PnrEwF82YyH6GP3elQrNNKq2ryq67uVJweCMvgERU90SQNpuVZxzn0DqoBkVkVwVCzIhhHg20xilgPqbM4Kf/?imgmax=800" style="background-image: none; border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="438" /></a><br />
<h2>
</h2>
<h3>
¿Sabías que Windows trae una herramienta para limpiar discos?</h3>
Se puede llegar a ella de varios modos, el que yo uso es a través de las propiedades de un disco:<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSFBvFjMmZuajdGaDzxAX10wbg5ZD1vDWyaYQ__F4xwA6B23EKoanQKEQTymDkWQ-kNE-4RNElztF-t3PsaUs6uXst7Nglgo0PM0f9jiWJHimnz9s2WiEp_xmAbaA8s-sKFVvdSW4brSnI/s1600-h/image%25255B6%25255D.png"><img alt="image" border="0" height="484" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCJ3RlatftgFMd1bOspJ_Dq8bxc__BU42A4DeLbDOWUjIWNRBkK1t1CclstTgw9gAdFgNaMLxovd4-sHM5oKEgkbpyK7Tl58Ur4u0ffHdYV-UP-A7LWoL1ibAjLE07Sw3C72KSNhSUmIA6/?imgmax=800" style="background-image: none; border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="366" /></a><br />Una vez en la herramienta, analizará vuestro disco y propondrá elementos a eliminar. Como veis nhay un bug raro por ahi… mirad lo que dice que puede borrar: 3.99TB!<br />
<br /><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1hZrG07a5wreew24Pxi7StZoZnz4Zkw2KIcFdS3Kf8sQEvu3zmk2G9AF8TwAcHtERF9hPEsKMGsVTfONXCdORIYLACHTNOymeLDg5ymx0xTzLnCqfYFVBlztQr0Gz4mFs887H3LPdktZV/s1600-h/image%25255B7%25255D.png"><img alt="image" border="0" height="484" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgObl6TK6f5VJISx3vnA0VZ3pAInKzvdEj580uSjhV1pgHnBHNnr8oa609oY8chXty4tSTiUbznS5alXG-V2R6PAtE9eHSb6xstGrbaZyQDO5cnw57ko1oqVAXQLhDcoawJpq38mMCXOJk5/?imgmax=800" style="background-image: none; border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="401" /></a><br />Si seleccionamos lo que queremos borrar y pulsamos Aceptar Windows se encargara de borrar lo indicado y recuperaremos algo de espacio. Mi experiencia es que esta herramienta es fiable, no tengais miedo.<br />
<h2>
</h2>
<h3>
¿Tienes muchas cosas en OneDrive?</h3>
Por defecto Onedrive sincronizará vuestros datos en una carpeta en C… y no queremos eso. Yo tengo como 60GB de información en Onedrive y claro,m quiero sacarlos de ahí. Lo podemos hacer de un modo muy sencillo al instalar Windows 10, pero si no es el caso aun podemos hacerlo: id a las propiedades de Onedrive (botón derecho sobre el icono) y después desvinculad. Al volver a configurar la cuenta de Onedrive podréis elegir la nueva ubicación. Eso si, espero que tengáis una buena conexión, porque volverán a descargarse todos los archivos<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMNn8s8DN8j7zp3osOnS1PRJnS3JXAZwKfz8VeeByQmSWMecKopiVB2db_rEZzG90NBWh_fY9ra2lyGC866n4B47c8GZEMRbx2iyf9SVRKxhzEJjRDw899UIS9a3vdmxabhe2kuGCZ27vx/s1600-h/image%25255B11%25255D.png"><img alt="image" border="0" height="394" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkE3AClWgPKMgFy2S1V3gMryWruH8-WGA_ozGyE_dvWULHNJ3wvPWXn2EwOzIR4q_QaW2wg3RNYACJfl-MaVxsAd-mLliH6dYZknoLqmKQztCW_vdlGejnB031JHMytNyY_oPuuP4LOocs/?imgmax=800" style="background-image: none; border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="644" /></a><h2>
</h2>
<h3>
Haz que Windows instale aplicaciones y almacene cierto elementos donde quieras</h3>
Si escribes “Almacenamiento” en la caja de búsqueda llegarás a una opción de configuración que te permite determinar en que unidad windows maneja ciertos elementos, como las aplicaciones de la tienda que instales, las fotos y otros. <br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWZmFT86RBbUH4SIL5Pp6twZflurG89d-_9RIi3Yqkk__07LziyJ1qvd5VLTUvsqXn7Q3QCNLOkPpa1MixZJDA319udC5gKPq7Gq1HcPkTxQRCWlvN9-km3TCrJ_4O69M6vCJsU4a0a0FJ/s1600-h/image%25255B19%25255D.png"><img alt="image" border="0" height="772" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0Dk_QsCtPtO3cbGlNb7bs4UEMC95QG2WeoWu5QpBKnfNvs9nBm2mEfw2qzrIEKXeIE7s7TSRMhGgCbj3uC6oxa2ifHQDZuL80fPXZhBa6qEGXadtd6iKmgJV0LtrtxTxINxQndF7_3ES5/?imgmax=800" style="background-image: none; border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="406" /></a><br /><br /><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiP3yktaXxngn163QeRPTe-hCl1ArqUvKJjiz6_aYrMhj-fhegC9I7RKYRX7GwSvlEz2yr_OW2bkBgLgl0pp9i7POZVKtsnSl5jgxLIXoMjvA2WsG_Y0bH306OV_eQ7GEBWrSRQIFYEPbMC/s1600-h/image%25255B20%25255D.png"><img alt="image" border="0" height="772" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUKVpnrBCjQKz5s8QEK3KX7zUyNkzQDRFawfO7v0PWolY5Yo-YOQSHvyHwEKchVzQjxcTsxNSKafGpMAdo0haqRA2ex_W4Pi_ezb-KhWq7QlbjMEN7LOYgEn7beDI0iZuOkknHcdHh4qF9/?imgmax=800" style="background-image: none; border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="1028" /></a><br /><br />Hasta aquí, no suele ser la temática de este blog, peeero, a mi me ha venido bien y quizás a alguien tbSergio Navarro Pinohttp://www.blogger.com/profile/15840814836353973005noreply@blogger.com2Valdebebas, 28055 Madrid, España40.4820657 -3.61610710000002216.0085087 -44.924701100000021 64.955622699999992 37.692486899999977tag:blogger.com,1999:blog-7984577123741299127.post-70288874467289691542016-06-14T12:32:00.000+02:002016-06-14T12:33:54.474+02:00Mis performance tips… (son realmente míos?) Parte 1. Mide<p> </p> <p>Hola a todos, el miércoles 25 de Mayo estuve en una sesión de @mscoders madrid, donde Luis Ruiz nos dio algunos consejos útiles relacionados con el rendimiento de nuestros desarrollos. Vamos a ver alguno de ellos… y algunos mas de mi cosecha.</p> <h3>Midiendo que es gerundio:</h3> <p>Lo primero cuando hablamos de performance es medir, o mas bien ser capaz de medir las mejoras que vamos implementando. Tenemos para ello una herramienta sencilla y muy poderosa: <a href="https://github.com/PerfDotNet/BenchmarkDotNet" target="_blank">BenchMarkDotNet</a>, que se va a encargar de ejecutar nuestro código varias veces, analizar su rendimiento y otros factores, y presentarnos los resultados en diversos formatos. Vamos a dedicar este post a usarlo, utilizando un ejemplo código que enlaza con uno de los primeros post de este blog, que comparaba distintos métodos para clonar objetos: <a title="Clone Wars (o el arte de clonar objetos). Parte 1" href="https://serginet.blogspot.com.es/2012/12/clone-wars-o-el-arte-de-clonar-objetos.html" target="_blank">Clone Wars (o el arte de clonar objetos). Parte 1</a></p> <p>Compararemos el rendimiento al clonar un objetos pesado, utilizando dos metodos diferentes que explicaba en mi post: Mediante serialización y mediante reflexión.</p> <h3>BecnMarkDotNet, empezando</h3> <p>El readme que vais a encontrar en GitHub explica todo bastante bien, por lo que ahora me voy a centrar en lo mínimo que necesito para echar a andar el ejemplo. Más delante en esta serie veremos aspectos mas avanzados y como resolver algún problemilla que he encontrado por el camino</p> <p>Una vez que dispones del código que quieres testear los pasos son los siguientes:</p> <ul> <li>Añade el paquete via Nuget</li></ul> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgaMGRppL_A1vlOyiwS519POfPzldI5JRsFNW76uFfzB16dsmbFDDH6v4af_188GFmJrZyMpUQUhFBiLeSfH8ByTBTj1gfr8b-71ZVDBcYOvuW3wXQepEfVK3HPp390B6Sa009Zvypk4Ylm/s1600-h/image%25255B4%25255D.png"><img title="image" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="image" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEho27B2l2cCtTntiVRqsz3HjE1h1zQ1ylWWPs5-YscYsvPJlZ1aOA5KjeJBPTPzxKpOe8DW3kGmzyOTZPS1hU3G7iz-CLCx7S4TeHpKit7Fjctbr72FHqypuCy3nV4rfl5ew0CwUGPj2BBw/?imgmax=800" width="340" height="62"></a></p> <ul> <li>Crea una clase “tester”</li></ul> <p>En esta clase debemos decorar los métodos que se van a medir con el atributo Benchmark. En mi caso, si quiero comparar dos modos de clonar un objeto, voy a crear dos métodos, cada uno se encargará de realizar el clonado de una de las maneras:</p><pre class="csharpcode">[Benchmark]
<span class="kwrd">public</span> <span class="kwrd">void</span> CloneByReflection()
{
<span class="kwrd">var</span> cloner = <span class="kwrd">new</span> ReflectionCloner();
<span class="kwrd">var</span> garage = CreateGarage(numberOfVehicles: 10);
<span class="kwrd">var</span> res = cloner.Clone<Garage>(garage);
}
[Benchmark]
<span class="kwrd">public</span> <span class="kwrd">void</span> CloneBySerialization()
{
<span class="kwrd">var</span> cloner = <span class="kwrd">new</span> SerializationCloner();
<span class="kwrd">var</span> garage = CreateGarage(numberOfVehicles: 10);
<span class="kwrd">var</span> res = cloner.Clone<Garage>(garage);
}
</pre>
<p> </p>
<ul>
<li>Invoca tu test</li></ul>
<p>Es sencillo, solo hay que utilizar el BencharkRunner que nos proporciona el framework </p><pre class="csharpcode"><span class="kwrd">class</span> Program
{
<span class="kwrd">static</span> <span class="kwrd">void</span> Main(<span class="kwrd">string</span>[] args)
{
<span class="kwrd">var</span> summary = BenchmarkRunner.Run<CloneTester>();
}
}</pre>
<ul>
<li>Analiza los resultados</li></ul>
<p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHRNR0e4PyObOsnfKsyiDiE4IgJrCfsSEva0EsWyIOoXnrclVFEgEBs3ZAm1OoDDiXuQctEbEZ9zkuAm_iRVQnDTSZGZaC91_MoAkoXgJYLfwsthOJIUGhwSQ4QZLq6uoeuvFmI6xk9Jp2/s1600-h/image%25255B8%25255D.png"><img title="image" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="image" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhepwZXPCFfCwkSQt-tk4Iir0jExlK6NUd4wCzJnCnAyZiKBcx5IQmJujFlq2yhmwFslrWS-syWlEfBdpEJCBmy1-g092VPpd7t2YlvTnP7qDh0_8WxQs6197EnOd159C_z127hl463NNOS/?imgmax=800" width="579" height="349"></a></p>
<p>En este caso vemos lo que esperábamosy ya vimos allá por finales de 2012: la clonación por serialización es más rápida, no mucho pero más rápida que el método basado en reflection.</p>
<p>¿Como lo hace BenchMarkDotNet? Realiza varias ejecuciones de los métodos marcados, para finalmente calcular medias y desviaciones, y presentarnos los resultados. Todo esto se puedo controlar y configurar, vamos a empezar con ello.</p>
<h3>Parámetros</h3>
<p>Me pregunto, ¿obtendré el mismo resultado en base al objeto clonado (que sea más grande o más pequeño)? </p>
<p>Que pasará si realizo la misma operación de clonado varias veces seguidas, ¿seguirá siendo el clonado por serialización más rápido?</p>
<p>Esto lo podemos manejar con los parámetros que BenchMarkDotNet nos ofrece. Voy a añadir 2 propiedades a mi código, una para indicar el tamaño del objeto a clonar (número de vehículos del garaje) y otro para controlar en numero de clonaciones que voy a ejecutar.</p>
<p>Decorándolas con el atributo Params puedo especificarle distintos valores que BenchMarkDotNet usará como combinaciones para los tests… ¿fácil verdad? </p><pre class="csharpcode">[Params(100, 200, 2000)]
<span class="kwrd">public</span> <span class="kwrd">int</span> NumTimes { <span class="kwrd">get</span>; <span class="kwrd">set</span>; }
[Params(2, 5, 15)]
<span class="kwrd">public</span> <span class="kwrd">int</span> NumVehicles { <span class="kwrd">get</span>; <span class="kwrd">set</span>; }
[Benchmark]
<span class="kwrd">public</span> <span class="kwrd">void</span> CloneByReflection()
{
<span class="kwrd">var</span> cloner = <span class="kwrd">new</span> ReflectionCloner();
<span class="kwrd">var</span> garage = CreateGarage(numberOfVehicles: NumVehicles);
<span class="kwrd">for</span> (<span class="kwrd">var</span> i = 0; i < NumTimes; i++)
{
<span class="kwrd">var</span> res = cloner.Clone<Garage>(garage);
}
}
[Benchmark]
<span class="kwrd">public</span> <span class="kwrd">void</span> CloneBySerialization()
{
<span class="kwrd">var</span> cloner = <span class="kwrd">new</span> SerializationCloner();
<span class="kwrd">var</span> garage = CreateGarage(numberOfVehicles: NumVehicles);
<span class="kwrd">for</span> (<span class="kwrd">var</span> i = 0; i < NumTimes; i++)
{
<span class="kwrd">var</span> res = cloner.Clone<Garage>(garage);
}
}</pre>
<p>¿Que hemos conseguido? Que BenchMarkDotNet se encarga de ejecutar todas las combinaciones posibles, es decir:</p>
<ul>
<li>100 clonados de un garaje con 2 vehículos</li>
<li>100 clonados de un garaje con 5 vehículos</li>
<li>100 clonados de un garaje con 15 vehículos</li>
<li>200 clonados de un garaje con 2 vehículos</li>
<li>200 clonados de un garaje con 5 vehículos</li>
<li>200 clonados de un garaje con 15 vehículos</li>
<li>2000 clonados de un garaje con 2 vehículos</li>
<li>2000 clonados de un garaje con 5 vehículos</li>
<li>2000 clonados de un garaje con 15 vehículos</li></ul>
<p>Aquí tenéis el resultado, como veis las cosas han cambiado. El método de clonado por serialización no siempre gana… si el objeto a clonar es pequeño la reflexión puede ser una opción (ojo, si solo tenemos en cuenta una variable: el tiempo)</p>
<p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdwAYkFgfs8uo45S-mv0jZ8X6xKG7sNspmTp7cBfY7EUrMLD796bnAf2VkWFAGZQyQwHn-linZz_yveUBM7qfHKso05A3xTvY7V66ZrjPvkP8UbX-nWZiZkPnHS22jdmUzslr3tOPPjfRR/s1600-h/image%25255B18%25255D.png"><img title="image" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="image" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8Jxu61kLKmDl8qHKn4JTRbMvusZSZq1_gBd8L0jfdQwpxNNW2ABmoLxTKTyjaW-xEitJKwznPFDAxbRI2dDpB7mDR-FXbzYo2sIJ64XaYaGZg0uE61IITMtafw2jPRLs3_qVDLHd1CCB7/?imgmax=800" width="642" height="416"></a></p>
<p> </p>
<p>Podéis encontrar en código en github: <a href="https://github.com/snavarropino/PerformanceTips" target="_blank">https://github.com/snavarropino/PerformanceTips</a></p>
<p> </p>
<p>Continuará …</p>Sergio Navarro Pinohttp://www.blogger.com/profile/15840814836353973005noreply@blogger.com0tag:blogger.com,1999:blog-7984577123741299127.post-70599002503174593382016-05-11T11:46:00.001+02:002016-05-11T13:33:20.943+02:00Async & await… otra explicación<br />
Ha pasado algún tiempo desde que los bonitos palabros aparecieron ( ¿fue en Visual Studio 2012? ), yo pensaba que todo desarrollador .NET que se preciase de serlo los tenía claro, pero el otro día una conversación en el trabajo me hizo ver la cruda realidad. Vamos a ver si mi granito de arena ayuda:<br />
<br />
<strong>Async</strong>: indica que un método puede ejecutarse en modo asíncrono, y ojo, digo puede, porque que lo haga o no dependerá de la presencia del otro palabro.<br />
Un método marcado como async debe obligatoriamente devolver un Task o bien nada (void)<br />
<br />
Ojo! Debemos evitar funciones async que no devuelvan nada (void). Existen para soportar event handlers asíncronos... en otros escenarios evitémoslo. Mas detalles aquí: <a href="https://channel9.msdn.com/Blogs/channel9spain/Async-best-practices-por-Llus-Franco" target="_blank">https://channel9.msdn.com/Blogs/channel9spain/Async-best-practices-por-Llus-Franco</a><br />
<strong><br /></strong>
<strong>Await</strong>: Cuando dentro de un método asíncrono llamamos a await estamos indicando dos cosas:<br />
<ul>
<li>Queremos esperar por el resultado de la ejecución de un task
<li>Mientras estamos esperando devolvemos el control a nuestro llamante </li>
</li>
</ul>
<br />
Mirad el siguiente ejemplo, es sencillo:<br />
<!-- code formatted by http://manoli.net/csharpformat/ --> <style type="text/css">
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #a31515; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
</style> <br />
<pre class="csharpcode"><span class="kwrd">class</span> Program
{
<span class="kwrd">static</span> <span class="kwrd">void</span> Main(<span class="kwrd">string</span>[] args)
{
FooAsync();
Console.WriteLine(<span class="str">"Main continues execution..."</span>);
Console.ReadLine();
}
<span class="kwrd">public</span> <span class="kwrd">static</span> async <span class="kwrd">void</span> FooAsync()
{
Console.WriteLine(<span class="str">"Foo is going to call ExecuteLongRunningTask..."</span>);
Task<<span class="kwrd">string</span>> t = ExecuteLongRunningTask();
Console.WriteLine(<span class="str">"Foo continues execution..."</span>);
Thread.Sleep(1000);
Console.WriteLine(<span class="str">"Foo is going to wait for ExecuteLongRunningTask response..."</span>);
<span class="kwrd">var</span> s = await t;
Console.WriteLine(<span class="str">"Foo got the response: {0}"</span>, s);
}
<span class="kwrd">public</span> <span class="kwrd">static</span> Task<<span class="kwrd">string</span>> ExecuteLongRunningTask()
{
<span class="kwrd">return</span> (Task.Factory.StartNew(() =>
{
Thread.Sleep(5000);
<span class="kwrd">return</span> <span class="str">"Hi all!!"</span>;
}));
}
}</pre>
<br />
<br />
En cristiano:<br />
<br />
<ul>
<li>Main llama a FooAsync
<li>FooAsync llama a un metodo que genera una tarea que tardará en ejecutarse (5 segundos), De momento main sigue bloqueado
<li>Cuando FooAsync llame a await empezará la magia: main continuará su ejecución mientras que FooAsync estará bloqueado hasta que la tarea finaliza su ejecución </li>
</li>
</li>
</ul>
<br />
Si lo ejecuto tendré esto:<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXh3AkOjjHfixXtw1G9_7tSgKRb2NpooJvwdiHUF2FFmOJTdLZ0B7vpe3TREWQ1TJX140cLHoDmGr8wOuxEun42Bg6eoOCgjKgZ9mO22Zk9zCv5yxAFZ_P95RYNamjC0WmaJZC6EqUj3OR/s1600-h/Untitled2%25255B7%25255D.png"><img alt="Untitled2" border="0" height="177" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnh7Fu8MB46y3Y7zv-xdlK2XkQOjjophFNh5qCgfoufQ5fLX5oninsR-U3eGN2uaa6ryGkWbCZMDmHdaqGDztlRG5823LSeuJB2t29G261H9ifEWo6O5yUoooRQSzLtYyJsyIDUENDh9tF/?imgmax=800" style="background-image: none; border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="Untitled2" width="663" /></a><br />
<br />
Dicen que una imagen vale mas que mil palabras:<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZMDMWoxpv7u4m9aEDNsNSRBRNaohG-HkCXO9DHmx5yIqQIKKBEnWp9jH8JrqJLMEsP4HwkbK3MaBsiFcOIY8yC6E31KDdJ57xiCttQO_SNKBNkz2ijWApksTT0FVnlUrl0G8mKaE33Os8/s1600-h/Untitled%25255B4%25255D.png"><img alt="Untitled" border="0" height="405" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6ZVYawootc3MKiHZVOjUHoWc6fACXsUiteK1gBL_LfRs35mXSmZkTmiiXVPCva2rtlgLeg5LLnTrj2noO2Z50RApDiMj177U46p-zzzTeUp-YtSWeC0KIZh1j9aPkbHnnN9DH3E4GyVNx/?imgmax=800" style="background-image: none; border-bottom: 0px; border-left: 0px; border-right: 0px; border-top: 0px; display: block; float: none; margin-left: auto; margin-right: auto; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="Untitled" width="911" /></a><br />
<br />
En resumen, hemos conseguido que <u>el método FooAsync se ejecute en asíncrono</u>, pero lo mejor es que es <u>el propio método asíncrono el que decide que parte se ejecuta en asíncrono y que parte no</u>.<br />
Espero que sirva de ayuda…
<br />
<br />
Bonus: el código lo podeis encontrar en github: <a href="https://github.com/snavarropino/CSharp-Samples" target="_blank">https://github.com/snavarropino/CSharp-Samples</a>Sergio Navarro Pinohttp://www.blogger.com/profile/15840814836353973005noreply@blogger.com0tag:blogger.com,1999:blog-7984577123741299127.post-21476316356584202982016-02-06T17:05:00.002+01:002016-02-06T17:06:45.654+01:00¿Problemas de sincronización en Microsoft Edge?<div align="justify">
Normalmente no publico tips o consejos sobre sistemas operativos y dispositivos. Sin embargo hacía tienpo que un problema es estaba molestando bastante y ya que con lai nestimable ayuda de Internet lo solucione quiero compartir dicha solución.</div>
<div align="justify">
Vereis: En el ámbito personal uso principalmente dos equipos: Una surface pro 3 y un ordenador de sobremesa, ambos con Windows 10. Todo funcionaba con normalidad hasta que hace algunas semanas empece a notar que los favoritos y la lista de lectura de Edge (si, uso Edge, llamadme raro) no estaban sincronizados. Si lo habían estado en el pasado, pero esa relación parecía haber terminado <img alt="Broken heart" class="wlEmoticon wlEmoticon-brokenheart" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj49ID7vC_ru83HdgsZJX0HVRFBP3f0izYZLxqpz-DmbUJ9WOLsykvUdC2Xp7NwGycrSwk6v0v6xHJvzakZG3bcqEZUbTa56a6hucSCOyxjLGHp9IY_NMdJ1Yohf1T6psXbbfVn-k_fFCr7/?imgmax=800" style="border-bottom-style: none; border-left-style: none; border-right-style: none; border-top-style: none;" /></div>
<div align="justify">
<br />
Me dispuse a ver si algún setting había cambiado en Edge, y al ir a la configuración vi que la opción de sincronización estaba desactivada, y además en gris, sin permitirme modificarla:</div>
<div align="justify">
<br /></div>
<div align="justify">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3aiB8tJ1C2PH6f4FdKMNEJG5LRHgJX_0MzCrfrV3QvdSfaL973cALnvs1eeFgsv-qxKbYxJQ5j8q4A8mttJ__myBnHrFxd8EOVFiCmfnFgE0UTVj3S_oo-qAbVANTidUkrgRTNGojjEOH/s1600-h/EdgeSync1%25255B11%25255D.png"><img alt="EdgeSync1" border="0" height="484" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg9RsdDvJRST3MkP2Z5U2x-as0umxDxeeCQ6iT-eE3rkvWf84u8a518zDN6ylagkv_qCVuq1fk25xQutWQ9T8x2NnaVCRMvLZahmMvl-RIt6Wf78LsP6sUvyBLa6HsPtK_K4D7-VIrvNHE7/?imgmax=800" style="background-image: none; border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; display: inline; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="EdgeSync1" width="320" /></a> <br />
<div align="justify">
<br /></div>
<div align="justify">
Siguiendo las instrucciones fui a la configuración de Windows (pinche en Settinhs), para comprobar que allí todo estaba bién, la sincronización estaba activada:</div>
<div align="justify">
<br /></div>
<div align="justify">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh82e9athajQFS_tyYPJTphYtejBQFPyMKNrsJkTcJBKncQmfJ9TH1qPRShJK9pi2E4GAYYUwEOP69kpsngWuotIBfRErT_5V9YHQFvHPgrJ018-flqMbpeQ_aGqiwim7j5ViZexO3MY8xA/s1600-h/EdgeSync2%25255B3%25255D.png"><img alt="EdgeSync2" border="0" height="484" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgNx3HC3spTjtHkxz0Z2fEmND9lkPIn5u71CrCx95MGK5cx_PWDFrD4le7Y3AUd3QoKDHsLP1bw3HoU66a8V3wCRH_MkUCuUnhttPlbq8a45GOXgiIkT80WdM2CIS1M1-cdYbGH4p-IiuM5/?imgmax=800" style="background-image: none; border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; display: inline; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="EdgeSync2" width="622" /></a></div>
<div align="justify">
<br /></div>
<div align="justify">
Este mismo escenario estaba en los dos equipos… ¿Que pasaba? No lo se, estas son las cosas que me desesperan de Windows, pero bueno, un rato dse busqueda en internet me llevó a la solución: Editar en el registro (regedit.exe) la clave <strong>SyncPolicy </strong>que esta en la siguiente ruta:</div>
<div align="justify">
<strong><br /></strong></div>
<div align="justify">
<strong>HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\SettingSync</strong></div>
<div align="justify">
<br /></div>
<div align="justify">
<u>El valor a asignar es el 1</u> (estaba a cero).</div>
<div align="justify">
<br /></div>
<div align="justify">
Una vez hecho ya pude activar la sincronización en Edge:</div>
<div align="justify">
<br /></div>
<div align="justify">
<a href="https://lh3.googleusercontent.com/-Vomg-vw9KHQ/VrYYZAHFBFI/AAAAAAAAAgI/mMRt1mIk3kc/s1600-h/image%25255B5%25255D.png"><img alt="image" border="0" height="643" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJ-a3meFCPmXmBaKfFJHvgzCzToR_mOcrZCR50gcOiy56ZrrdYi1ekL5B5ynR4vto0IYO4q-K0ECxGnmRnPCsqq33unLnZFDEu0eOXQvRbKPwmAiQNLgGKU9G5XocYrjDGqDRNIdXEVzZY/?imgmax=800" style="background-image: none; border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; display: inline; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="268" /></a></div>
<div align="justify">
<br /></div>
<div align="justify">
Y lo mas curioso: solo necesité hacerlo en uno de los equipos. Una vez lo hice en el primero(el desktop) los favoritos de Edge empezaron a sincronizarse. Fui a la surface y la setting de sincronización se había activado sola <img alt="Smile" class="wlEmoticon wlEmoticon-smile" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgRLCKaj_vT31b3aEyC12rjuWDB3GFQF8vEuaM_4nYxh4uY0GoGR-QNNGOH11R8qpYYjAnz_shlOJjEtpl92wbzZPE5Xt3K7YUG2L9DJjKvQ5D_IZ81B1-hhmZ1mND4dkgilLpkhNR6vYZR/?imgmax=800" style="border-bottom-style: none; border-left-style: none; border-right-style: none; border-top-style: none;" /></div>
<div align="justify">
<br /></div>
<div align="justify">
Si esto resuelve a alguién su problema… genial!</div>
</div>
Sergio Navarro Pinohttp://www.blogger.com/profile/15840814836353973005noreply@blogger.com0tag:blogger.com,1999:blog-7984577123741299127.post-5360700796144599832015-03-02T19:26:00.000+01:002015-03-02T19:27:15.912+01:00De los delegados a la lambdas 2: Rarezas<div abp="1446">
<div abp="1816">
<style type="text/css">
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: Consolas, "Courier New", Courier, Monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #a31515; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
</style>
</div>
</div>
<div abp="1153">
<div abp="57">
<div abp="618">
<div abp="975">
<div abp="1451">
<div abp="1822">
Vamos a continuar la serie dedicada a delegados y lambdas. Después de haber explicado los delegados y antes de meternos con las lambdas, vamos a ver algunos aspectos "raros" o mejor dicho, menos conocidos de los delegados.</div>
</div>
</div>
</div>
</div>
</div>
<div abp="1154">
<div abp="59">
<div abp="621">
<div abp="979">
<div abp="1456">
<div abp="1828">
<br /></div>
</div>
</div>
</div>
</div>
</div>
<h3 abp="1155">
Delegate multicasting</h3>
<div abp="1156">
<div abp="62">
<div abp="625">
<div abp="984">
<div abp="1462">
<div abp="1835">
<br /></div>
</div>
</div>
</div>
</div>
</div>
<div abp="1157">
<div abp="64">
<div abp="628">
<div abp="988">
<div abp="1467">
<div abp="1841">
Se trata de una característica que nos va a permitir almacenar más de un método en un delegado.</div>
</div>
</div>
</div>
</div>
</div>
<div abp="1158">
<div abp="66">
<div abp="631">
<div abp="992">
<div abp="1472">
<div abp="1847">
Para emplearla debemos utilizar el operador +=</div>
</div>
</div>
</div>
</div>
</div>
<div abp="1159">
<div abp="68">
<div abp="634">
<div abp="996">
<div abp="1477">
<div abp="1853">
<br /></div>
</div>
</div>
</div>
</div>
</div>
<div abp="1160">
<div abp="70">
<div abp="637">
<div abp="1000">
<div abp="1482">
<div abp="1859">
<!-- code formatted by http://manoli.net/csharpformat/ -->
</div>
</div>
</div>
</div>
</div>
</div>
<div abp="1161" class="csharpcode">
<pre abp="1162" class="alt"><span abp="1163" class="lnum"> 1: </span><span abp="1164" class="kwrd">class</span> Program</pre>
<pre abp="1165"><span abp="1166" class="lnum"> 2: </span>{</pre>
<pre abp="1167" class="alt"><span abp="1168" class="lnum"> 3: </span> <span abp="1169" class="kwrd">delegate</span> <span abp="1170" class="kwrd">void</span> delSaludo();</pre>
<pre abp="1171"><span abp="1172" class="lnum"> 4: </span> </pre>
<pre abp="1173" class="alt"><span abp="1174" class="lnum"> 5: </span> <span abp="1175" class="kwrd">static</span> <span abp="1176" class="kwrd">void</span> Main(<span abp="1177" class="kwrd">string</span>[] args)</pre>
<pre abp="1178"><span abp="1179" class="lnum"> 6: </span> {</pre>
<pre abp="1180" class="alt"><span abp="1181" class="lnum"> 7: </span> delSaludo miDel = Saludo1;</pre>
<pre abp="1182"><span abp="1183" class="lnum"> 8: </span> miDel += Saludo2;</pre>
<pre abp="1184" class="alt"><span abp="1185" class="lnum"> 9: </span> </pre>
<pre abp="1186"><span abp="1187" class="lnum"> 10: </span> miDel();</pre>
<pre abp="1188" class="alt"><span abp="1189" class="lnum"> 11: </span> }</pre>
<pre abp="1190"><span abp="1191" class="lnum"> 12: </span> </pre>
<pre abp="1192" class="alt"><span abp="1193" class="lnum"> 13: </span> <span abp="1194" class="kwrd">protected</span> <span abp="1195" class="kwrd">static</span> <span abp="1196" class="kwrd">void</span> Saludo1()</pre>
<pre abp="1197"><span abp="1198" class="lnum"> 14: </span> {</pre>
<pre abp="1199" class="alt"><span abp="1200" class="lnum"> 15: </span> Console.WriteLine(<span abp="1201" class="str">"Este es el saludo 1"</span>);</pre>
<pre abp="1202"><span abp="1203" class="lnum"> 16: </span> }</pre>
<pre abp="1204" class="alt"><span abp="1205" class="lnum"> 17: </span> </pre>
<pre abp="1206"><span abp="1207" class="lnum"> 18: </span> <span abp="1208" class="kwrd">protected</span> <span abp="1209" class="kwrd">static</span> <span abp="1210" class="kwrd">void</span> Saludo2()</pre>
<pre abp="1211" class="alt"><span abp="1212" class="lnum"> 19: </span> {</pre>
<pre abp="1213"><span abp="1214" class="lnum"> 20: </span> Console.WriteLine(<span abp="1215" class="str">"Este es el saludo 2"</span>);</pre>
<pre abp="1216" class="alt"><span abp="1217" class="lnum"> 21: </span> }</pre>
<pre abp="1218"><span abp="1219" class="lnum"> 22: </span>}</pre>
</div>
<div abp="1220">
<div abp="131">
<div abp="699">
<div abp="1063">
<div abp="1546">
<div abp="1924">
<br /></div>
</div>
</div>
</div>
</div>
</div>
<div abp="1221">
<div abp="133">
<div abp="702">
<div abp="1067">
<div abp="1551">
<div abp="1930">
La salida por pantalla correspondiente la podéis imaginar:</div>
</div>
</div>
</div>
</div>
</div>
<div abp="1222">
<div abp="135">
<div abp="705">
<div abp="1071">
<div abp="1556">
<div abp="1936">
</div>
</div>
</div>
</div>
</div>
</div>
<div abp="1326" class="separator" style="clear: both; text-align: center;">
<a abp="1327" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEYjmc9rB1hyphenhyphena0mk_8Q8qRLtmEDKDPWJ3Yi0944Si5rK0TfKXXz-XktJ7-HsoTyLT-0TQfX8kUf1D-QGfQqPF2dzmMetTrrRVOWM2u7alOR9-lUyCsgVkjQgSvQaIgmInUr1xIVPshazFw/s1600/Salida21.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img abp="1328" border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEYjmc9rB1hyphenhyphena0mk_8Q8qRLtmEDKDPWJ3Yi0944Si5rK0TfKXXz-XktJ7-HsoTyLT-0TQfX8kUf1D-QGfQqPF2dzmMetTrrRVOWM2u7alOR9-lUyCsgVkjQgSvQaIgmInUr1xIVPshazFw/s1600/Salida21.png" height="323" width="640" /></a></div>
<div abp="1223">
<div abp="140">
<div abp="711">
<div abp="1078">
<div abp="1564">
<div abp="1945">
</div>
</div>
</div>
</div>
</div>
</div>
<div abp="1226">
<div abp="142">
<div abp="714">
<div abp="1082">
<div abp="1569">
<div abp="1951">
</div>
</div>
</div>
</div>
</div>
</div>
<div abp="1227">
<div abp="144">
<div abp="717">
<div abp="1086">
<div abp="1574">
<div abp="1957">
También podemos utilizar el operador -=</div>
</div>
</div>
</div>
</div>
</div>
<div abp="1227">
<div abp="146">
<div abp="720">
<div abp="1090">
<div abp="1579">
<div abp="1963">
</div>
</div>
</div>
</div>
</div>
</div>
<div abp="1227">
<div abp="148">
<div abp="723">
<div abp="1094">
<div abp="1584">
<div abp="1969">
Hemos visto un ejemplo con métodos que no devuelven nada. Sin embargo he leído por ahí que si hacemos multicasting a métodos no void obtendremos un excepción. ¿Es esto cierto? Veámoslo</div>
</div>
</div>
</div>
</div>
</div>
<div abp="1227">
<div abp="150">
<div abp="726">
<div abp="1098">
<div abp="1589">
<div abp="1975">
</div>
</div>
</div>
</div>
</div>
</div>
<div abp="151">
<div abp="728">
<div abp="1101">
<div abp="1593">
<div abp="1980">
<!-- code formatted by http://manoli.net/csharpformat/ -->
</div>
</div>
</div>
</div>
</div>
<div abp="152" class="csharpcode">
<pre abp="153" class="alt"><span abp="154" class="lnum"> 1: </span><span abp="155" class="kwrd">class</span> Program</pre>
<pre abp="156"><span abp="157" class="lnum"> 2: </span>{</pre>
<pre abp="158" class="alt"><span abp="159" class="lnum"> 3: </span> <span abp="160" class="kwrd">delegate</span> <span abp="161" class="kwrd">int</span> delOperacion(<span abp="162" class="kwrd">int</span> a, <span abp="163" class="kwrd">int</span> b);</pre>
<pre abp="164"><span abp="165" class="lnum"> 4: </span> </pre>
<pre abp="166" class="alt"><span abp="167" class="lnum"> 5: </span> <span abp="168" class="kwrd">static</span> <span abp="169" class="kwrd">void</span> Main(<span abp="170" class="kwrd">string</span>[] args)</pre>
<pre abp="171"><span abp="172" class="lnum"> 6: </span> { </pre>
<pre abp="173" class="alt"><span abp="174" class="lnum"> 7: </span> delOperacion miDelOp = Suma;</pre>
<pre abp="175"><span abp="176" class="lnum"> 8: </span> miDelOp += Mult;</pre>
<pre abp="177" class="alt"><span abp="178" class="lnum"> 9: </span> </pre>
<pre abp="179"><span abp="180" class="lnum"> 10: </span> <span abp="181" class="kwrd">var</span> res = miDelOp(2, 3);</pre>
<pre abp="182" class="alt"><span abp="183" class="lnum"> 11: </span> Console.WriteLine(<span abp="184" class="str">"Resultado={0}"</span>,res);</pre>
<pre abp="185"><span abp="186" class="lnum"> 12: </span> }</pre>
<pre abp="187" class="alt"><span abp="188" class="lnum"> 13: </span> </pre>
<pre abp="189"><span abp="190" class="lnum"> 14: </span> <span abp="191" class="kwrd">protected</span> <span abp="192" class="kwrd">static</span> <span abp="193" class="kwrd">int</span> Suma(<span abp="194" class="kwrd">int</span> a, <span abp="195" class="kwrd">int</span> b)</pre>
<pre abp="196" class="alt"><span abp="197" class="lnum"> 15: </span> {</pre>
<pre abp="198"><span abp="199" class="lnum"> 16: </span> <span abp="200" class="kwrd">return</span> a + b;</pre>
<pre abp="201" class="alt"><span abp="202" class="lnum"> 17: </span> }</pre>
<pre abp="203"><span abp="204" class="lnum"> 18: </span> </pre>
<pre abp="205" class="alt"><span abp="206" class="lnum"> 19: </span> <span abp="207" class="kwrd">protected</span> <span abp="208" class="kwrd">static</span> <span abp="209" class="kwrd">int</span> Mult(<span abp="210" class="kwrd">int</span> a, <span abp="211" class="kwrd">int</span> b)</pre>
<pre abp="212"><span abp="213" class="lnum"> 20: </span> {</pre>
<pre abp="214" class="alt"><span abp="215" class="lnum"> 21: </span> <span abp="216" class="kwrd">return</span> a * b;</pre>
<pre abp="217"><span abp="218" class="lnum"> 22: </span> }</pre>
<pre abp="219" class="alt"><span abp="220" class="lnum"> 23: </span>}</pre>
</div>
<div abp="1227">
<div abp="222">
<div abp="800">
<div abp="1174">
<div abp="1667">
<div abp="2055">
</div>
</div>
</div>
</div>
</div>
</div>
<div abp="1227">
<div abp="224">
<div abp="803">
<div abp="1178">
<div abp="1672">
<div abp="2061">
¿Qué salida obtendremos?</div>
</div>
</div>
</div>
</div>
</div>
<div abp="1227">
<div abp="226">
<div abp="806">
<div abp="1182">
<div abp="1677">
<div abp="2067">
</div>
</div>
</div>
</div>
</div>
</div>
<div abp="1227">
<div abp="228" class="separator" style="clear: both; text-align: center;">
<a abp="229" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPVlspdCKIOORsYCn49gap5D6VnfGvXBjlRE5sXjg3IZgKWiMOjOhte4Iqn8OBR1ITE3qE1y0IahjPpj04FxatFCHm4M8D9kmA-vkTRMJhkZOYxlW28u1zjZWemzrb_f8Pid0UUO5-ZzaD/s1600/Salida22.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img abp="230" border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPVlspdCKIOORsYCn49gap5D6VnfGvXBjlRE5sXjg3IZgKWiMOjOhte4Iqn8OBR1ITE3qE1y0IahjPpj04FxatFCHm4M8D9kmA-vkTRMJhkZOYxlW28u1zjZWemzrb_f8Pid0UUO5-ZzaD/s1600/Salida22.png" height="324" width="640" /></a></div>
<div abp="231">
<div abp="812">
<div abp="1189">
<div abp="1685">
<div abp="2076">
</div>
</div>
</div>
</div>
</div>
</div>
<div abp="1227">
<div abp="233">
<div abp="815">
<div abp="1193">
<div abp="1690">
<div abp="2082">
</div>
</div>
</div>
</div>
</div>
</div>
<div abp="1227">
<div abp="235">
<div abp="818">
<div abp="1197">
<div abp="1695">
<div abp="2088">
No hay excepción, y el valor que queda en la variable es el valor retornado por el último método invocado. ¿Curioso no?</div>
</div>
</div>
</div>
</div>
</div>
<div abp="1228">
<div abp="237">
<div abp="821">
<div abp="1201">
<div abp="1700">
<div abp="2094">
<br /></div>
</div>
</div>
</div>
</div>
</div>
<div abp="1229">
<h3 abp="239">
Delegate async invocation</h3>
<div abp="239">
<div abp="1205">
<div abp="1705">
<div abp="2100">
</div>
</div>
</div>
</div>
<div abp="239">
<div abp="1207">
<div abp="1708">
<div abp="2104">
Los delegados pueden ser ejecutados de modo asíncrono. Esto hará que un nuevo thread sea creado para nosotros y que este nuevo thread sea el que ejecute el método almacenado en el delegado.</div>
</div>
</div>
</div>
<div abp="239">
<div abp="1209">
<div abp="1711">
<div abp="2108">
</div>
<div abp="2108">
La forma general de usarlo es la siguiente. </div>
</div>
</div>
</div>
<div abp="239">
<div abp="1211">
<div abp="1714">
<div abp="2112">
</div>
<div abp="2112">
delegado.BeginInvoke(params, asyncCallback, object)</div>
</div>
</div>
</div>
</div>
<div abp="1230">
<div abp="827">
<div abp="1215">
<div abp="1719">
<div abp="2117">
</div>
<div abp="2117">
Si no vamos a usar un callback podemos fijarlo a nulo, junto con el último parámetro.</div>
<div abp="2117">
</div>
<div abp="2117">
Un ejemplo:</div>
</div>
</div>
</div>
<div abp="827">
<div abp="1219">
<div abp="1725">
<div abp="2125">
</div>
</div>
</div>
</div>
<div abp="1220">
<div abp="1727">
<div abp="2128">
<!-- code formatted by http://manoli.net/csharpformat/ -->
</div>
</div>
</div>
<div abp="1221" class="csharpcode">
<pre abp="1222" class="alt"><span abp="1223" class="lnum"> 1: </span><span abp="1224" class="kwrd">class</span> Program</pre>
<pre abp="1225"><span abp="1226" class="lnum"> 2: </span>{</pre>
<pre abp="1227" class="alt"><span abp="1228" class="lnum"> 3: </span> <span abp="1229" class="kwrd">delegate</span> <span abp="1230" class="kwrd">bool</span> delMakeOperation(<span abp="1231" class="kwrd">int</span> param);</pre>
<pre abp="1232"><span abp="1233" class="lnum"> 4: </span> <span abp="1234" class="kwrd">static</span> <span abp="1235" class="kwrd">void</span> Main(<span abp="1236" class="kwrd">string</span>[] args)</pre>
<pre abp="1237" class="alt"><span abp="1238" class="lnum"> 5: </span> {</pre>
<pre abp="1239"><span abp="1240" class="lnum"> 6: </span> delMakeOperation d1 = MakeLonggOp;</pre>
<pre abp="1241" class="alt"><span abp="1242" class="lnum"> 7: </span> </pre>
<pre abp="1243"><span abp="1244" class="lnum"> 8: </span> Console.WriteLine(<span abp="1245" class="str">"Main (threadId={0}) va a llamar al delegado"</span>, Thread.CurrentThread.ManagedThreadId);</pre>
<pre abp="1246" class="alt"><span abp="1247" class="lnum"> 9: </span> d1.BeginInvoke(6, <span abp="1248" class="kwrd">null</span>, <span abp="1249" class="kwrd">null</span>);</pre>
<pre abp="1250"><span abp="1251" class="lnum"> 10: </span> Thread.Sleep(1000);</pre>
<pre abp="1252" class="alt"><span abp="1253" class="lnum"> 11: </span> Console.WriteLine(<span abp="1254" class="str">"Main acaba"</span>);</pre>
<pre abp="1255"><span abp="1256" class="lnum"> 12: </span> }</pre>
<pre abp="1257" class="alt"><span abp="1258" class="lnum"> 13: </span> </pre>
<pre abp="1259"><span abp="1260" class="lnum"> 14: </span> <span abp="1261" class="kwrd">protected</span> <span abp="1262" class="kwrd">static</span> <span abp="1263" class="kwrd">bool</span> MakeLonggOp (<span abp="1264" class="kwrd">int</span> param)</pre>
<pre abp="1265" class="alt"><span abp="1266" class="lnum"> 15: </span> {</pre>
<pre abp="1267"><span abp="1268" class="lnum"> 16: </span> Console.WriteLine(<span abp="1269" class="str">"MakeLonggOp empieza (threadId={0})"</span>,Thread.CurrentThread.ManagedThreadId);</pre>
<pre abp="1270" class="alt"><span abp="1271" class="lnum"> 17: </span> Thread.Sleep(param * 1000);</pre>
<pre abp="1272"><span abp="1273" class="lnum"> 18: </span> Console.WriteLine(<span abp="1274" class="str">"MakeLonggOp acaba"</span>);</pre>
<pre abp="1275" class="alt"><span abp="1276" class="lnum"> 19: </span> </pre>
<pre abp="1277"><span abp="1278" class="lnum"> 20: </span> <span abp="1279" class="kwrd">return</span> <span abp="1280" class="kwrd">true</span>;</pre>
<pre abp="1281" class="alt"><span abp="1282" class="lnum"> 21: </span> }</pre>
<pre abp="1283"><span abp="1284" class="lnum"> 22: </span>}</pre>
</div>
<div abp="827">
<div abp="1286">
<div abp="1794">
<div abp="2196">
</div>
</div>
</div>
</div>
<div abp="827">
<div abp="1288">
<div abp="1797">
<div abp="2200">
La salida:</div>
</div>
</div>
<div abp="1288">
<div abp="1799">
<div abp="2203">
</div>
</div>
</div>
<div abp="1402" class="separator" style="clear: both; text-align: center;">
<a abp="1403" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTj-umQbpS5haARaG1_e60OEJvt9sclmUiN9EVHnq-mUVrHEwInfgvjO9Cw2kRSUm04BSy6pO24N7njBsrSQpDbZB2yDSJFMsyDnZ2ka0OHfE4bm8-Ob3Pq19Pyl-VoFNveHwnufni0WWA/s1600/Salida31.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img abp="1404" border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTj-umQbpS5haARaG1_e60OEJvt9sclmUiN9EVHnq-mUVrHEwInfgvjO9Cw2kRSUm04BSy6pO24N7njBsrSQpDbZB2yDSJFMsyDnZ2ka0OHfE4bm8-Ob3Pq19Pyl-VoFNveHwnufni0WWA/s1600/Salida31.png" height="324" width="640" /></a></div>
<div abp="1288">
<div abp="1804">
<div abp="2209">
</div>
</div>
</div>
</div>
<div abp="827">
<div abp="1290">
<div abp="1807">
<div abp="2213">
</div>
</div>
</div>
</div>
<div abp="1292">
<div abp="2215">
Como veis hay 2 threads ejecutándose... sin embargo la llamada asíncrona al delegado nunca termina, ya que mi aplicación de consola finaliza antes. ¿Cómo esperamos por la finalización del delegado? Hay varias formas</div>
</div>
<div abp="1292">
<div abp="2217">
</div>
</div>
<div abp="1292">
<ul abp="2219">
<li abp="2220"><div abp="1810">
<div abp="2222">
Esperando la finalización asíncrona con EndInvoke</div>
</div>
</li>
</ul>
</div>
<div abp="827">
<ul abp="2224">
<li abp="2225"><div abp="1810">
<div abp="2227">
Esperando a la finalización asíncrona con WaitHandle </div>
</div>
</li>
</ul>
</div>
<div abp="241">
<ul abp="2229">
<li abp="2230"><div abp="1810">
<div abp="2232">
Haciendo pooling ...no me gusta :-(</div>
</div>
</li>
</ul>
</div>
<ul abp="2233">
<li abp="2234"><div abp="1810">
<div abp="2236">
Ejecutando un callback</div>
</div>
</li>
</ul>
<div abp="1292">
<div abp="2238">
</div>
</div>
<div abp="1292">
<div abp="2240">
Aquí tenéis código para todas ellas:</div>
</div>
<div abp="1292">
<div abp="2242">
</div>
</div>
<div abp="2243">
<!-- code formatted by http://manoli.net/csharpformat/ -->
</div>
<div abp="2244" class="csharpcode">
<pre abp="2245" class="alt"><span abp="2246" class="lnum"> 1: </span><span abp="2247" class="kwrd">class</span> Program</pre>
<pre abp="2248"><span abp="2249" class="lnum"> 2: </span>{</pre>
<pre abp="2250" class="alt"><span abp="2251" class="lnum"> 3: </span> <span abp="2252" class="kwrd">delegate</span> <span abp="2253" class="kwrd">bool</span> delMakeOperation(<span abp="2254" class="kwrd">int</span> param);</pre>
<pre abp="2255"><span abp="2256" class="lnum"> 4: </span> <span abp="2257" class="kwrd">static</span> <span abp="2258" class="kwrd">void</span> Main(<span abp="2259" class="kwrd">string</span>[] args)</pre>
<pre abp="2260" class="alt"><span abp="2261" class="lnum"> 5: </span> {</pre>
<pre abp="2262"><span abp="2263" class="lnum"> 6: </span> Espera_EndInvoke();</pre>
<pre abp="2264" class="alt"><span abp="2265" class="lnum"> 7: </span> Espera_WaitHandle();</pre>
<pre abp="2266"><span abp="2267" class="lnum"> 8: </span> Espera_Pooling();</pre>
<pre abp="2268" class="alt"><span abp="2269" class="lnum"> 9: </span> Llamando_Callback();</pre>
<pre abp="2270"><span abp="2271" class="lnum"> 10: </span> }</pre>
<pre abp="2272" class="alt"><span abp="2273" class="lnum"> 11: </span> </pre>
<pre abp="2274"><span abp="2275" class="lnum"> 12: </span> <span abp="2276" class="kwrd">protected</span> <span abp="2277" class="kwrd">static</span> <span abp="2278" class="kwrd">bool</span> MakeLonggOp(<span abp="2279" class="kwrd">int</span> param)</pre>
<pre abp="2280" class="alt"><span abp="2281" class="lnum"> 13: </span> {</pre>
<pre abp="2282"><span abp="2283" class="lnum"> 14: </span> Console.WriteLine(<span abp="2284" class="str">"MakeLonggOp empieza (threadId={0})"</span>, Thread.CurrentThread.ManagedThreadId);</pre>
<pre abp="2285" class="alt"><span abp="2286" class="lnum"> 15: </span> Thread.Sleep(param * 1000);</pre>
<pre abp="2287"><span abp="2288" class="lnum"> 16: </span> Console.WriteLine(<span abp="2289" class="str">"MakeLonggOp acaba"</span>);</pre>
<pre abp="2290" class="alt"><span abp="2291" class="lnum"> 17: </span> </pre>
<pre abp="2292"><span abp="2293" class="lnum"> 18: </span> <span abp="2294" class="kwrd">return</span> <span abp="2295" class="kwrd">true</span>;</pre>
<pre abp="2296" class="alt"><span abp="2297" class="lnum"> 19: </span> }</pre>
<pre abp="2298"><span abp="2299" class="lnum"> 20: </span> </pre>
<pre abp="2300" class="alt"><span abp="2301" class="lnum"> 21: </span> <span abp="2302" class="kwrd">protected</span> <span abp="2303" class="kwrd">static</span> <span abp="2304" class="kwrd">void</span> Espera_EndInvoke()</pre>
<pre abp="2305"><span abp="2306" class="lnum"> 22: </span> {</pre>
<pre abp="2307" class="alt"><span abp="2308" class="lnum"> 23: </span> delMakeOperation d1 = MakeLonggOp;</pre>
<pre abp="2309"><span abp="2310" class="lnum"> 24: </span> </pre>
<pre abp="2311" class="alt"><span abp="2312" class="lnum"> 25: </span> Console.WriteLine(<span abp="2313" class="str">"Main (threadId={0}) va a llamar al delegado"</span>, Thread.CurrentThread.ManagedThreadId);</pre>
<pre abp="2314"><span abp="2315" class="lnum"> 26: </span> <span abp="2316" class="kwrd">var</span> asyncRes=d1.BeginInvoke(6, <span abp="2317" class="kwrd">null</span>, <span abp="2318" class="kwrd">null</span>);</pre>
<pre abp="2319" class="alt"><span abp="2320" class="lnum"> 27: </span> <span abp="2321" class="kwrd">var</span> res = d1.EndInvoke(asyncRes); <span abp="2322" class="rem">//Esta llamada es bloqueante</span></pre>
<pre abp="2323"><span abp="2324" class="lnum"> 28: </span> </pre>
<pre abp="2325" class="alt"><span abp="2326" class="lnum"> 29: </span> Console.WriteLine(<span abp="2327" class="str">"Metodo asincrono acaba. Resultado={0}"</span>, res);</pre>
<pre abp="2328"><span abp="2329" class="lnum"> 30: </span> }</pre>
<pre abp="2330" class="alt"><span abp="2331" class="lnum"> 31: </span> </pre>
<pre abp="2332"><span abp="2333" class="lnum"> 32: </span> <span abp="2334" class="kwrd">protected</span> <span abp="2335" class="kwrd">static</span> <span abp="2336" class="kwrd">void</span> Espera_WaitHandle()</pre>
<pre abp="2337" class="alt"><span abp="2338" class="lnum"> 33: </span> {</pre>
<pre abp="2339"><span abp="2340" class="lnum"> 34: </span> delMakeOperation d1 = MakeLonggOp;</pre>
<pre abp="2341" class="alt"><span abp="2342" class="lnum"> 35: </span> </pre>
<pre abp="2343"><span abp="2344" class="lnum"> 36: </span> Console.WriteLine(<span abp="2345" class="str">"Main (threadId={0}) va a llamar al delegado"</span>, Thread.CurrentThread.ManagedThreadId);</pre>
<pre abp="2346" class="alt"><span abp="2347" class="lnum"> 37: </span> <span abp="2348" class="kwrd">var</span> asyncRes = d1.BeginInvoke(6, <span abp="2349" class="kwrd">null</span>, <span abp="2350" class="kwrd">null</span>);</pre>
<pre abp="2351"><span abp="2352" class="lnum"> 38: </span> </pre>
<pre abp="2353" class="alt"><span abp="2354" class="lnum"> 39: </span> asyncRes.AsyncWaitHandle.WaitOne();<span abp="2355" class="rem">//Esta llamada es bloqueante </span></pre>
<pre abp="2356"><span abp="2357" class="lnum"> 40: </span> </pre>
<pre abp="2358" class="alt"><span abp="2359" class="lnum"> 41: </span> <span abp="2360" class="kwrd">var</span> res = d1.EndInvoke(asyncRes);</pre>
<pre abp="2361"><span abp="2362" class="lnum"> 42: </span> </pre>
<pre abp="2363" class="alt"><span abp="2364" class="lnum"> 43: </span> Console.WriteLine(<span abp="2365" class="str">"Metodo asincrono acaba. Resultado={0}"</span>, res);</pre>
<pre abp="2366"><span abp="2367" class="lnum"> 44: </span> }</pre>
<pre abp="2368" class="alt"><span abp="2369" class="lnum"> 45: </span> </pre>
<pre abp="2370"><span abp="2371" class="lnum"> 46: </span> <span abp="2372" class="kwrd">protected</span> <span abp="2373" class="kwrd">static</span> <span abp="2374" class="kwrd">void</span> Espera_Pooling()</pre>
<pre abp="2375" class="alt"><span abp="2376" class="lnum"> 47: </span> {</pre>
<pre abp="2377"><span abp="2378" class="lnum"> 48: </span> delMakeOperation d1 = MakeLonggOp;</pre>
<pre abp="2379" class="alt"><span abp="2380" class="lnum"> 49: </span> </pre>
<pre abp="2381"><span abp="2382" class="lnum"> 50: </span> Console.WriteLine(<span abp="2383" class="str">"Main (threadId={0}) va a llamar al delegado"</span>, Thread.CurrentThread.ManagedThreadId);</pre>
<pre abp="2384" class="alt"><span abp="2385" class="lnum"> 51: </span> <span abp="2386" class="kwrd">var</span> asyncRes = d1.BeginInvoke(6, <span abp="2387" class="kwrd">null</span>, <span abp="2388" class="kwrd">null</span>);</pre>
<pre abp="2389"><span abp="2390" class="lnum"> 52: </span> </pre>
<pre abp="2391" class="alt"><span abp="2392" class="lnum"> 53: </span> while (! asyncRes.IsCompleted)</pre>
<pre abp="2393"><span abp="2394" class="lnum"> 54: </span> {</pre>
<pre abp="2395" class="alt"><span abp="2396" class="lnum"> 55: </span> Thread.Sleep(250);</pre>
<pre abp="2397"><span abp="2398" class="lnum"> 56: </span> Console.Write(<span abp="2399" class="str">"."</span>);</pre>
<pre abp="2400" class="alt"><span abp="2401" class="lnum"> 57: </span> }</pre>
<pre abp="2402"><span abp="2403" class="lnum"> 58: </span> </pre>
<pre abp="2404" class="alt"><span abp="2405" class="lnum"> 59: </span> <span abp="2406" class="rem">//La ejecucion asincrona acabo</span></pre>
<pre abp="2407"><span abp="2408" class="lnum"> 60: </span> </pre>
<pre abp="2409" class="alt"><span abp="2410" class="lnum"> 61: </span> <span abp="2411" class="kwrd">var</span> res = d1.EndInvoke(asyncRes);</pre>
<pre abp="2412"><span abp="2413" class="lnum"> 62: </span> Console.WriteLine(<span abp="2414" class="str">"Metodo asincrono acaba. Resultado={0}"</span>, res);</pre>
<pre abp="2415" class="alt"><span abp="2416" class="lnum"> 63: </span> }</pre>
<pre abp="2417"><span abp="2418" class="lnum"> 64: </span> </pre>
<pre abp="2419" class="alt"><span abp="2420" class="lnum"> 65: </span> <span abp="2421" class="kwrd">protected</span> <span abp="2422" class="kwrd">static</span> <span abp="2423" class="kwrd">void</span> Llamando_Callback()</pre>
<pre abp="2424"><span abp="2425" class="lnum"> 66: </span> {</pre>
<pre abp="2426" class="alt"><span abp="2427" class="lnum"> 67: </span> delMakeOperation d1 = MakeLonggOp;</pre>
<pre abp="2428"><span abp="2429" class="lnum"> 68: </span> </pre>
<pre abp="2430" class="alt"><span abp="2431" class="lnum"> 69: </span> Console.WriteLine(<span abp="2432" class="str">"Main (threadId={0}) va a llamar al delegado"</span>, Thread.CurrentThread.ManagedThreadId);</pre>
<pre abp="2433"><span abp="2434" class="lnum"> 70: </span> <span abp="2435" class="kwrd">var</span> asyncRes = d1.BeginInvoke(6, <span abp="2436" class="kwrd">new</span> AsyncCallback(CallbackMethod), <span abp="2437" class="str">"async state"</span>);</pre>
<pre abp="2438" class="alt"><span abp="2439" class="lnum"> 71: </span> </pre>
<pre abp="2440"><span abp="2441" class="lnum"> 72: </span> Console.ReadLine();</pre>
<pre abp="2442" class="alt"><span abp="2443" class="lnum"> 73: </span> }</pre>
<pre abp="2444"><span abp="2445" class="lnum"> 74: </span> </pre>
<pre abp="2446" class="alt"><span abp="2447" class="lnum"> 75: </span> <span abp="2448" class="kwrd">static</span> <span abp="2449" class="kwrd">void</span> CallbackMethod(IAsyncResult ar)</pre>
<pre abp="2450"><span abp="2451" class="lnum"> 76: </span> {</pre>
<pre abp="2452" class="alt"><span abp="2453" class="lnum"> 77: </span> <span abp="2454" class="rem">// Recuperamos el delagdo</span></pre>
<pre abp="2455"><span abp="2456" class="lnum"> 78: </span> AsyncResult result = (AsyncResult)ar;</pre>
<pre abp="2457" class="alt"><span abp="2458" class="lnum"> 79: </span> delMakeOperation del = (delMakeOperation)result.AsyncDelegate;</pre>
<pre abp="2459"><span abp="2460" class="lnum"> 80: </span> </pre>
<pre abp="2461" class="alt"><span abp="2462" class="lnum"> 81: </span> <span abp="2463" class="kwrd">string</span> obj = (<span abp="2464" class="kwrd">string</span>)ar.AsyncState; <span abp="2465" class="rem">//Objeto pasado cuando se hizo la llamada asíncrona. Obtendremos "async state" que es lo que se paso</span></pre>
<pre abp="2466"><span abp="2467" class="lnum"> 82: </span> </pre>
<pre abp="2468" class="alt"><span abp="2469" class="lnum"> 83: </span> <span abp="2470" class="rem">// Call EndInvoke to retrieve the results. </span></pre>
<pre abp="2471"><span abp="2472" class="lnum"> 84: </span> <span abp="2473" class="kwrd">var</span> res = del.EndInvoke(ar);</pre>
<pre abp="2474" class="alt"><span abp="2475" class="lnum"> 85: </span> </pre>
<pre abp="2476"><span abp="2477" class="lnum"> 86: </span> Console.WriteLine(<span abp="2478" class="str">"Metodo asincrono acaba. Resultado={0}"</span>, res);</pre>
<pre abp="2479" class="alt"><span abp="2480" class="lnum"> 87: </span> }</pre>
<pre abp="2481"><span abp="2482" class="lnum"> 88: </span> </pre>
<pre abp="2483" class="alt"><span abp="2484" class="lnum"> 89: </span>}</pre>
</div>
<div abp="1292">
<div abp="2486">
</div>
</div>
<div abp="1292">
<div abp="2488">
En todos los casos obtenemos una salida como la siguiente:</div>
<div abp="2488">
</div>
<div abp="2623" class="separator" style="clear: both; text-align: center;">
<a abp="2624" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgfLYHMjKo_A8kC2FSfp19-ZaAmek_MxP2naZBbBFFz4OgANt8vvyHRYQgWiAYnnJIp-VABL8gicEKVRtCa58ELwT8YEK8nzy0Haz1Lh9rJNFMF6Uqh9jJGMXTRNYZAGFX3mDLVBRir5cfa/s1600/Salida41.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img abp="2625" border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgfLYHMjKo_A8kC2FSfp19-ZaAmek_MxP2naZBbBFFz4OgANt8vvyHRYQgWiAYnnJIp-VABL8gicEKVRtCa58ELwT8YEK8nzy0Haz1Lh9rJNFMF6Uqh9jJGMXTRNYZAGFX3mDLVBRir5cfa/s1600/Salida41.png" height="324" width="640" /></a></div>
<div abp="2626">
</div>
<div abp="2488">
</div>
</div>
<div abp="1292">
<div abp="2490">
Como veis hay muchas formas de esperar por la finalización del método asíncrono. De todos modos hoy en día hay formas más elegantes de hacer programación asíncrona (¿te suena async & await?). Ya lo veremos! Prometo escribir al respecto.</div>
</div>
<div abp="1292">
<div abp="2492">
</div>
</div>
<div abp="1292">
<div abp="2494">
Con esto dejamos los delegados, aquí tenéis el <a abp="2695" href="http://1drv.ms/1Li3tPe">código</a> completo.</div>
<div abp="2494">
</div>
<div abp="2494">
En la siguiente entrada trataremos las lambdas!</div>
</div>
<div abp="1292">
<div abp="2496">
</div>
</div>
<div abp="1292">
<div abp="2498">
Post publicados en esta serie:</div>
<div abp="2498">
</div>
<ul abp="2555">
<li abp="2556"><div abp="2557">
<a abp="2559" href="http://serginet.blogspot.com.es/2015/02/de-los-delegados-las-lambdas-1.html">De los delegados a las lambdas 1: Comenzando</a> </div>
</li>
<li abp="2561"><div abp="2562">
De los delegados a la lambdas 2: Rarezas</div>
</li>
</ul>
<div abp="2498">
</div>
</div>
<div abp="1292">
<div abp="2500">
</div>
</div>
<div abp="1292">
<div abp="2502">
</div>
</div>
</div>
Sergio Navarro Pinohttp://www.blogger.com/profile/15840814836353973005noreply@blogger.com228939 Arroyomolinos, Madrid, Spain40.264347 -3.920280100000013640.2522305 -3.9404501000000134 40.2764635 -3.9001101000000138tag:blogger.com,1999:blog-7984577123741299127.post-12702610384906235592015-02-20T11:54:00.000+01:002015-02-20T11:56:28.802+01:00De los delegados a las lambdas 1: Comenzando<div abp="1665">
<div abp="2229">
<div abp="523">
<style type="text/css">
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: Consolas, "Courier New", Courier, Monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #a31515; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
</style>
</div>
</div>
</div>
<div abp="634">
<div abp="978">
<div abp="1370">
<div abp="1669">
<div abp="2234">
<div abp="529">
Inicio una nueva serie de post (3 en principio) dedicados a los delegados y las lambdas... que hace tiempo se convirtieron en algo imprescindible para todo desarrollador C#.</div>
</div>
</div>
</div>
<div abp="1371">
<div abp="1671">
<div abp="2237">
<div abp="533">
<br /></div>
</div>
</div>
</div>
<div abp="1372">
<div abp="1673">
<div abp="2240">
<div abp="537">
Empecemos por el principio: que es un delegado, métodos anónimos, predicados y todas esas cosas que algunos desarrolladores usan, sin llegar a comprender lo que hacen. </div>
</div>
</div>
</div>
</div>
</div>
<div abp="635">
<div abp="980">
<div abp="1375">
<div abp="1677">
<div abp="2245">
<div abp="543">
<br /></div>
</div>
</div>
</div>
</div>
</div>
<h3 abp="636">
¿Qué es un delegado? </h3>
<div abp="637">
<div abp="983">
<div abp="1379">
<div abp="1682">
<div abp="2251">
<div abp="550">
</div>
</div>
</div>
<div abp="1683">
<div abp="2253">
<div abp="553">
Algo así (un poco de código vale mas que mil palabras):</div>
</div>
</div>
</div>
</div>
</div>
<div abp="639">
<div abp="987">
<div abp="1382">
<div abp="1687">
<div abp="2258">
<div abp="559">
<br /></div>
</div>
</div>
</div>
</div>
</div>
<div abp="640" class="csharpcode">
<pre abp="641" class="alt"><span abp="642" class="lnum"> 1: </span><span abp="643" class="kwrd">delegate</span> <return type> <delegate-name> <parameter list></pre>
</div>
<div abp="644">
<div abp="993">
<div abp="1389">
<div abp="1695">
<div abp="2267">
<div abp="569">
<br /></div>
</div>
</div>
</div>
</div>
</div>
<div abp="645">
<div abp="995">
<div abp="1392">
<div abp="1699">
<div abp="2272">
<div abp="575">
Un ejemplo:</div>
</div>
</div>
<div abp="1700">
<div abp="2274">
<div abp="578">
</div>
</div>
</div>
</div>
</div>
</div>
<div abp="647">
<div abp="999">
<div abp="1395">
<div abp="1704">
<div abp="2279">
<div abp="584">
<!-- code formatted by http://manoli.net/csharpformat/ -->
</div>
</div>
</div>
</div>
</div>
</div>
<div abp="648" class="csharpcode">
<pre abp="649" class="alt"><span abp="650" class="lnum"> 1: </span><span abp="651" class="kwrd">delegate</span> <span abp="652" class="kwrd">int</span> delOperacionSobreEnteros(<span abp="653" class="kwrd">int</span> num1, <span abp="654" class="kwrd">int</span> num2);</pre>
</div>
<div abp="655">
<div abp="1008">
<div abp="1405">
<div abp="1715">
<div abp="2291">
<div abp="597">
<br /></div>
</div>
</div>
</div>
</div>
</div>
<div abp="656">
<div abp="1010">
<div abp="1408">
<div abp="1719">
<div abp="2296">
<div abp="603">
Si eres de la vieja escuela habrás programado en C / C++ (que tiempos) así que lo más fácil es decir que es muy similar a un puntero a función. </div>
</div>
</div>
</div>
</div>
</div>
<div abp="657">
<div abp="1012">
<div abp="1411">
<div abp="1723">
<div abp="2301">
<div abp="609">
<br /></div>
</div>
</div>
</div>
</div>
</div>
<div abp="658">
<div abp="1014">
<div abp="1414">
<div abp="1727">
<div abp="2306">
<div abp="615">
Pero si no, te diré que un delegado es <u abp="659">un tipo que permite almacenar un método</u>. En el ejemplo anterior el delegado delOperacionSobreEnteros permite representar métodos que cumplan con su firma: recibe dos enteros como parámetros y devuelve un entero. Puesto que un delegado almacena un método, puede ser ejecutado.</div>
</div>
</div>
</div>
</div>
</div>
<div abp="660">
<div abp="1017">
<div abp="1418">
<div abp="1732">
<div abp="2312">
<div abp="622">
<br /></div>
</div>
</div>
</div>
</div>
</div>
<div abp="661">
<div abp="1019">
<div abp="1421">
<div abp="1736">
<div abp="2317">
<div abp="628">
Uno de los principales usos es definir delegados en los parámetros de un método, lo que nos permite mucha flexibilidad:</div>
</div>
</div>
</div>
</div>
</div>
<div abp="662">
<div abp="1021">
<div abp="1424">
<div abp="1740">
<div abp="2322">
<div abp="634">
<br /></div>
</div>
</div>
</div>
</div>
</div>
<div abp="663">
<div abp="1023">
<div abp="1427">
<div abp="1744">
<div abp="2327">
<div abp="640">
<!-- code formatted by http://manoli.net/csharpformat/ -->
</div>
</div>
</div>
</div>
</div>
</div>
<div abp="664" class="csharpcode">
<pre abp="665" class="alt"><span abp="666" class="lnum"> 1: </span><span abp="667" class="kwrd">class</span> Program</pre>
<pre abp="668"><span abp="669" class="lnum"> 2: </span>{</pre>
<pre abp="670" class="alt"><span abp="671" class="lnum"> 3: </span> <span abp="672" class="kwrd">public</span> <span abp="673" class="kwrd">delegate</span> <span abp="674" class="kwrd">int</span> delOperacionSobreEnteros(<span abp="675" class="kwrd">int</span> num1, <span abp="676" class="kwrd">int</span> num2);</pre>
<pre abp="677"><span abp="678" class="lnum"> 4: </span> <span abp="679" class="kwrd">static</span> <span abp="680" class="kwrd">void</span> Main(<span abp="681" class="kwrd">string</span>[] args)</pre>
<pre abp="682" class="alt"><span abp="683" class="lnum"> 5: </span> {</pre>
<pre abp="684"><span abp="685" class="lnum"> 6: </span> </pre>
<pre abp="686" class="alt"><span abp="687" class="lnum"> 7: </span> delOperacionSobreEnteros d1 = OperacionSuma;</pre>
<pre abp="688"><span abp="689" class="lnum"> 8: </span> delOperacionSobreEnteros d2 = <span abp="690" class="kwrd">new</span> delOperacionSobreEnteros(OperacionMultiplicacion);</pre>
<pre abp="691" class="alt"><span abp="692" class="lnum"> 9: </span> </pre>
<pre abp="693"><span abp="694" class="lnum"> 10: </span> EjecutaOperacion(d1);</pre>
<pre abp="695" class="alt"><span abp="696" class="lnum"> 11: </span> EjecutaOperacion(d2); </pre>
<pre abp="697"><span abp="698" class="lnum"> 12: </span> </pre>
<pre abp="699" class="alt"><span abp="700" class="lnum"> 13: </span> Console.ReadLine();</pre>
<pre abp="701"><span abp="702" class="lnum"> 14: </span> }</pre>
<pre abp="703" class="alt"><span abp="704" class="lnum"> 15: </span> </pre>
<pre abp="705"><span abp="706" class="lnum"> 16: </span> <span abp="707" class="kwrd">protected</span> <span abp="708" class="kwrd">static</span> <span abp="709" class="kwrd">void</span> EjecutaOperacion(delOperacionSobreEnteros delegado)</pre>
<pre abp="710" class="alt"><span abp="711" class="lnum"> 17: </span> {</pre>
<pre abp="712"><span abp="713" class="lnum"> 18: </span> <span abp="714" class="kwrd">const</span> <span abp="715" class="kwrd">int</span> a=3;</pre>
<pre abp="716" class="alt"><span abp="717" class="lnum"> 19: </span> <span abp="718" class="kwrd">const</span> <span abp="719" class="kwrd">int</span> b=2;</pre>
<pre abp="720"><span abp="721" class="lnum"> 20: </span> </pre>
<pre abp="722" class="alt"><span abp="723" class="lnum"> 21: </span> Console.WriteLine(<span abp="724" class="str">"Vamos a llamar al delegado"</span>);</pre>
<pre abp="725"><span abp="726" class="lnum"> 22: </span> <span abp="727" class="kwrd">var</span> res=delegado(a, b);</pre>
<pre abp="728" class="alt"><span abp="729" class="lnum"> 23: </span> Console.WriteLine(<span abp="730" class="str">"a op b={0}"</span>, res);</pre>
<pre abp="731"><span abp="732" class="lnum"> 24: </span> }</pre>
<pre abp="733" class="alt"><span abp="734" class="lnum"> 25: </span> </pre>
<pre abp="735"><span abp="736" class="lnum"> 26: </span> <span abp="737" class="kwrd">protected</span> <span abp="738" class="kwrd">static</span> <span abp="739" class="kwrd">int</span> OperacionSuma(<span abp="740" class="kwrd">int</span> a, <span abp="741" class="kwrd">int</span> b)</pre>
<pre abp="742" class="alt"><span abp="743" class="lnum"> 27: </span> {</pre>
<pre abp="744"><span abp="745" class="lnum"> 28: </span> <span abp="746" class="kwrd">return</span> a + b;</pre>
<pre abp="747" class="alt"><span abp="748" class="lnum"> 29: </span> }</pre>
<pre abp="749"><span abp="750" class="lnum"> 30: </span> </pre>
<pre abp="751" class="alt"><span abp="752" class="lnum"> 31: </span> <span abp="753" class="kwrd">protected</span> <span abp="754" class="kwrd">static</span> <span abp="755" class="kwrd">int</span> OperacionMultiplicacion(<span abp="756" class="kwrd">int</span> a, <span abp="757" class="kwrd">int</span> b)</pre>
<pre abp="758"><span abp="759" class="lnum"> 32: </span> {</pre>
<pre abp="760" class="alt"><span abp="761" class="lnum"> 33: </span> <span abp="762" class="kwrd">return</span> a * b;</pre>
<pre abp="763"><span abp="764" class="lnum"> 34: </span> }</pre>
<pre abp="765" class="alt"><span abp="766" class="lnum"> 35: </span>}</pre>
</div>
<div abp="767">
<div abp="1128">
<div abp="1533">
<div abp="1851">
<div abp="2435">
<div abp="749">
</div>
</div>
</div>
</div>
</div>
</div>
<div abp="768">
<div abp="1130">
<div abp="1536">
<div abp="1855">
<div abp="2440">
<div abp="755">
Como veis tengo un método que se encarga de ejecutar operaciones (EjecutaOperacion). ¿Qué operación? La recibida como parámetro a través de un delegado. La salida del anterior ejemplo es la siguiente:</div>
</div>
</div>
</div>
</div>
</div>
<div abp="769">
<div abp="1132">
<div abp="1539">
<div abp="1859">
<div abp="2445">
<div abp="761">
</div>
</div>
</div>
</div>
</div>
</div>
<div abp="2154" class="separator" style="clear: both; text-align: center;">
<a abp="2155" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3OQbj0ROYXqQk5QJl_dgcr1MnPmuUlThPCpofSonm1EyR_gHfxTy3Ss-P09J-bLettZMHljymoQvWFPJv-OqXqsmO4XguU73AhAPkzyUeBtMSBkVD1jdvLgNwAXn1zPdeSBtJIygxMN0c/s1600/Salida2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img abp="2156" border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3OQbj0ROYXqQk5QJl_dgcr1MnPmuUlThPCpofSonm1EyR_gHfxTy3Ss-P09J-bLettZMHljymoQvWFPJv-OqXqsmO4XguU73AhAPkzyUeBtMSBkVD1jdvLgNwAXn1zPdeSBtJIygxMN0c/s1600/Salida2.png" height="324" width="640" /></a></div>
<div abp="870" class="separator" style="clear: both; text-align: center;">
<br /></div>
<div abp="770">
<div abp="1137">
<div abp="1545">
<div abp="1866">
<div abp="2454">
<div abp="771">
</div>
</div>
</div>
</div>
</div>
</div>
<div abp="773">
<div abp="1139">
<div abp="1548">
<div abp="1870">
<div abp="2459">
<div abp="777">
También podemos hacer que una propiedad de una clase sea un delegado... seguro que se os ocurren más usos :-P</div>
</div>
</div>
</div>
</div>
</div>
<div abp="774">
<div abp="1141">
<div abp="1551">
<div abp="1874">
<div abp="2464">
<div abp="783">
</div>
</div>
</div>
</div>
</div>
</div>
<h3 abp="774">
¿Y que es eso de los métodos anónimos?</h3>
<div abp="774">
<div abp="1144">
<div abp="1555">
<div abp="1879">
<div abp="2470">
<div abp="790">
<br /></div>
</div>
</div>
</div>
</div>
<div abp="1145">
<div abp="1557">
<div abp="1882">
<div abp="2474">
<div abp="795">
Pues más de lo mismo, pero con sintaxis "reducida". Nos permiten asignar un bloque de código a un delegado, sin tener que declarar un método. Son por tanto métodos sin nombre, solo con cuerpo.</div>
</div>
</div>
</div>
</div>
</div>
<div abp="774">
<div abp="1147">
<div abp="1560">
<div abp="1886">
<div abp="2479">
<div abp="801">
<br /></div>
</div>
</div>
</div>
</div>
<div abp="1148">
<div abp="1562">
<div abp="1889">
<div abp="805">
<!-- code formatted by http://manoli.net/csharpformat/ -->
</div>
<div abp="806" class="csharpcode">
<pre abp="807" class="alt"><span abp="808" class="lnum"> 1: </span><span abp="809" class="kwrd">public</span> <span abp="810" class="kwrd">delegate</span> <span abp="811" class="kwrd">int</span> delOperacionSobreEnteros(<span abp="812" class="kwrd">int</span> num1, <span abp="813" class="kwrd">int</span> num2);</pre>
<pre abp="814"><span abp="815" class="lnum"> 2: </span> </pre>
<pre abp="816" class="alt"><span abp="817" class="lnum"> 3: </span>...</pre>
<pre abp="818"><span abp="819" class="lnum"> 4: </span> </pre>
<pre abp="820" class="alt"><span abp="821" class="lnum"> 5: </span>delOperacionSobreEnteros d3 = <span abp="822" class="kwrd">delegate</span> (<span abp="823" class="kwrd">int</span> a, <span abp="824" class="kwrd">int</span> b) { <span abp="825" class="kwrd">return</span> a / b ; };</pre>
<pre abp="826"><span abp="827" class="lnum"> 6: </span><span abp="828" class="kwrd">var</span> resd3=d3(10,2);</pre>
<pre abp="829" class="alt"><span abp="830" class="lnum"> 7: </span>Console.WriteLine(<span abp="831" class="str">"10 / 2={0}"</span>, resd3);</pre>
</div>
<div abp="2483">
<div abp="833">
</div>
</div>
<div abp="2483">
<div abp="835">
Estos métodos anónimos han sido reemplazados por las lambdas, mucho más potentes y cómodos. Lo veremos en las siguientes entradas</div>
</div>
</div>
</div>
</div>
</div>
<h3 abp="774">
<br abp="1150" />Otros tipos de delegados</h3>
<div abp="774">
<div abp="1152">
<div abp="1567">
<div abp="1895">
<div abp="2490">
<div abp="843">
</div>
</div>
</div>
</div>
</div>
</div>
<div abp="774">
<div abp="1154">
<div abp="1570">
<div abp="1899">
<div abp="2495">
<div abp="849">
Si... hay más. ¿ habéis usado <a abp="968" href="https://msdn.microsoft.com/en-us/library/system.threading.tasks.taskfactory(v=vs.110).aspx" target="_blank">TaskFactory</a> para ejecutar algo de un modo asíncrono ? </div>
</div>
</div>
</div>
</div>
</div>
<div abp="774">
<div abp="1157">
<div abp="1574">
<div abp="1904">
<div abp="2501">
<div abp="856">
Pues bien, aunque tiene varias sobrecargas, vamos a fijarnos en estas:</div>
</div>
</div>
<div abp="1904">
<div abp="2503">
<div abp="859">
</div>
</div>
</div>
</div>
</div>
</div>
<div abp="1158">
<div abp="1576">
<div abp="1907">
<div abp="2507">
<div abp="864">
<!-- code formatted by http://manoli.net/csharpformat/ -->
</div>
</div>
</div>
</div>
</div>
<div abp="1159" class="csharpcode">
<pre abp="1160" class="alt"><span abp="1161" class="lnum"> 1: </span><span abp="1162" class="kwrd">public</span> Task StartNew( Action action)</pre>
<pre abp="1163"><span abp="1164" class="lnum"> 2: </span><span abp="1165" class="kwrd">public</span> Task StartNew<TResult>(Func<TResult>) </pre>
</div>
<div abp="774">
<div abp="1167">
<div abp="1586">
<div abp="1918">
<div abp="2519">
<div abp="877">
<br /></div>
</div>
</div>
</div>
</div>
</div>
<div abp="774">
<div abp="1169">
<div abp="1589">
<div abp="1922">
<div abp="2524">
<div abp="883">
La primera permite ejecutar código asíncrono que no devuelve nada. Veis que recibe un parámetro tipo Action</div>
</div>
</div>
</div>
</div>
</div>
<div abp="774">
<div abp="1171">
<div abp="1592">
<div abp="1926">
<div abp="2529">
<div abp="889">
La segunda permite ejecutar código asíncrono que devolverá un valor, y recibe un parámetro tipo Func</div>
</div>
</div>
</div>
</div>
</div>
<div abp="774">
<div abp="1173">
<div abp="1595">
<div abp="1930">
<div abp="2534">
<div abp="895">
</div>
</div>
</div>
</div>
</div>
<div abp="1174">
<div abp="1597">
<div abp="1933">
<div abp="2538">
<div abp="900">
<u abp="1175">¿Y que son action y Func? Pues otros tipos de delegados.</u> </div>
</div>
</div>
</div>
</div>
</div>
<div abp="774">
<div abp="1177">
<div abp="1601">
<div abp="1938">
<div abp="2544">
<div abp="907">
</div>
</div>
</div>
</div>
</div>
<div abp="1178">
<div abp="1603">
<div abp="1941">
<div abp="2548">
<div abp="912">
Action <T1, T2,...> es un delegado que puede almacenar un método que recibe hasta 16 parámetros, pero que no retorna valor (void)</div>
</div>
</div>
</div>
</div>
</div>
<div abp="774">
<div abp="1180">
<div abp="1606">
<div abp="1945">
<div abp="2553">
<div abp="918">
Func <T1,T2..., TResult> también almacena un método con hasta 16 parámetros, pero puede devolver un valor</div>
</div>
</div>
</div>
</div>
</div>
<div abp="774">
<div abp="1182">
<div abp="1609">
<div abp="1949">
<div abp="2558">
<div abp="924">
</div>
</div>
</div>
</div>
</div>
<div abp="1182">
<div abp="1611">
<div abp="1952">
<div abp="2562">
<div abp="929">
Veámos un ejemplo con Func:</div>
</div>
</div>
</div>
</div>
</div>
<div abp="774">
<div abp="1184">
<div abp="1614">
<div abp="1956">
<div abp="2567">
<div abp="935">
</div>
</div>
</div>
</div>
</div>
<div abp="1615">
<div abp="1958">
<div abp="2570">
<div abp="939">
<!-- code formatted by http://manoli.net/csharpformat/ -->
</div>
</div>
</div>
</div>
<div abp="1616" class="csharpcode">
<pre abp="1617" class="alt"><span abp="1618" class="lnum"> 1: </span>Func<<span abp="1619" class="kwrd">int</span>, <span abp="1620" class="kwrd">int</span>, <span abp="1621" class="kwrd">int</span>> funcSuma = OperacionSuma;</pre>
<pre abp="1622"><span abp="1623" class="lnum"> 2: </span><span abp="1624" class="kwrd">var</span> resFunc = funcSuma.Invoke(6, 7);</pre>
<pre abp="1625" class="alt"><span abp="1626" class="lnum"> 3: </span>Console.WriteLine(<span abp="1627" class="str">"6 + 7={0}"</span>, resFunc);</pre>
</div>
</div>
<div abp="774">
<div abp="1186">
<div abp="1630">
<div abp="1974">
<div abp="2587">
<div abp="957">
</div>
</div>
</div>
</div>
</div>
<div abp="1186">
<div abp="1632">
<div abp="1977">
<div abp="2591">
<div abp="962">
Como veis, me ahorro la definición del delegado y utilizo uno que ya viene de serie con el framework. ¿Más cómodo no?</div>
</div>
</div>
</div>
<div abp="1633">
<div abp="1979">
<div abp="2594">
<div abp="966">
<br /></div>
</div>
</div>
</div>
<div abp="1634">
<div abp="1981">
<div abp="2597">
<div abp="970">
<u abp="2598">¿Y predicate?</u></div>
</div>
</div>
</div>
<div abp="1635">
<div abp="1983">
<div abp="2601">
<div abp="975">
<br /></div>
</div>
</div>
</div>
<div abp="1636">
<div abp="1985">
<div abp="2604">
<div abp="979">
De nuevo es un delegado que viene definido en .NET Framework (<a abp="1638" href="https://msdn.microsoft.com/en-us/library/bfcke1bz(v=vs.110).aspx" target="_blank">msdn</a>). En este caso se trata de un delegado que recibe un elemento como parámetros y que devuelve un booleano:</div>
</div>
</div>
</div>
<div abp="1640">
<div abp="1988">
<div abp="2608">
<div abp="984">
<br /></div>
</div>
</div>
</div>
<div abp="1641">
<div abp="1990">
<div abp="2611">
<div abp="988">
<!-- code formatted by http://manoli.net/csharpformat/ -->
</div>
</div>
</div>
</div>
<div abp="1642" class="csharpcode">
<pre abp="1643" class="alt"><span abp="1644" class="lnum"> 1: </span><span abp="1645" class="kwrd">public</span> <span abp="1646" class="kwrd">delegate</span> <span abp="1647" class="kwrd">bool</span> Predicate<<span abp="1648" class="kwrd">in</span> T>(</pre>
<pre abp="1649"><span abp="1650" class="lnum"> 2: </span> T obj</pre>
<pre abp="1651" class="alt"><span abp="1652" class="lnum"> 3: </span>)</pre>
</div>
<div abp="1653">
<div abp="2003">
<div abp="2625">
<div abp="1003">
<br /></div>
</div>
</div>
</div>
<div abp="1654">
<div abp="2005">
<div abp="2628">
<div abp="1007">
Se usa principalmente en ciertos métodos de filtrado sobre los tipos de datos Array y List. Mirad el siguiente ejemplo, creo que es autoexplicativo: </div>
</div>
</div>
</div>
</div>
<div abp="1186">
<div abp="1658">
<div abp="2012">
<div abp="2632">
<div abp="1012">
</div>
</div>
</div>
</div>
</div>
<div abp="1186">
<div abp="1660">
<div abp="2015">
<div abp="2636">
<div abp="1017">
</div>
</div>
</div>
</div>
</div>
</div>
<div abp="2637">
<div abp="1019">
<!-- code formatted by http://manoli.net/csharpformat/ -->
</div>
</div>
<div abp="2638" class="csharpcode">
<pre abp="2639" class="alt"><span abp="2640" class="lnum"> 1: </span>Predicate<<span abp="2641" class="kwrd">int</span>> predMayor = MayorQue10;</pre>
</div>
<div abp="2638" class="csharpcode">
<pre abp="2642"><span abp="2643" class="lnum"> 2: </span> </pre>
</div>
<div abp="2638" class="csharpcode">
<pre abp="2644" class="alt"><span abp="2645" class="lnum"> 3: </span><span abp="2646" class="kwrd">var</span> lista = <span abp="2647" class="kwrd">new</span> List<<span abp="2648" class="kwrd">int</span>>() { 3, 6, 15, 45 };</pre>
</div>
<div abp="2638" class="csharpcode">
<pre abp="2649"><span abp="2650" class="lnum"> 4: </span> </pre>
</div>
<div abp="2638" class="csharpcode">
<pre abp="2651" class="alt"><span abp="2652" class="lnum"> 5: </span><span abp="2653" class="kwrd">var</span> listaFiltrada = lista.FindAll(predMayor);</pre>
</div>
<div abp="2638" class="csharpcode">
<pre abp="2654"><span abp="2655" class="lnum"> 6: </span> </pre>
</div>
<div abp="2638" class="csharpcode">
<pre abp="2656" class="alt"><span abp="2657" class="lnum"> 7: </span>Console.WriteLine(<span abp="2658" class="str">"Elementos de la lista mayores que 10:"</span>);</pre>
</div>
<div abp="2638" class="csharpcode">
<pre abp="2659"><span abp="2660" class="lnum"> 8: </span><span abp="2661" class="kwrd">foreach</span>(<span abp="2662" class="kwrd">var</span> elem <span abp="2663" class="kwrd">in</span> listaFiltrada)</pre>
</div>
<div abp="2638" class="csharpcode">
<pre abp="2664" class="alt"><span abp="2665" class="lnum"> 9: </span> Console.Write(<span abp="2666" class="str">"{0} "</span>, elem);</pre>
</div>
<div abp="2638" class="csharpcode">
<pre abp="2667"><span abp="2668" class="lnum"> 10: </span> </pre>
</div>
<div abp="2638" class="csharpcode">
<pre abp="2669" class="alt"><span abp="2670" class="lnum"> 11: </span> </pre>
</div>
<div abp="2638" class="csharpcode">
<pre abp="2671"><span abp="2672" class="lnum"> 12: </span><span abp="2673" class="kwrd">protected</span> <span abp="2674" class="kwrd">static</span> <span abp="2675" class="kwrd">bool</span> MayorQue10 (<span abp="2676" class="kwrd">int</span> valor)</pre>
</div>
<div abp="2638" class="csharpcode">
<pre abp="2677" class="alt"><span abp="2678" class="lnum"> 13: </span>{</pre>
</div>
<div abp="2638" class="csharpcode">
<pre abp="2679"><span abp="2680" class="lnum"> 14: </span> <span abp="2681" class="kwrd">return</span> (valor > 10);</pre>
</div>
<div abp="2638" class="csharpcode">
<pre abp="2682" class="alt"><span abp="2683" class="lnum"> 15: </span>}</pre>
</div>
<div abp="2638" class="csharpcode">
<pre abp="2684" class="alt"> </pre>
</div>
<div abp="2638" class="csharpcode">
<div abp="1083">
</div>
</div>
<div abp="2638" class="csharpcode">
<div abp="1085">
Con esto terminamos, espero que lo anterior os resulte claro y útil.</div>
</div>
<div abp="2685" class="csharpcode">
<div abp="1087">
</div>
</div>
<div abp="2686" class="csharpcode">
<div abp="1089">
Como siempre <a abp="2688" href="http://1drv.ms/1Li3tPe" target="_blank">aquí</a> os dejo el código.</div>
</div>
Sergio Navarro Pinohttp://www.blogger.com/profile/15840814836353973005noreply@blogger.com028939 Arroyomolinos, Madrid, Spain40.264347 -3.920280100000013640.2522305 -3.9404501000000134 40.2764635 -3.9001101000000138