dagope's blog2024-01-02T10:29:01+00:00https://dagope.comDavid GonzaloTérminos útiles y trucos para KQL y Application Insights2023-10-11T23:00:00+00:00https://dagope.com/2023/10/11/terminos-utiles-kql<p><img src="https://dagope.com/public/uploads/2023/10/kql_trucos01.jpg" style="border:0px;" alt="KQL trucos" />
A continuación, encontrarás un resumen de los conceptos y términos básicos para manejarte con KQL por Application Insights perfectamente. Entender cuáles son las tablas importantes y sus campos principales. Y por supuesto las funciones más usadas y un ejemplo explicativo para manejarte sin problemas con KQL.
<!--break--></p>
<h2 id="tablas-y-campos-importantes">Tablas y campos importantes</h2>
<h3 id="requests">requests</h3>
<p>Esta tabla contiene información sobre las solicitudes HTTP realizadas a tu aplicación, incluyendo detalles como la URL, el método HTTP, el código de respuesta, el tiempo de respuesta y más.</p>
<table>
<tbody>
<tr>
<td>timestamp</td>
<td>La marca de tiempo de la solicitud.</td>
</tr>
<tr>
<td>id</td>
<td>Un identificador único para la solicitud.</td>
</tr>
<tr>
<td>name</td>
<td>El nombre de la solicitud.</td>
</tr>
<tr>
<td>duration</td>
<td>La duración de la solicitud en milisegundos.</td>
</tr>
<tr>
<td>resultCode</td>
<td>El código de respuesta HTTP de la solicitud (por ejemplo, 200, 404).</td>
</tr>
<tr>
<td>url</td>
<td>La URL de la solicitud.</td>
</tr>
<tr>
<td>customDimensions</td>
<td>Las dimensiones personalizadas asociadas con el evento.</td>
</tr>
<tr>
<td>cloudRoleName</td>
<td>generalmente contiene el nombre de la función, servicio o componente en la nube que está generando la telemetría.</td>
</tr>
</tbody>
</table>
<h3 id="traces">traces</h3>
<p>Esta tabla contiene registros de trazas o mensajes que puedes generar desde tu aplicación. Es útil para registrar información adicional sobre el comportamiento de tu aplicación.</p>
<table>
<tbody>
<tr>
<td>timestamp</td>
<td>La marca de tiempo del registro de seguimiento.</td>
</tr>
<tr>
<td>message</td>
<td>El mensaje de registro.</td>
</tr>
<tr>
<td>severityLevel</td>
<td>El nivel de gravedad del registro (por ejemplo, Information, Warning, Error).</td>
</tr>
<tr>
<td>loggerName</td>
<td>El nombre del registrador.</td>
</tr>
<tr>
<td>customDimensions</td>
<td>Las dimensiones personalizadas asociadas con el evento.</td>
</tr>
<tr>
<td>cloudRoleName</td>
<td>generalmente contiene el nombre de la función, servicio o componente en la nube que está generando la telemetría.</td>
</tr>
</tbody>
</table>
<h3 id="exceptions">exceptions</h3>
<p>Esta tabla contiene detalles sobre las excepciones o errores que ocurren en tu aplicación, incluyendo el tipo de excepción, la pila de llamadas y la hora en que ocurrió.</p>
<table>
<tbody>
<tr>
<td>timestamp</td>
<td>La marca de tiempo de la excepción.</td>
</tr>
<tr>
<td>type</td>
<td>El tipo de excepción.</td>
</tr>
<tr>
<td>message</td>
<td>El mensaje de la excepción.</td>
</tr>
<tr>
<td>outerMessage</td>
<td>El mensaje de la excepción externa si corresponde.</td>
</tr>
<tr>
<td>handledAt</td>
<td>Indica si la excepción fue manejada o no.</td>
</tr>
<tr>
<td>customDimensions</td>
<td>Las dimensiones personalizadas asociadas con el evento.</td>
</tr>
<tr>
<td>cloudRoleName</td>
<td>generalmente contiene el nombre de la función, servicio o componente en la nube que está generando la telemetría.</td>
</tr>
</tbody>
</table>
<h3 id="dependencies">dependencies</h3>
<p>Aquí encontrarás información sobre las llamadas a servicios externos o dependencias de tu aplicación, como bases de datos, servicios web, etc. Puedes ver detalles como el tipo de dependencia, el tiempo de respuesta y el éxito o fracaso de la llamada.</p>
<table>
<tbody>
<tr>
<td>timestamp</td>
<td>La marca de tiempo de la dependencia.</td>
</tr>
<tr>
<td>type</td>
<td>El tipo de dependencia (por ejemplo, SQL, HTTP).</td>
</tr>
<tr>
<td>target</td>
<td>El destino de la dependencia (por ejemplo, la URL de un servicio externo).</td>
</tr>
<tr>
<td>success</td>
<td>Indica si la dependencia se realizó con éxito o no.</td>
</tr>
<tr>
<td>resultCode</td>
<td>El código de respuesta de la dependencia.</td>
</tr>
<tr>
<td>customDimensions</td>
<td>Las dimensiones personalizadas asociadas con el evento.</td>
</tr>
<tr>
<td>cloudRoleName</td>
<td>generalmente contiene el nombre de la función, servicio o componente en la nube que está generando la telemetría.</td>
</tr>
</tbody>
</table>
<h3 id="customevents">customEvents</h3>
<p>Puedes registrar eventos personalizados en tu aplicación y consultar esta tabla para obtener información específica sobre esos eventos. Esto es útil para rastrear eventos importantes en tu aplicación.</p>
<table>
<tbody>
<tr>
<td>timestamp</td>
<td>La marca de tiempo del evento personalizado.</td>
</tr>
<tr>
<td>name</td>
<td>El nombre del evento personalizado.</td>
</tr>
<tr>
<td>customDimensions</td>
<td>Las dimensiones personalizadas asociadas con el evento.</td>
</tr>
</tbody>
</table>
<h2 id="funciones-más-usadas">Funciones más usadas</h2>
<p>Aquí tienes un listado de algunas de las características de filtros y funciones más utilizados en Kusto Query Language (KQL) junto con su definición y ejemplos de su uso:</p>
<h3 id="filtros">Filtros:</h3>
<h4 id="where"><em>where</em></h4>
<p>Se utiliza para filtrar filas que cumplan con una condición específica.</p>
<ul>
<li>Ejemplo: Filtrar solicitudes con un código de respuesta igual a 200.</li>
</ul>
<pre data-enlighter-language="sql">
datatable
| where resultCode == 200
</pre>
<h4 id="project"><em>project</em></h4>
<p>Se utiliza para seleccionar columnas específicas para incluir en los resultados de la consulta.</p>
<ul>
<li>Ejemplo: Seleccionar solo las columnas “timestamp” y “name”.</li>
</ul>
<pre data-enlighter-language="sql">
datatable
| project timestamp, name
</pre>
<h4 id="extend"><em>extend</em></h4>
<p>Permite agregar columnas calculadas a los resultados.</p>
<ul>
<li>Ejemplo: Calcular la duración en segundos.</li>
</ul>
<pre data-enlighter-language="sql">
datatable
| extend durationInSeconds =duration /1000
</pre>
<h4 id="summarize"><em>summarize</em></h4>
<p>Se utiliza para realizar agregaciones en los datos, como sumas, promedios o contar filas.</p>
<ul>
<li>Ejemplo: Contar el número de solicitudes por código de respuesta.</li>
</ul>
<pre data-enlighter-language="sql">
datatable
| summarize RequestCount = count() by resultCode
</pre>
<h4 id="join"><em>join</em></h4>
<p>Permite unir dos tablas en función de una condición.</p>
<ul>
<li>Ejemplo: Unir tablas de solicitudes y excepciones por ID de solicitud.</li>
</ul>
<pre data-enlighter-language="sql">
requests
| join kind = inner(exceptions) on operation_Id
</pre>
<h3 id="funciones">Funciones:</h3>
<h4 id="avg"><em>avg</em></h4>
<p>Calcula el promedio de los valores en una columna.</p>
<ul>
<li>Ejemplo: Calcular el tiempo promedio de respuesta de las solicitudes.</li>
</ul>
<pre data-enlighter-language="sql">
datatable
| summarize AvgResponseTime = avg(duration)
</pre>
<h4 id="count"><em>count</em></h4>
<p>Cuenta el número de filas en una tabla.</p>
<ul>
<li>Ejemplo: Contar el número total de excepciones.</li>
</ul>
<pre data-enlighter-language="sql">
exceptions
| count
</pre>
<h4 id="min-y-max"><em>min</em> y <em>max</em></h4>
<p>Encuentra el valor mínimo y máximo en una columna.</p>
<ul>
<li>Ejemplo: Encontrar el valor mínimo de una métrica.</li>
</ul>
<pre data-enlighter-language="sql">
datatable
| summarize MinValue =min(metricValue)
</pre>
<h4 id="distinct"><em>distinct</em></h4>
<p>Encuentra valores únicos en una columna.</p>
<ul>
<li>Ejemplo: Encontrar valores únicos en la columna “userId.”</li>
</ul>
<pre data-enlighter-language="sql">
datatable
| distinct userId
</pre>
<h4 id="make_datetime"><em>make_datetime</em></h4>
<p>Se utiliza para crear un valor de fecha y hora personalizado utilizando componentes de fecha y hora, como año, mes, día, hora, minuto, segundo y milisegundo. Esto es útil cuando deseas construir manualmente una marca de tiempo en lugar de depender de una columna de fecha y hora existente en tus datos.</p>
<ul>
<li>Sintaxis: <code class="language-plaintext highlighter-rouge">make_datetime(year, month, day, hour, minute, second, millisecond)</code>
<ul>
<li>year: El año.</li>
<li>month: El mes (1-12).</li>
<li>day: El día del mes.</li>
<li>hour: La hora (0-23).</li>
<li>minute: El minuto (0-59).</li>
<li>second: El segundo (0-59).</li>
<li>millisecond: El milisegundo (0-999).</li>
</ul>
</li>
<li>Ejemplo:</li>
</ul>
<pre data-enlighter-language="sql">
datatable
| extend customTimestamp = make_datetime(2023, 1, 1, 12, 30, 45, 500)
</pre>
<h4 id="bin"><em>Bin</em></h4>
<p>Se utiliza para agrupar valores en intervalos o contenedores. Puedes especificar el tamaño del intervalo y el punto de inicio del primer intervalo. La función render puede tomar varios tipos de visualización, como <em>timechart</em> (gráfico de tiempo), <em>barchart</em> (gráfico de barras), <em>piechart</em> (gráfico circular), <em>table</em> (tabla) y otros. El tipo de visualización que elijas depende de tus necesidades y de cómo desees presentar tus datos.</p>
<ul>
<li>Ejemplo de uso:
Supongamos que tienes una tabla de datos con una columna que representa la duración en segundos y deseas agrupar estas duraciones en intervalos de 10 segundos para analizar la distribución de las duraciones. Puedes usar la función bin de la siguiente manera:</li>
</ul>
<pre data-enlighter-language="sql">
datatable
| summarize Count = count() by bin(DurationInSeconds, 10)
</pre>
<h3 id="un-par-que-son-básicas">Un par que son básicas</h3>
<h4 id="order-by"><em>order by</em></h4>
<p>Se utiliza para ordenar los resultados de una consulta en función de una o más columnas específicas. Puedes utilizar order by para organizar los datos en un orden específico, ya sea ascendente o descendente, lo que facilita la identificación de tendencias, valores máximos o mínimos, entre otros.</p>
<ul>
<li>Ejemplo: Encontrar valores únicos en la columna “userId.”</li>
</ul>
<pre data-enlighter-language="sql">
requests
| order by timestamp desc
</pre>
<h4 id="render"><em>render</em></h4>
<p>Se utiliza para generar visualizaciones a partir de los resultados de una consulta KQL. Puedes especificar el tipo de visualización que deseas, como gráficos de líneas, gráficos de barras, tablas, entre otros. La función render se aplica al final de una consulta para mostrar visualmente los datos recuperados.</p>
<ul>
<li>Ejemplo de uso:
Supongamos que tienes una consulta que recopila datos de rendimiento de solicitudes en Application Insights y deseas visualizar estos datos en un gráfico de líneas. Puedes usar la función render de la siguiente manera:</li>
</ul>
<pre data-enlighter-language="sql">
datatable
| summarize AvgResponseTime = avg(duration) by bin(timestamp, 1h)
| render timechart
</pre>
<p>En este ejemplo, hemos agrupado el tiempo de respuesta promedio de las solicitudes en intervalos de 1 hora utilizando la función bin. Luego, aplicamos la función render con el tipo de visualización “timechart” para generar un gráfico de líneas que muestra la evolución del tiempo de respuesta promedio a lo largo del tiempo.</p>
<p>Espero te sea de ayuda todo esto.</p>
<p>Happy Codding!</p>
Resumen del 20192019-12-31T04:00:00+00:00https://dagope.com/2019/12/31/resumen-del-2019<p><img src="https://dagope.com/public/uploads/2019/12/resumen_2019.png" alt="Mirando al 2019" /></p>
<p>Último día del 2019 y me gustaría hacer un resumen de este año en torno a charlas y participación en comunidades tecnológicas. Mi último post fue en marzo de este año, y tengo iniciados unos 8 artículos con muchas notas pero me ha faltado tiempo para poder terminarlos. Ya tengo el primer propósito para el 2020 🙂</p>
<p>Yendo al resumen citando lo mejor de cada mes y seguro que algo me dejo:
<!--break--></p>
<h1 id="enero">Enero</h1>
<p>Comenzaba el año aplicándome duramente para terminar el curso de <a href="https://www.campusmvp.es/catalogo/Product-Docker-y-Kubernetes-desarrollo-y-despliegue-de-aplicaciones-basadas-en-contenedores_237.aspx#9389926812" target="_blank">Docker y Kubernetes de CampusMVP</a>, altamente recomendado, currazo del amigo Edu.
Pero la guinda de comenzar el 2019 fue con la oportunidad de participar en la primera
<a href="https://twitter.com/netcoreconf" target="_blank">NetCoreConf</a> en Barcelona.
Una charla sobre las novedades de C# 8.0, junto con <a href="https://twitter.com/fernandoescolar" target="_blank">Fer</a>.
Nos lo pasamos genial votando qué features eran más locas 😂.
Dejé un post escrito sobre toda la charla y los resultados <a href="https://dagope.com//2019/02/19/novedades-csharp8/">aquí</a> por si apetece recordar.</p>
<blockquote class="twitter-tweet"><p lang="es" dir="ltr">Empieza fuerte la charla con <a href="https://twitter.com/fernandoescolar?ref_src=twsrc%5Etfw">@fernandoescolar</a> y <a href="https://twitter.com/dagope?ref_src=twsrc%5Etfw">@dagope</a> <a href="https://t.co/BxeTZW4Cxj">pic.twitter.com/BxeTZW4Cxj</a></p>— jorgemht (@jorgemht) <a href="https://twitter.com/jorgemht/status/1089135416404520960?ref_src=twsrc%5Etfw">January 26, 2019</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<h1 id="febrero">Febrero</h1>
<p>Y arrancaba el año en la comunidad
<a href="https://twitter.com/XantarDev" target="_blank">@XantarDev</a>
donde nos vimos envueltos con nuevo evento el mismo <a href="https://xantardev.org/xantardev-vuelve-el-23f/" target="_blank">23F</a>. Desde la parte de la organización se ponía foco en hacer mejor las cosas este año. Se nos empezaba a conocer algo más fuera de Santiago y parece que las barreras de liar a nuevos ponentes empezaban a desaparecer.</p>
<blockquote class="twitter-tweet"><p lang="es" dir="ltr">Y el día aún no ha terminado que queda lo mejor. O Xantar! <a href="https://t.co/XQvFEjJOb8">https://t.co/XQvFEjJOb8</a></p>— David Gonzalo (@dagope) <a href="https://twitter.com/dagope/status/1099310213713268737?ref_src=twsrc%5Etfw">February 23, 2019</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<h1 id="marzo">Marzo</h1>
<p>Pues de un tweet a otro y al final acabas liando a dos grandes como
<a href="https://twitter.com/_unaizc_" target="_blank">Unai Zorrilla</a>
y <a href="https://twitter.com/psluaces" target="_blank">Pablo Santos</a>
para un evento en XantarDev. Lo sorprenderte es que liarlos fue lo más fácil.</p>
<blockquote class="twitter-tweet"><p lang="es" dir="ltr">El pasado XantarDev 23F tuvimos un 74% de asistencia entre todas las personas anotadas. No está mal, aunque hay margen de mejora. El próximo 29 de Marzo es una buena oportunidad para mejorar el porcentaje.<a href="https://t.co/qjkJrCuuxr">https://t.co/qjkJrCuuxr</a></p>— XantarDev (@XantarDev) <a href="https://twitter.com/XantarDev/status/1102529081063690241?ref_src=twsrc%5Etfw">March 4, 2019</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>Además, hicimos la presentación oficial de la meta-comunidad
<a href="https://twitter.com/CompostelaTech" target="_blank">CompostelaTech</a>
para servir de unión y difusión de todas las comunidades de Compostela. Un trabajo de meses en coordinación con otros grupos. Conseguimos poner un sitio común para estar mejor organizados y tener mayor visibilidad. <a href="https://compostelatech.org" target="_blank">https://compostelatech.org</a></p>
<blockquote class="twitter-tweet"><p lang="es" dir="ltr">Esta tarde nos presentamos al mundo, a partir de las 17.50 en el Centro Sociocultural Castiñeiriño. Uniendo comunidades. <a href="https://twitter.com/XantarDev?ref_src=twsrc%5Etfw">@XantarDev</a> <a href="https://twitter.com/GDGSantiagoES?ref_src=twsrc%5Etfw">@GDGSantiagoES</a> <a href="https://twitter.com/blockchain_gal?ref_src=twsrc%5Etfw">@blockchain_gal</a> <a href="https://twitter.com/ABERTEO?ref_src=twsrc%5Etfw">@ABERTEO</a> <a href="https://twitter.com/RecunchoMaker?ref_src=twsrc%5Etfw">@RecunchoMaker</a> WPCompostela</p>— CompostelaTech (@CompostelaTech) <a href="https://twitter.com/CompostelaTech/status/1111650584422215680?ref_src=twsrc%5Etfw">March 29, 2019</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<h1 id="abril">Abril</h1>
<p>Pude participar como ponente en la <a href="https://twitter.com/Gwab_Es" target="_blank">Global Azure Bootcamp</a>
en Barcelona. Compartir cartel con grandes profesionales con una ponencia de cómo creamos nuestros archivos PDFs en la nube.</p>
<blockquote class="twitter-tweet"><p lang="es" dir="ltr">Preparados por la Room 2. El <a href="https://twitter.com/GlobalAzure?ref_src=twsrc%5Etfw">@GlobalAzure</a> arranca en nada! <a href="https://twitter.com/CAT_zure?ref_src=twsrc%5Etfw">@CAT_zure</a> <a href="https://twitter.com/hashtag/Microsoft?src=hash&ref_src=twsrc%5Etfw">#Microsoft</a> <a href="https://twitter.com/hashtag/GAB2019?src=hash&ref_src=twsrc%5Etfw">#GAB2019</a> <a href="https://t.co/1ts7ygZtKb">pic.twitter.com/1ts7ygZtKb</a></p>— David Gonzalo (@dagope) <a href="https://twitter.com/dagope/status/1122022516082585600?ref_src=twsrc%5Etfw">April 27, 2019</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>Barcelona ya es como mi segunda casa de eventos.</p>
<p>Y entre medias recibo un mensaje de <a href="https://twitter.com/eiximenis" target="_blank">Edu</a> preguntando si se liaba un meetup o sino unas cervezas durante unos días que estaría por Santiago, ¿qué fué lo que pasó? Pues lo esperado, otro meetup para que aprendamos algo más de AKS. Y luego cervecitas. 🍻</p>
<blockquote class="twitter-tweet"><p lang="es" dir="ltr">aclarando conceptos de los contenedores Windows y Linux. <a href="https://twitter.com/eiximenis?ref_src=twsrc%5Etfw">@eiximenis</a> <a href="https://twitter.com/hashtag/XantarDev?src=hash&ref_src=twsrc%5Etfw">#XantarDev</a> <a href="https://t.co/eqOXpU3jVR">pic.twitter.com/eqOXpU3jVR</a></p>— XantarDev (@XantarDev) <a href="https://twitter.com/XantarDev/status/1115679610144948232?ref_src=twsrc%5Etfw">April 9, 2019</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<h1 id="mayo">Mayo</h1>
<p>Mes de “descanso”, conociendo ciudades como Roma y Florencia. Y entre medias trabajando en la organización de eventos futuros, que llegaban curvas.</p>
<h1 id="junio">Junio</h1>
<p>Este año el <a href="https://xantardev.org/xantardev-iii-aniversario/" target="_blank">3º aniversario de XantarDev</a>
se celebró en junio y desde el 2016 que creamos esta comunidad tecnológica. Un evento especial donde nos juntamos gente nueva con los que siempre están apoyando. Ni muchos, ni pocos, las entradas volaron y todas las comunidades de la zona se volcaron en participar.</p>
<blockquote class="twitter-tweet"><p lang="es" dir="ltr">Trabajazo junto con <a href="https://twitter.com/dariocp?ref_src=twsrc%5Etfw">@dariocp</a> para tener todo listo. <br />Mañana a las 9h en <a href="https://twitter.com/XantarDev?ref_src=twsrc%5Etfw">@XantarDev</a> III Aniversario. <a href="https://t.co/FH6Gb8Gc1H">pic.twitter.com/FH6Gb8Gc1H</a></p>— David Gonzalo (@dagope) <a href="https://twitter.com/dagope/status/1137115687187623938?ref_src=twsrc%5Etfw">June 7, 2019</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>Además de que conseguimos la paridad de ponentes mujeres/hombres, llevamos a cabo una iniciativa que no sabíamos cómo saldría y que fue totalmente respaldada por los asistentes, 1 kilo de comida por 1 entrada. Algo que animo al resto de los grupos a realizar. Hacemos comunidad tecnológica y aportamos nuestro granito de arena a la sociedad.</p>
<blockquote class="twitter-tweet"><p lang="es" dir="ltr">😲😲😲 Ya tenemos 3 cajas llenas de nuestra operación kilo. Bravo por todos los que han colaborado con su entrada gratuita. <a href="https://twitter.com/hashtag/XantarDev?src=hash&ref_src=twsrc%5Etfw">#XantarDev</a> <a href="https://t.co/mC5Q80kmMe">pic.twitter.com/mC5Q80kmMe</a></p>— XantarDev (@XantarDev) <a href="https://twitter.com/XantarDev/status/1137268162070745093?ref_src=twsrc%5Etfw">June 8, 2019</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>Tras el curro de organizar llegó el de asistir, y el evento de la <a href="dotNetConfSpain" target="_blank">DotNetConf Spain 2019</a>, es una cita de obligada. Indispensable para ver y aprender de ponentes de ámbito internacional. Organización de 10 y evento marcado en la agenda para 2020.</p>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Roadmap and future .NET 5 <a href="https://twitter.com/hashtag/DotNet2019?src=hash&ref_src=twsrc%5Etfw">#DotNet2019</a> <a href="https://twitter.com/hashtag/Microsoft?src=hash&ref_src=twsrc%5Etfw">#Microsoft</a> by <a href="https://twitter.com/coolcsh?ref_src=twsrc%5Etfw">@coolcsh</a> <a href="https://t.co/nZYwaqtSbg">pic.twitter.com/nZYwaqtSbg</a></p>— David Gonzalo (@dagope) <a href="https://twitter.com/dagope/status/1141262207960985601?ref_src=twsrc%5Etfw">June 19, 2019</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<h1 id="julio">Julio</h1>
<p>Primer <a href="https://compostelatech.org/resultado-primer-open-space-compostela-tech/" target="_blank">OpenSpace de CompostelaTech!!</a>.
Se organizó dentro del marco de la Galicia MakerFaire y se pudo debatir de forma totalmente abierta de tecnología con perfiles de asistentes muy dispares que enriquecieron el evento. Muchos temas quedaron en el tintero para la organización, por lo que otro OpenSpace en el futuro caerá, estoy convencido.</p>
<blockquote class="twitter-tweet"><p lang="es" dir="ltr">Moi Bo ambiente no <a href="https://twitter.com/hashtag/OpenSpace?src=hash&ref_src=twsrc%5Etfw">#OpenSpace</a> de <a href="https://twitter.com/CompostelaTech?ref_src=twsrc%5Etfw">@CompostelaTech</a> na <a href="https://twitter.com/MAKERSGALICIA?ref_src=twsrc%5Etfw">@MAKERSGALICIA</a> <a href="https://twitter.com/hashtag/makerfairegalicia?src=hash&ref_src=twsrc%5Etfw">#makerfairegalicia</a> <br /><br />CC <a href="https://twitter.com/Dthinkingal?ref_src=twsrc%5Etfw">@Dthinkingal</a> <a href="https://t.co/icRxwQfk5K">pic.twitter.com/icRxwQfk5K</a></p>— Miguel Varela (@varelamiguelou) <a href="https://twitter.com/varelamiguelou/status/1147451837571829760?ref_src=twsrc%5Etfw">July 6, 2019</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<h1 id="septiembre">Septiembre</h1>
<p>Fue la culminación de meses de trabajo. Por si no fuera poco aún eché una mano a la organización del evento <a href="https://xantardev.org/datagirls/" target="_blank">DataGirls</a> y pude compartir muy buenas conversaciones con las chicas de <a href="https://twitter.com/RLadiesMad" target="_blank">@RLadiesMad</a>. Son muy cracks!.</p>
<blockquote class="twitter-tweet"><p lang="es" dir="ltr">Calidad de evento el de <a href="https://twitter.com/XantarDev?ref_src=twsrc%5Etfw">@XantarDev</a> con <a href="https://twitter.com/RLadiesMAD?ref_src=twsrc%5Etfw">@RLadiesMAD</a>. Ahí la comunidad de compostela a tope <a href="https://twitter.com/CompostelaTech?ref_src=twsrc%5Etfw">@CompostelaTech</a> <a href="https://t.co/pEa9VxmN6f">pic.twitter.com/pEa9VxmN6f</a></p>— David Gonzalo (@dagope) <a href="https://twitter.com/dagope/status/1170408525576777730?ref_src=twsrc%5Etfw">September 7, 2019</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>Principalmente septiembre viene marcado por la organización del mayor evento de comunidad de .Net en Galicia, con la
<a href="https://galicia.netcoreconf.com/" target="_blank">Galicia NetCoreConf</a>. Evento que se llevaba cociendo desde las navidades del 2018, el “cuando se hace algo en Galicia” de muchos compañeros y amigos, acabó con esta respuesta. Con mucho trabajo conjunto con <a href="https://twitter.com/ferraces" target="_blank">Alberto</a> aquí en Coruña y desde la distancia Robert, Manu y Txema. Y además pude compartir charla con el gran <a href="https://twitter.com/robertbemejo" target="_blank">Robert</a> sobre las herramientas para saber que hace un App Service de Azure.</p>
<blockquote class="twitter-tweet"><p lang="es" dir="ltr">Todo a punto para la <a href="https://twitter.com/netcoreconf?ref_src=twsrc%5Etfw">@netcoreconf</a> <a href="https://twitter.com/hashtag/galicia?src=hash&ref_src=twsrc%5Etfw">#galicia</a> con el gran <a href="https://twitter.com/dagope?ref_src=twsrc%5Etfw">@dagope</a> <a href="https://t.co/85NJ4DBhZP">pic.twitter.com/85NJ4DBhZP</a></p>— Robert Bermejo (@robertbemejo) <a href="https://twitter.com/robertbemejo/status/1177846692185624576?ref_src=twsrc%5Etfw">September 28, 2019</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>Un día muy completo donde el no poder ver alguna charla valió la pena escuchando frases del tipo “Brutal! Nunca comí tan bien en un evento de comunidad”. Agradecer a todos los ponentes y personas que asistieron. La acogida superó todas las expectativas.</p>
<blockquote class="twitter-tweet"><p lang="es" dir="ltr">Un fin de semana también inolvidable en Galicia apoyando a la comunidad técnica junto con los amigos de la <a href="https://twitter.com/netcoreconf?ref_src=twsrc%5Etfw">@netcoreconf</a> <a href="https://twitter.com/dagope?ref_src=twsrc%5Etfw">@dagope</a> <a href="https://twitter.com/hashtag/netcoreconf2019?src=hash&ref_src=twsrc%5Etfw">#netcoreconf2019</a> <a href="https://twitter.com/tokiota_IT?ref_src=twsrc%5Etfw">@tokiota_IT</a> <a href="https://t.co/9zjmwXjait">pic.twitter.com/9zjmwXjait</a></p>— TOKIOTA (@tokiota_IT) <a href="https://twitter.com/tokiota_IT/status/1178957164544761857?ref_src=twsrc%5Etfw">October 1, 2019</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet"><p lang="und" dir="ltr"><a href="https://twitter.com/hashtag/netcoreconf?src=hash&ref_src=twsrc%5Etfw">#netcoreconf</a> <a href="https://twitter.com/hashtag/azure?src=hash&ref_src=twsrc%5Etfw">#azure</a> <a href="https://twitter.com/hashtag/ML?src=hash&ref_src=twsrc%5Etfw">#ML</a> <a href="https://twitter.com/hashtag/IOT?src=hash&ref_src=twsrc%5Etfw">#IOT</a> <a href="https://twitter.com/hashtag/SpeakersTeam?src=hash&ref_src=twsrc%5Etfw">#SpeakersTeam</a> <a href="https://t.co/P2qC1kCZ7F">pic.twitter.com/P2qC1kCZ7F</a></p>— netcoreconf (@netcoreconf) <a href="https://twitter.com/netcoreconf/status/1177990656960147459?ref_src=twsrc%5Etfw">September 28, 2019</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<h1 id="octubre">Octubre</h1>
<p>Este mes de octubre finalizó asistiendo a la <a href="https://twitter.com/netConfBcn" target="_blank">NetConfBcn</a> de Barcelona. Un evento en el que me lo pasé muy bien y pude tener muy buenas conversaciones sobre tecnología. Una organización de lujo y espero poder asistir el próximo año.</p>
<blockquote class="twitter-tweet"><p lang="es" dir="ltr">Aprovecho este tweet para el nombramiento a la organización y unirme al mensaje de GRACIAS por hacer esta <a href="https://twitter.com/hashtag/netconfbcn?src=hash&ref_src=twsrc%5Etfw">#netconfbcn</a> . Grandísimo evento, de los mejores que he podido asistir. Sois muy grandes todos! 👏👏👏 <a href="https://t.co/j4POWUycxa">https://t.co/j4POWUycxa</a></p>— David Gonzalo (@dagope) <a href="https://twitter.com/dagope/status/1188250229369380864?ref_src=twsrc%5Etfw">October 27, 2019</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<h1 id="noviembre">Noviembre</h1>
<p>Un mes en donde llevaba años sin faltar al Commit (viejo Codemotion) y motivado por una escapada conjunta con amig@s de Galicia, pero en esta ocasión lo dejé pasar por descansar y dedicar el tiempo a quien lo merece con un viaje a Japón muy enriquecedor.</p>
<h1 id="diciembre">Diciembre</h1>
<p>Ha pasado tan rápido que casi no me he enterado. <a href="https://twitter.com/dariocp" target="_blank">@dariocp</a> me lleva ventaja preparando nuestra primera actuación conjunta que será por febrero 2020.</p>
<h1 id="conclusiones">Conclusiones</h1>
<p>Haciendo este resumen yo mismo me quedo sorprendido de todo lo realizado en este 2019. Ya que ha sido acompañado de mucho trabajo en Tokiota y del que creo que todas estas aportaciones a la comunidad me han hecho crecer un poco más como profesional. Muchas horas robadas a la familia y a la pareja que sin el apoyo y paciencia no habría hecho nada de lo aquí citado.</p>
<p>Gracias a esas personas, me llevaría un buen rato citarlas a todas pero saben bien quienes son, que han colaborado en todo este año en la organización y creación de comunidad. Desde ponentes, colaboradores puntuales, patrocinios, salas, etc.. todos ellos/as gracias y mi respeto ganado.</p>
<h1 id="2020-is-comming">2020 is comming</h1>
<p>Tiempo es algo que me ha faltado en todo este año 2019 para poder hacer todo lo que quería. Un recurso escaso y del que una vez gastado nunca lo volverás a recuperar, es por eso que para este 2020 tengo como propósito gastar mi tiempo de la mejor manera posible.</p>
<p>Feliz 2020!</p>
Compartir discos con Docker cuando usamos un usuario del AzureAD2019-03-10T23:00:00+00:00https://dagope.com/2019/03/10/compartir-discos-docker-usuario-azuread<p>A día de hoy, existe un
<a href="https://github.com/docker/for-win/issues/132" target="_blank">issue</a>
(aún sin arreglar) en Docker CE for Windows que si has iniciado sesión con un usuario del Azure Active Directory (AzureAD) docker no es capaz de compartir los discos. Por mucho que lo intentes y aunque le pongas las credenciales correctas vuelves a comprobar y los discos siguen sin estar compartidos. Esto es un gran problema porque no podrás crear ningún volumen sobre tus contenedores.</p>
<p><a href="https://dagope.com/public/uploads/2019/03/docker_share_drive_azuread_user.png" target="_blank"><img src="https://dagope.com/public/uploads/2019/03/docker_share_drive_azuread_user.png" alt="Error compartiendo discos en docker con usuario del AzureAD" /></a>
<!--break--></p>
<h4 id="solución">Solución</h4>
<p>La solución encontrada ha sido <strong>crear un usuario local en Windows que sea Administrador y usar sus credenciales.</strong></p>
<p>Sencillo arreglo tras interpretar los errores en el log de Docker y comprenderlos. Agradecer e los cracks Edu
<a href="https://www.twitter.com/eiximenis/" target="_blank">@eiximenis</a>
y Jose Corral
<a href="https://www.twitter.com/jmanuelcorral/" target="_blank">@jmanuelcorral</a>
por la ayuda prestada 👍 .</p>
<p>Fácil de hacer así que vamos ello:</p>
<ul>
<li>Creamos un usuario local en Windows. Por ejemplo:
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>User: DockerAdmin
Pwd: 123456
</code></pre></div> </div>
<p>Obviamente <strong>usa una password segura</strong> y no la del ejemplo.</p>
</li>
<li>
<p>Inicia sesión en Windows con <code class="language-plaintext highlighter-rouge">nombrepc\DockerAdmin</code>
<br />Si no sabes el nombre de tu máquina escribe <code class="language-plaintext highlighter-rouge">hostname</code> en consola y lo obtendrás.</p>
</li>
<li>
<p>Tras el proceso de bienvenida al nuevo usuario, finaliza sesión y vuelve a iniciar con el usuario del Azure AD.</p>
</li>
<li>
<p>Abre las Settings de Docker Desktop. Botón derecho en el icono de docker en la bandeja de entrada.</p>
</li>
<li>
<p>Ve hacia Shared Drives, selecciona los discos a compartir, y pulsa Apply.</p>
</li>
<li>
<p>Ahora se nos abrirá el dialogo para introducir las credenciales de usuario. Este es el momento de poner el usuario que acabamos de crear <code class="language-plaintext highlighter-rouge">hostname</code>\DockerAdmin y la password.</p>
</li>
<li>Si todo ha funcionado correctamente los discos aparecerán marcados.</li>
</ul>
<p>-Si pruebas a ejecutar:
<br /><code class="language-plaintext highlighter-rouge">docker run --rm -v c:/Users:/data alpine ls /data</code>
<br />Verás que ahora funciona correctamente 😃</p>
<p>Happy coding!</p>
<p>David.</p>
<h2 id="update-10102019">UPDATE 10/10/2019:</h2>
<p>Es probable que si ejecutas (debug) una aplicación Web con Docker configurado de forma automática desde Visual Studio, recibas un <strong>error CT126</strong>, que si entras en el detalle verás te dice <em>Permisos denegados</em> en la carpeta <code class="language-plaintext highlighter-rouge">c:\Users\NombreDelUsuario</code> donde está intentando montar un volumen.</p>
<p>Como es lógico solo tienes que ir a esa carpeta y concederle permisos al usuario DockerAdmin que hiciste en los pasos de arriba y error resuelto.</p>
Moviendo la máquina Linux de Docker CE for Windows2019-03-05T23:00:00+00:00https://dagope.com/2019/03/05/mover-maquina-linux-docker-ce-windows<p>Cuando tenemos instalado <strong>Docker CE for Windows</strong> y configurado para imágenes linux, en realidad tenemos una máquina HyperV con una distro de linux ejecutándose y que hace de intermediaria para que todo parezca que funciona como si de magia negra se tratase. Pero, ¿qué pasa cuando te vas quedando sin espacio según vas descargando más y más imágenes docker?
<img src="https://dagope.com/public/uploads/2019/03/docker_move.png" style="border:0px" alt="Moviendo docker" />
<!--break--></p>
<p>La solución no pasa por configuraciones en ficheros json de docker, olvídate de eso, lo más simple es mover el disco virtual usado por HyperV para ejecutar la distro de Linux hacia otra partición donde tengas más espacio. Ese disco virtual es un fichero que se llama <strong>MobyLinuxVM.vhdx</strong>.</p>
<h4 id="dónde-está-mobylinuxvmvhdx">¿Dónde está MobyLinuxVM.vhdx?</h4>
<p>Puedes averiguar la ruta del fichero que hace uso Docker desde el HyperV Manager:</p>
<ul>
<li>
<p>Selecciona la máquina <strong>MobyLinuxVM</strong>.</p>
</li>
<li>
<p>Entra en Settings.</p>
</li>
<li>
<p>Selecciona HardDrive MobyLinuxVM.hdx.</p>
</li>
<li>
<p>Verás la ruta en la sección Virtual hard disk. <br />Probablemente sea: <code class="language-plaintext highlighter-rouge">C:\Users\Public\Documents\Hyper-V\Virtual hard disks</code></p>
</li>
</ul>
<p><a href="https://dagope.com/public/uploads/2019/03/docker_where_is_mobylinuxvm_hyper_v.png" target="_blank"><img src="https://dagope.com/public/uploads/2019/03/docker_where_is_mobylinuxvm_hyper_v.png" alt="Donde encontrar la ruta de MobyLinuxVM" /></a></p>
<h4 id="moviendo-vhd-y-configurando-link">Moviendo vhd y configurando Link</h4>
<ul>
<li>
<p>Lo primero, hay que parar Docker Desktop antes de continuar.
<a href="https://dagope.com/public/uploads/2019/03/docker_stop.png" target="_blank"><img src="https://dagope.com/public/uploads/2019/03/docker_stop.png" alt="Parando Docker en Windows 10" /></a></p>
</li>
<li>
<p>Ahora que ya tienes localizado el fichero vhdx, muévelo a tu nueva ruta, por ejemplo, <code class="language-plaintext highlighter-rouge">D:\HyperV-disks\MobyLinuxVM.vhdx</code></p>
</li>
<li>
<p>Elimina la carpeta <code class="language-plaintext highlighter-rouge">Virtual hard disks</code> de la ruta <code class="language-plaintext highlighter-rouge">C:\Users\Public\Documents\Hyper-V\</code></p>
</li>
<li>Abre una consola en modo administrador y ejecutas:
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mklink /J <span class="s2">"C:</span><span class="se">\U</span><span class="s2">sers</span><span class="se">\P</span><span class="s2">ublic</span><span class="se">\D</span><span class="s2">ocuments</span><span class="se">\H</span><span class="s2">yper-V</span><span class="se">\V</span><span class="s2">irtual hard disks"</span> <span class="s2">"D:</span><span class="se">\H</span><span class="s2">yperV-disks"</span>
</code></pre></div> </div>
<p><a href="https://dagope.com/public/uploads/2019/03/docker_console_create_link.png" target="_blank"><img src="https://dagope.com/public/uploads/2019/03/docker_console_create_link.png" alt="Resultado de creación de mklink" /></a></p>
</li>
<li>Inicia Docker Desktop.</li>
</ul>
<p>Docker debería ser iniciado sin problema. Vamos a comprobar los cambios descargandonos una imagen, por ejemplo:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker pull microsoft/azure-cli:2.0.59
</code></pre></div></div>
<p>Si observamos el fichero <code class="language-plaintext highlighter-rouge">D:\HyperV-disks\MobyLinuxVM.vhdx</code> veremos que está modificándose conforme la descarga avanza.</p>
<p>Y así sin desinstalar Docker ni hacer raras configuraciones optimizamos nuestro espacio en disco.</p>
<p>Happy coding!
<br />
David.</p>
<h2 id="bonus">Bonus</h2>
<p>Si quieres hacer búsquedas de ficheros en windows, te recomiendo usar el programa
<a href="https://www.voidtools.com/es-es/" target="_blank">Search Everything</a>
, útil, rápido y gratis. Aunque puedes dejar una donación ;-)</p>
Novedades de C# 8.0 al escenario2019-02-19T03:00:00+00:00https://dagope.com/2019/02/19/novedades-csharp8<p>El pasado 23 de enero tuve el placer de compartir escenario en la <a href="https://twitter.com/netcoreconf" target="_blank">@NetCoreConf</a> de Barcelona con mi amigo
<a href="https://twitter.com/fernandoescolar" target="_blank">@fernandoescolar</a>
, y exponer las novedades que nos traerá C# 8.0 en este 2019. Puede que estés pensando “¡uff! eso debió ser una buena siesta y este post de resumen apunta a lo mismo”, así que siendo conscientes de que podría producirse tal situación, decidimos aplicar la escala HotCrazy que en su día hizo Fernando con <a href="http://fernandoescolar.github.io/2016/11/16/csharp-7/" target="_blank">la versión 7.0</a>,
pero en esta ocasión sería la audiencia la encargada de evaluar y poner nota a las diferentes <em>features</em>.</p>
<p>En este artículo explicaré cada una de las novedades y veremos las puntuaciones obtenidas del público.
<!--break--></p>
<div class="note note">
<ul>
<li>El público en ningún momento ha sido coaccionado para su votación.</li>
<li>Los resultados no han sido manipulados (a pesar de los trols). </li>
<li>Y ningún gatito ha sido maltratado.</li>
</ul>
</div>
<h2 id="antes-de-empezar">Antes de empezar</h2>
<p>Es bueno conocer un poco de historia, tan importante es saber de dónde viene un lenguaje como a donde se dirige.
Si estás interesado en los orígenes de C# te recomiendo
<a href="http://fernandoescolar.github.io/2019/02/05/historia-csharp/" target="_blank">este artículo</a>
donde Fernando lo ha resumido muy bien hasta el día de hoy.</p>
<p>Por otro lado, vamos a comentar brevemente cómo va esto de la escala <em>hot-crazy</em>.</p>
<p><a href="https://youtu.be/8HO717nZD-4?t=6" target="_blank">
<img src="https://dagope.com/public/uploads/2019/02/hot_crazy.jpg" alt="escala sexy loca" title="Click para ver el video explicativo de Barnie." />
</a></p>
<p>Cada <em>feature</em> estará representada en el eje Y por lo útil que nos resulta y en el eje X lo extraño/loco/incomprensible (pon el sinónimo que quieras) que nos parece su implementación.
El objetivo es estar por encima de <em>la diagonal Vicky Mendoza</em> (x=y) para poder considerarse una buena <em>feature</em>.</p>
<p><br />¡Bueno vamos a meternos en harina!</p>
<h2 id="nullable-reference-types">Nullable reference types</h2>
<p>El propósito de esta nueva característica es ayudar con la gestión de valores “null” en nuestras variables mediante warnings.
La idea es “obligar” a marcar los tipos de referencia, p.ej: <code class="language-plaintext highlighter-rouge">string</code> o cualquier otra clase, como nulos haciendo uso del ya conocido símbolo de interrogación <code class="language-plaintext highlighter-rouge">?</code>.</p>
<p>Si ejecutamos este código sobre C#7.0 obtendríamos un error en tiempo de ejecución:</p>
<pre data-enlighter-language="csharp">
using static System.Console;
class Program
{
static void Main(string[] args)
{
string s = null;
WriteLine($"The first letter of {s} is {s[0]}"); //Se produce un error porque s es null ---> NullReferenceException
}
}
</pre>
<p>Si intentamos nular el tipo string con <code class="language-plaintext highlighter-rouge">string? s= null</code> verás que el editor te avisa de que solo puedes nular tipos que sean non-nullable, y <code class="language-plaintext highlighter-rouge">string</code> es uno de ellos.
<a href="https://dagope.com/public/uploads/2019/02/csharp8_error_string_null_c7.png" target="_blank"><img src="https://dagope.com/public/uploads/2019/02/csharp8_error_string_null_c7.png" alt="Error cuando marcamos null en tipo string en CSharp 7" /></a></p>
<p>Y aquí es donde la feature que trae C# 8.0 aparece. Con el código de arriba obtendríamos un warning en la línea que en tiempo de ejecución nos estaba fallando.
Lo bueno es que <strong>ahora podemos marcar los tipos nulables</strong> y que el compilador sepa dónde hacemos uso de ellos, mostrándonos un warning en el caso de no validar correctamente el valor null.</p>
<p>Y el código anterior con C#8.0 nos quedaría así:</p>
<pre data-enlighter-language="csharp">
using static System.Console;
class Program
{
static void Main(string[] args)
{
string? s = null;
WriteLine($"The first letter of {s} is {s[0] ?? 'null' }");
}
}
</pre>
<p>¿Qué ganamos? <strong>Evitar posibles errores en tiempo de ejecución.</strong></p>
<p>Parece que el público se ha dado cuente de ello.</p>
<blockquote>
<p><b>Valoración del público</b>:</p>
<p>Useful = <b>6.0</b></p>
<p>Crazy = <b>3.5</b></p>
</blockquote>
<h2 id="async-streams">Async streams</h2>
<p>Para facilitar el flujo de iteraciones de forma asíncrona en casos donde queremos leer datos sin bloquear la ejecución de un proceso, aparece <code class="language-plaintext highlighter-rouge">IAsyncEnumerable</code>, que no es lo mismo que hacer <code class="language-plaintext highlighter-rouge">async-await</code> de una tarea que retornará un IEnumerable.</p>
<p>Mejor explicarlo con un ejemplo de código que con tanta prosa.</p>
<pre data-enlighter-language="csharp">
static async Task Main(string[] args)
{
foreach(var data in await GetBigResultsAsync())
{
Console.WriteLine($"{DateTime.Now.ToString()} => {data}");
}
Console.ReadLine();
}
static async Task<IEnumerable<int>> GetBigResultsAsync()
{
List<int> data = new List<int>();
for (int i = 1; i <= 10; i++)
{
await Task.Delay(1000); //Simulate waiting for external API
data.Add(i);
}
return data;
}
</pre>
<p>En el código de arriba el método GetBigResultsAsync() simula que tarda 1 segundo en obtener un dato numérico.
Iterando 10 veces tardaremos 10 segundos en devolver todos los datos en nuestra list.
Está sucediendo que desde el primer segundo ya tenemos un dato que podría estar aprovechando los otros 9 segundos para ir procesándose. ¡Estamos desperdiciando un tiempo precioso de poner nuestra CPU a tope!</p>
<p>Pero se ejecuta en bloque y no puedo utilizar <code class="language-plaintext highlighter-rouge">yield</code> para devolver el dato. ¿Cómo lo arreglo?</p>
<p>Pues con el nuevo <code class="language-plaintext highlighter-rouge">IAsyncEnumerable</code> y el <code class="language-plaintext highlighter-rouge">await foreach</code> podemos transformar el código dejándolo tal que así:</p>
<pre data-enlighter-language="csharp">
static async Task Main(string[] args)
{
await foreach(var data in GetBigResultsAsync())
{
Console.WriteLine($"{DateTime.Now.ToString()} => {data}"); //Processing data
}
Console.ReadLine();
}
static async IAsyncEnumerable<int> GetBigResultsAsync()
{
for (int i = 1; i <= 10; i++)
{
await Task.Delay(1000); //Simulate waiting for external API
yield return i;
}
}
</pre>
<p>¿Te cuesta verlo? Te ayudará la siguiente animación que compara la ejecución de ambos códigos.</p>
<p><a href="https://dagope.com/public/uploads/2019/02/demo_csharpo8_async_streams.gif" target="_blank"><img src="https://dagope.com/public/uploads/2019/02/demo_csharpo8_async_streams.gif" alt="Async Streams new feature Csharp 8 " /></a></p>
<p>Si quieres ejecutarlo tú mismo, te dejo este ejemplo en mi GitHub:
<br /><a href="https://github.com/dagope/chsarp8_async_streams" target="_blank">https://github.com/dagope/chsarp8_async_streams</a></p>
<blockquote>
<p><b>Valoración del público</b>:</p>
<p>Useful = <b>7.0</b></p>
<p>Crazy = <b>4.0</b></p>
</blockquote>
<h2 id="rangos-e-índices">Rangos e Índices</h2>
<p>Los tipos <code class="language-plaintext highlighter-rouge">Range</code> e <code class="language-plaintext highlighter-rouge">Index</code> llegan con la finalidad de ayudarnos en el manejo de una colección.
Se aplican sobre un array o cualquier objeto que cumpla con la interfaz <code class="language-plaintext highlighter-rouge">IEnumerator</code> y se declaran entre corchetes los índices de inicio y fin separados por dos puntos seguidos.
El índice contiene el valor que nos indica la posición del array a delimitar.</p>
<p>Su sintaxis sería:</p>
<pre data-enlighter-language="csharp">
Index indexStart = 1;
Index indexEnd = ^5;
Range range = people[2..^5];
Range range = people[indexStart..indexEnd])
</pre>
<p>Vamos a ver cómo funcionan con el código:</p>
<pre data-enlighter-language="csharp">
var people = new string[] {
"Elena", "Armando", "Dolores", "Aitor",
"Leia", "Vader", "Yoda", "Skywalker"
};
foreach (var p in people[0..3]) Console.Write($"{p}, "); // Elena, Armando, Dolores, Aitor,
foreach (var p in people[0..^5]) Console.Write($"{p}, "); // Elena, Armando, Dolores, Aitor,
foreach (var p in people[^4]) Console.Write($"{p}, "); // Leia, Vader, Yoda, Skywalker,
foreach (var p in people[6..]) Console.Write($"{p}, "); // Yoda, Skywalker,
foreach (var p in people[..]) Console.Write($"{p}, "); // Elena, Armando, Dolores, Aitor, Leia, Vader, Yoda, Skywalker,
</pre>
<p>Vemos que aparece en acción un nuevo símbolo, el <code class="language-plaintext highlighter-rouge">^</code> circunflejo:
<br />El uso del <code class="language-plaintext highlighter-rouge">^</code> circunflejo nos indica que nuestro valor del índice <strong>comienza desde el final</strong>.
Si pensamos en que se podría usar el <code class="language-plaintext highlighter-rouge">^0</code>, en realidad estaríamos intentando acceder al elemento siguiente al último y como no hay no puedes llegar a él.
Para coger elementos desde el final hacia el principio, <strong>debemos empezar a contar los índices desde 1</strong> y no desde 0.</p>
<p>Viendo el ejemplo sacamos las siguientes reglas para los índices:</p>
<ul>
<li>Puedo traerme un rango indicando la posición inicio y fin tal que <code class="language-plaintext highlighter-rouge">people[0..3]</code></li>
<li>Si quiero limitar por el final puedo omitir el índice de inicio tal que <code class="language-plaintext highlighter-rouge">people[..3]</code></li>
<li>Si quiero limitar por el principio también puedo decir que comience desde <em>Length - posición</em>, es decir <code class="language-plaintext highlighter-rouge">people[^4]</code></li>
<li>Si quiero limitar por el principio puedo omitir el índice final tal que <code class="language-plaintext highlighter-rouge">people[6..]</code></li>
<li>La ausencia de alguno de los índices en el rango se tomará como el inicio que delimita. Por lo tanto si omito el de inicio contará desde 0, si omito el de final tomará la última posición, sería el <code class="language-plaintext highlighter-rouge">people.Length</code></li>
<li>Puedo omitir cualquiera de los índices y el rango tendría todos los valores, siendo como el valor normal entonces:
<br /><code class="language-plaintext highlighter-rouge">people[..].Length == people.Length</code>
<br />Curioso, no?</li>
</ul>
<blockquote>
<p><b>Valoración del público</b>:</p>
<p>Useful = <b>5.0</b></p>
<p>Crazy = <b>6.5</b></p>
</blockquote>
<p>Parece que no caló muy bien el nuevo símbolo ^ y la gente lo encontró algo más Crazy que Useful.</p>
<h2 id="recursive-patterns">Recursive patterns</h2>
<p>C# cada vez está cogiendo más características de los lenguajes funcionales y esta es una de ellas. ¿te suena
<a href="https://www.campusmvp.es/recursos/post/Pattern-matching-en-lenguajes-de-programacion-funcionales.aspx" target="_blank"><em>Pattern Matching</em></a>
? Entonces no te costará entender esto.</p>
<p>Partiendo de una clase definida <em>Student</em>:</p>
<pre data-enlighter-language="csharp">
class Student
{
public string Name { get; set; }
public bool Graduated { get; set; }
}
</pre>
<p>Si tenemos un array de objetos definidos como:</p>
<pre data-enlighter-language="csharp">
var People = new object[] {
new Student(){Name = "Leia", Graduated= false},
new Student(){Name = "Yoda", Graduated= true},
new Student(){Name = "Skywalker", Graduated= false},
}
</pre>
<p>En C#7 para obtener los nombres de los no graduados haríamos un <code class="language-plaintext highlighter-rouge">foreach</code> con una condición <code class="language-plaintext highlighter-rouge">if</code> y devolviendo el nombre del objeto que cumpliese la condición.</p>
<p>Sí, también podríamos usar Linq pero añadimos una dependencia a nuestra clase y tampoco es la finalidad del ejemplo.</p>
<p>El código sería:</p>
<pre data-enlighter-language="csharp">
IEnumerable<string> GetNameStudentsNotGraduated()
{
foreach (var p in People)
{
if (p is Student && !p.Graduated)
{
string name = p.Name;
yield return name;
}
}
}
</pre>
<p>Y en C#8 lo podemos transformar hacia:</p>
<pre data-enlighter-language="csharp">
IEnumerable<string> GetNameStudentsNotGraduated()
{
foreach (var p in People)
{
if (p is Student { Graduated: false, Name: string name })
yield return name;
}
}
</pre>
<p>Observamos que simplifica bastante la condición del <code class="language-plaintext highlighter-rouge">if</code> si pensamos en el filtrado que queremos hacer de nuestra colección. Este debe cumplir que sea un objeto de tipo <code class="language-plaintext highlighter-rouge">Student</code> y con <code class="language-plaintext highlighter-rouge">Graduated == false</code>. Además, la propiedad <code class="language-plaintext highlighter-rouge">Name</code> la asigne a una variable <code class="language-plaintext highlighter-rouge">string name</code> que usaré para agregarla con el <code class="language-plaintext highlighter-rouge">yield</code> a mi colección de nombres que devuelve mi función.</p>
<p>En mi opinión creo que es bastante más útil que <em>Crazy</em>. Y dada las valoraciones obtenidas parece que la gente optó más por el Crazy.</p>
<blockquote>
<p><b>Valoración del público</b>:</p>
<p>Useful = <b>5.0</b></p>
<p>Crazy = <b>7.5</b></p>
</blockquote>
<h2 id="switch-expressions">Switch expressions</h2>
<p>Esta característica viene a elevar los bloques <code class="language-plaintext highlighter-rouge">switch</code> a su máxima potencia tras haberse metido una buena fumada.</p>
<ol>
<li>Ahora podremos olvidarnos del <code class="language-plaintext highlighter-rouge">case</code> y en su lugar poner la “condición” de varias maneras:
<ul>
<li>Puedo seguir usando mi palabra <code class="language-plaintext highlighter-rouge">when</code>, esto ya viene de C#7.</li>
<li>Y ¿por qué no un <em>Pattern matching</em> que acabamos de ver antes? Pues sí, puedes y te olvidas del uso de <code class="language-plaintext highlighter-rouge">when</code>.</li>
<li>Y ¿por qué no haciendo una deconstruccion del objeto? Pues sí, también puedes fumarte eso.</li>
</ul>
</li>
</ol>
<pre data-enlighter-language="csharp">
return o switch
{
Point p when p.X == 5 && p.Y == 5 => "Hight 5", //Disponible en C# 7.0
Point { X: 0, Y: 0 } => "origin", //Por pattern matching
Point { X: var x, Y: var y } => $"{x}, {y}", //Por pattern matching
Point(-5, -5) => "Low", //Por deconstruccion
Point(var x, var y) => $"{x}, {y}", //Por deconstruccion
_ => "unknown"
};
</pre>
<p><small><em>Ojo: no copies este código tal cual, es probable que la combinación de las cláusulas falle, se muestra a modo de ejemplo</em></small></p>
<ul>
<li>Olvidarnos del <code class="language-plaintext highlighter-rouge">default</code> y poner <code class="language-plaintext highlighter-rouge">_</code> ya lo tenemos en C# 7.0, no viene mal recordarlo porque escribir 7 caracteres a 1 es la vagancia máxima.</li>
</ul>
<ol>
<li>El cuerpo de cada función lo podemos expresar en una misma tras el <code class="language-plaintext highlighter-rouge">=></code>, y de paso también olvidarnos del <code class="language-plaintext highlighter-rouge">break;</code> al final.
Esto lo compro, me gusta.</li>
</ol>
<pre data-enlighter-language="csharp">
var area = figure switch
{
Rectangle r => r.Width * r.Height,
Circle c => Math.PI * c.Radius * c.Radius,
_ => 0
};
</pre>
<p>Bien, pues la imaginación es tu límite con lo que puedes hacer dentro de un <code class="language-plaintext highlighter-rouge">switch</code>.</p>
<blockquote>
<p><b>Valoración</b>:</p>
<p>Useful = <b>4.0</b></p>
<p>Crazy = <b>8.0</b></p>
</blockquote>
<p>Era de esperar.</p>
<h2 id="implicit-constructors">Implicit constructors</h2>
<p>Hacía falta algo de
<a href="https://es.wikipedia.org/wiki/Az%C3%BAcar_sint%C3%A1ctico" target="_blank"><em>azúcar</em></a>
para digerir bien lo anterior y aquí llega un poco.
Esta característica cumple con la ley de los vagos de no escribir lo que es evidente.</p>
<p>Si tenemos un array de Personas y queremos inicializarlo,
¿Por qué tengo que poner un <code class="language-plaintext highlighter-rouge">new Person(...)</code> a cada elemento si ya el array está tipado?
Pues dicho y hecho, ahora podremos omitirlo porque no tiene sentido:</p>
<pre data-enlighter-language="csharp">
Person[] people =
{
new ("Elena", "Nito", "del Bosque"),
new ("Armando", "Bronca", "Segura"),
new ("Dolores", "Cabeza", "Baja"),
new ("Aitor", "Tilla", "del Bosque"),
};
</pre>
<blockquote>
<p><b>Valoración</b>:</p>
<p>Useful = <b>7.0</b></p>
<p>Crazy = <b>2.0</b></p>
</blockquote>
<p>Esto ha gustado ;)</p>
<h2 id="using-declaration">Using declaration</h2>
<p>Seguro que estás acostumbrado a utilizar los bloques <code class="language-plaintext highlighter-rouge">using</code> para abrir una conexión a base de datos, o en un <code class="language-plaintext highlighter-rouge">Stream</code> para la lectura del fichero, etc…
en todas esas ocasiones nos aseguramos que nuestro objeto Disposable ejecute su método Dispose() al finalizar el código que engloba el bloque <code class="language-plaintext highlighter-rouge">using</code>.
Algo cómo:</p>
<pre data-enlighter-language="csharp">
static void Main(string[] args)
{
using (var disposable = CreateDisposable(args))
{
...
} // disposable is disposed here
}
</pre>
<p>Con esta nueva característica ahora podremos indicar el ámbito del using sobre una variable, sin necesidad de encapsular el código dentro de un bloque <code class="language-plaintext highlighter-rouge">using</code>.
El método Dispose() del objeto se ejecutará cuando su ámbito finalice.</p>
<pre data-enlighter-language="csharp">
static void Main(string[] args)
{
using var disposable = CreateDisposable(args);
...
} // disposable is disposed here
</pre>
<p>Existen algunas limitaciones sobre el uso:</p>
<ul>
<li>No puedes reasignar una variable</li>
</ul>
<pre data-enlighter-language="csharp">
using var stream = file1.Open();
stream = file2.Open();
</pre>
<ul>
<li>No se puede enlazar a una variable de salida</li>
</ul>
<pre data-enlighter-language="csharp">
if (myCustomMethod(
out using var stream, // Error
ref size)
)
{
// our code
}
</pre>
<blockquote>
<p><b>Valoración del público</b>:</p>
<p>Useful = <b>6.0</b></p>
<p>Crazy = <b>2.5</b></p>
</blockquote>
<p>Parece que tuvo buena aceptación.</p>
<h2 id="default-interfaces">Default interfaces</h2>
<p>Bueno, hemos llegado a la característica de la polémica. Como si de un debate político se tratase, existen posturas de todos los colores, y se han escrito muchas opiniones al respecto. Se lleva años hablando del tema de si las Interfaces deberían implementar código.</p>
<p>Al final ha llegado, y esto es lo que podemos hacer.</p>
<pre data-enlighter-language="csharp">
interface ILogger
{
void Log(LogLevel level, string message);
void Log(Exception ex) => Log(LogLevel.Error, ex.ToString()); // New overload
}
class ConsoleLogger : ILogger
{
public void Log(LogLevel level, string message) { ... }
// Log(Exception) gets default implementation
}
</pre>
<p>En mi opinión, de siempre una interfaz se ha definido como un “contrato” que todo objeto debe cumplir. Si ese “contrato” no lo cumples en su totalidad no es un objeto válido. Bien, pues que ahora una <em>interface</em> pueda incluir código por defecto es como exponer un “contrato” con letra pequeña si no lo cumples. Una letra pequeña que nadie lee o se nos olvida leer en profundidad.</p>
<p>Parece ser que la influencia de lenguajes como Java y Switch a través de Xamarin han acabado trayendo esta característica y nos deja a los programadores la responsabilidad de usarla <del>correctamente</del>.</p>
<blockquote>
<p><b>Valoración del público</b>:</p>
<p>Useful = <b>4.5</b></p>
<p>Crazy = <b>5.5</b></p>
</blockquote>
<p>Había un polizón javero entre el público que seguro moderó los resultados ;-P</p>
<h2 id="conclusiones">Conclusiones</h2>
<p>Pues hasta aquí todas las características. Hemos publicado esta <a href="https://netcoreapp.azurewebsites.net/">página web con las estadísticas de las votaciones</a>, pero como no creo que la tengamos para siempre online, vamos a dejaros unas capturas a continuación:</p>
<p>En etos gráficos podemos visualizar las puntuación de cada feature con <em>la diagonal Vicky Mendoza</em>, como dijimos todo lo que sea por encima era buena señal:
<a href="https://dagope.com/public/uploads/2019/02/bcn_netcoreconf_results_crazyhot_1.png" target="_blank"><img src="https://dagope.com/public/uploads/2019/02/bcn_netcoreconf_results_crazyhot_1.png" alt="Grafico con diagonal Vicky Mendoza" /></a></p>
<p><a href="https://dagope.com/public/uploads/2019/02/bcn_netcoreconf_results_crazyhot_2.png" target="_blank"><img src="https://dagope.com/public/uploads/2019/02/bcn_netcoreconf_results_crazyhot_2.png" alt="Grafico con resultados positivos negativos según diagonal Vicky Mendoza" /></a></p>
<p>Estos son los datos, tuyas son las conclusiones.</p>
<h3 id="bonus-y-referencias">Bonus y referencias:</h3>
<p>Este artículo tiene su hermano mellizo en
<a href="http://fernandoescolar.github.io/2019/02/19/csharp-8/" target="_blank">este otro</a>
escrito por Fernando en su blog. Te recomiendo que lo leas porque aunque se parecen, se complementan.</p>
<p>Y como sabrás, Visual Studio 2019 está al caer y con su presentación llegarán todas estas features de C# 8.0, si algo cambia (cosa que dudo) lo veremos el próximo 2 de abril con la presentación que puedes seguir online.</p>
<blockquote class="twitter-tweet" data-lang="es"><p lang="en" dir="ltr">The Visual Studio 2019 Launch Event is Coming April 2nd, 9:00am PT.<br /><br />Whether you're a C#, C++, or Python dev & target the web, desktop, or cloud, we'll have demos & sessions for all the new goodies coming to <a href="https://twitter.com/hashtag/VS2019?src=hash&ref_src=twsrc%5Etfw">#VS2019</a>.<br /><br />Save the date & join us: <a href="https://t.co/uGthsbkv7h">https://t.co/uGthsbkv7h</a> <a href="https://t.co/LsgZjglrzE">pic.twitter.com/LsgZjglrzE</a></p>— Visual Studio (@VisualStudio) <a href="https://twitter.com/VisualStudio/status/1096095478628917254?ref_src=twsrc%5Etfw">14 de febrero de 2019</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>Y si quieres profundizar más te dejo unas referencias útiles:
<br /><a href="https://blogs.msdn.microsoft.com/dotnet/2018/11/12/building-c-8-0/" target="_blank">https://blogs.msdn.microsoft.com/dotnet/2018/11/12/building-c-8-0/</a>
<br /><a href="https://blogs.msdn.microsoft.com/dotnet/2019/01/24/do-more-with-patterns-in-c-8-0/" target="_blank">https://blogs.msdn.microsoft.com/dotnet/2019/01/24/do-more-with-patterns-in-c-8-0/</a>
<br /><a href="https://vcsjones.com/2019/01/30/csharp-8-using-declarations/" target="_blank">https://vcsjones.com/2019/01/30/csharp-8-using-declarations/</a>
<br /><a href="https://dotnetcoretutorials.com/2019/01/09/iasyncenumerable-in-c-8/" target="_blank">https://dotnetcoretutorials.com/2019/01/09/iasyncenumerable-in-c-8/</a></p>
<h3 id="bonus-barcelona-netcoreconf">Bonus Barcelona NetCoreConf:</h3>
<p>Fue un gran evento y creo que nos divertimos dejando la vara de medir al público. Como un gran poder conlleva una gran responsabilidad, decidimos premiar la implicación del público con un obsequio al más <em>Crazy</em> y el más <em>Useful</em>. En secreto diseñamos e imprimimos en 3D dos premios de los que no existen réplicas (de momento). Espero que sus dueños sepan valorarlo y los mantengan a buen recaudo.</p>
<iframe id="vs_iframe" src="https://www.viewstl.com/?embedded&url=https://www.thingiverse.com/download:6080355&color=white&bgcolor=gray&shading=flat&rotation=yes&orientation=bottom" style="border:0;margin:0;width:100%;height:400px;"></iframe>
<p>El diseño lo he dejado <a href="https://www.thingiverse.com/thing:3434291" target="_blank">aquí publicado.</a></p>
<p>Y el momento de la entrega de “premios”:</p>
<blockquote class="twitter-tweet" data-lang="es"><p lang="es" dir="ltr">Y aquí la foto finish de la Hot Crazy C# junto con <a href="https://twitter.com/fernandoescolar?ref_src=twsrc%5Etfw">@fernandoescolar</a>: la entrega de nuestro reconocimiento (único y exclusivo) al +Crazy y al +Usefull de entre los votos del público. <a href="https://twitter.com/hashtag/netcoreconf?src=hash&ref_src=twsrc%5Etfw">#netcoreconf</a> <a href="https://twitter.com/hashtag/lohemospasadogenial?src=hash&ref_src=twsrc%5Etfw">#lohemospasadogenial</a> <a href="https://t.co/qgjyzJmLNn">pic.twitter.com/qgjyzJmLNn</a></p>— David Gonzalo (@dagope) <a href="https://twitter.com/dagope/status/1089629650664521730?ref_src=twsrc%5Etfw">27 de enero de 2019</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>Agradecer a la organización por haber tenido la oportunidad de participar en un gran evento de comunidad.
Nos vemos en otra.</p>
<p>Happy coding!
<br />
David.</p>
Performance test fallan sobre Azure Web Apps2018-12-19T09:00:56+00:00https://dagope.com/2018/12/19/error-performan-test-azure-web-app<p>El otro día realizando unas pruebas de rendimiento sobre una api REST desplegada en una Azure Web App, me encontré que todas las request del test habían fallado. Que raro, porque si ejecuto una de ellas desde el navegador funciona, en cambio todas habian sido rechazadas con el mismo error:</p>
<p><span style="color:red"><em>An existing connection was forcibly closed by the remote host.</em></span></p>
<p>Vamos a ver como reproducir el problema y comentamos las soluciones.
<!--break--></p>
<h1 id="reproduciendo-el-problema">Reproduciendo el problema:</h1>
<p>Desde Visual Studio tengo generado un fichero .webtest, para poder realizar diferentes llamadas sobre los endpoints de mi API rest.</p>
<p>Algo normal hasta aquí. El problema me lo encontre por dos vías.</p>
<ul>
<li>
<p>Ejecutando el fichero desde la opción <em>Performance Test</em> del App Service en Azure.
<a href="https://dagope.com/public/uploads/2018/12/error_performance_test.png" target="_blank"><img src="https://dagope.com/public/uploads/2018/12/error_performance_test.png" alt="Listado de errores de la ejecución del performance test desde el portal de Azure" /></a></p>
</li>
<li>
<p>Ejecutando desde Visual Studio apuntando hacia la web API desplegada en Azure.
<a href="https://dagope.com/public/uploads/2018/12/error_from_visual_studio.png" target="_blank"><img src="https://dagope.com/public/uploads/2018/12/error_from_visual_studio.png" alt="Performance test, error ejecutando desde el Visual Studio" /></a></p>
</li>
</ul>
<p>Desde VSTS, el ahora llamado Azure DevOps, también obtendremos el mismo error.</p>
<h2 id="la-causa">La causa</h2>
<p>El problema reside la configuración de seguridad TSL que tengamos establecida. Si nuestra Web App tiene configurado una versión superior a la 1.0 fallará y nos dará el error <span style="color:red"><em>An existing connection was forcibly closed by the remote host.</em></span>
<br />Te comento dos posibles soluciones:</p>
<h1 id="soluciones">Soluciones</h1>
<h2 id="solución-1">Solución 1:</h2>
<p>Podemos establecer la <em>Minimum TLS Version</em> como 1.0 y nos funcionará tanto si ejecutas el test desde Visual Studio como desde Azure. De hecho, esta es la opción para que funcione nuestros <em>Performance Test</em> ejecutados desde las opciones de Azure WebApp Azure y Azure DevOps.
<a href="https://dagope.com/public/uploads/2018/12/change_tsl_azure.png" target="_blank"><img src="https://dagope.com/public/uploads/2018/12/change_tsl_azure.png" alt="Cambiando TSL en Web App Azure" /></a></p>
<div class="note note">
<span class="notetitle">
Nota:
</span>
Cuando hagas el cambio dale un marguen de tiempo, aunque Azure notifique que ya está actualizado puede que ejecutes y te siga dando error. Si lo ves necesario reinicia la web app.
</div>
<h2 id="solución-2">Solución 2:</h2>
<p>Si no queremos cambiar nuestro TSL Version, a día de hoy desde Azure no tenemos una solución. Pero podemos cambiar nuestro fichero .webtest para que la ejecución nos funcione desde Visual Studio.
Para ello tenemos que crear un <em>Web Test Plug-ins</em>. Agregamos el siguiente fichero de código al proyecto:
<script src="https://gist.github.com/dagope/a8063404874411314bc7c1f874d01b7d.js"></script></p>
<p>Después añadimos el plugin en nuestro webtest:
<a href="https://dagope.com/public/uploads/2018/12/insert_Web_plugin_test.png" target="_blank"><img src="https://dagope.com/public/uploads/2018/12/insert_Web_plugin_test.png" alt="Agregando Web Test Plugin" /></a>
En la ventana nos debería aparecer nuestro plug-in <strong>TSLPlugin</strong>, sino aparece compila y vuelve a mirar.</p>
<p>Lo seleccionamos, aceptamos y veremos que nos aparecerá una nueva carpeta llamada <strong>Web Test Plug-ins</strong> que contendrá nuestro TSLPlugin.</p>
<p>Ahora si ejecutamos el web test todo debería funcionar perfectamente.</p>
<div class="note note">
<span class="notetitle">
Nota:
</span>
Si estás pensando en utilizar el fichero .webtest con el plugin establecido sobre Azure obtendrás un error que te dirá que Azure no tiene soporte para Plugins de web-test. Buen intento pero a día de hoy es lo que hay.
</div>
<p><br />Saludos!
<br />David</p>
Aplicando las migraciones en EF Core2018-11-28T08:24:00+00:00https://dagope.com/2018/11/28/efcore-apply-migrations<p>Continuando con las migraciones de EF Core del <a href="https://dagope.com/2018/11/20/efcore-migrations">post anterior</a>, resulta muy útil saber cómo aplicarlas de forma “automática”.
Durante el desarrollo de un proyecto vamos creando migraciones y ejecutándolas con el comando ya conocido <code class="language-plaintext highlighter-rouge">update-database</code>.
Como miembro del equipo puedes estar desarrollando una funcionalidad en la que desconozcas cuando otro de tus compañeros ha necesitado crear una nueva migración, entonces ¿qué pasará cuando nos descarguemos los cambios del compañero? <!--break--> Pues que no se aplicarán los cambios en nuestra base de datos hasta que lo hagamos manualmente. Esto es un poco engorroso y puede producir errores en tiempo de ejecución inesperados.</p>
<p>Podemos evitar este problema y hacer que el resto del equipo de desarrollo se despreocupe de lanzar el comando update de una tarea que desconoce.</p>
<h2 id="aplicar-migraciones-al-arranque-del-proyecto">Aplicar migraciones al arranque del proyecto</h2>
<p>Si queremos asegurarnos de que la BD está con las últimas migraciones creadas, podemos configurar que se apliquen las migraciones en tiempo de ejecución de nuestra aplicación.
El siguiente método de ejemplo podemos incluirlo dentro de nuestra Startup.cs y llamarlo desde <code class="language-plaintext highlighter-rouge">Configure(IApplicationBuilder app, IHostingEnvironment env)</code></p>
<div class="language-c# highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">private</span> <span class="k">void</span> <span class="nf">ApplyLastMigrationsEF</span><span class="p">(</span><span class="n">IApplicationBuilder</span> <span class="n">app</span><span class="p">)</span>
<span class="p">{</span>
<span class="kt">var</span> <span class="n">scopeFactory</span> <span class="p">=</span> <span class="n">app</span><span class="p">.</span><span class="n">ApplicationServices</span><span class="p">.</span><span class="n">GetRequiredService</span><span class="p"><</span><span class="n">IServiceScopeFactory</span><span class="p">>();</span>
<span class="k">using</span> <span class="p">(</span><span class="kt">var</span> <span class="n">scope</span> <span class="p">=</span> <span class="n">scopeFactory</span><span class="p">.</span><span class="nf">CreateScope</span><span class="p">())</span>
<span class="p">{</span>
<span class="kt">var</span> <span class="n">dbContext</span> <span class="p">=</span> <span class="n">scope</span><span class="p">.</span><span class="n">ServiceProvider</span><span class="p">.</span><span class="n">GetRequiredService</span><span class="p"><</span><span class="n">SampleDBContext</span><span class="p">>();</span>
<span class="n">dbContext</span><span class="p">.</span><span class="n">Database</span><span class="p">.</span><span class="nf">Migrate</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Si te fijas en el código, únicamente tendremos que obtener el contexto y ejecutar el método <code class="language-plaintext highlighter-rouge">Migrate()</code>.
De esta manera migramos nuestra BD al estado de ejecución de nuestra aplicación.</p>
<p>Aquí debajo pongo un ejemplo de lo que podría ser la clase Startup de una API Rest en NetCore.
<script src="https://gist.github.com/dagope/01ce0c4263c3060c7a7458fe7a9e2666.js"></script></p>
<p>Happy coding!
<br />
David.</p>
Entity Framework Core y sus migraciones.2018-11-20T14:00:56+00:00https://dagope.com/2018/11/20/efcore-migrations<p><img src="https://dagope.com/public/uploads/2018/11/efcore.png" alt="EFCore logo" /></p>
<p>En esta ocasión voy a comentar las tareas del día a día que podemos realizar en las Migrations de Entity Framework Core (EFCore), y saber qué hacer con cada uno de los comandos.
Difieren un poco de las usadas en EF6, que ya comenté en un artículo anterior y puedes leer <a href="https://dagope.com/2018/09/27/EF6-migraciones-comandos">aquí</a>.
<!--break--></p>
<p>Esto no pretende ser una guía de EF Core, por lo que se recomienda tener conocimientos sobre el funcionamiento del mismo y de la creación de contextos. Para ello que mejor que un tutorial con toda la info:
<a href="http://www.entityframeworktutorial.net/efcore/entity-framework-core.aspx" target="_blank">http://www.entityframeworktutorial.net/efcore/entity-framework-core.aspx</a>.</p>
<p>Para las tareas que voy a explicar es suficiente con el uso de los comandos desde la PMC (<em>Package Manage Console</em>). También es bueno que conozcas los comandos desde la CLI <em>(Command Line Interface)</em>, hacen uso del tooling de dotnet y tienen más opciones de manejo.</p>
<p>Vamos con el contenido:</p>
<ul>
<li><a href="#migraciones-automáticas-en-ef-core">Migraciones Automáticas en EF Core</a></li>
<li><a href="#habilitar-migraciones-en-una-bd-y-viendo-el-detalle">Habilitar migraciones en una BD y viendo el detalle</a></li>
<li><a href="#como-crear-nuevas-migraciones">Como crear nuevas migraciones</a></li>
<li><a href="#editar-la-migración-creada">Editar la migración creada</a></li>
<li><a href="#crear-una-migración-vacía">Crear una migración vacía</a>
<ul>
<li><a href="#cómo-agregar-mi-script-sql-a-una-migración">Cómo agregar mi script SQL a una migración</a></li>
</ul>
</li>
<li><a href="#migrar-a-una-versión-concreta-downgrade">Migrar a una versión concreta (Downgrade)</a></li>
<li><a href="#eliminar-una-migración">Eliminar una migración</a></li>
<li><a href="#listado-de-migraciones-en-nuestra-bd">Listado de migraciones en nuestra BD</a></li>
</ul>
<h2 id="migraciones-automáticas-en-ef-core">Migraciones Automáticas en EF Core</h2>
<p>En el <a href="https://dagope.com/2018/09/27/EF6-migraciones-comandos/#migraciones-automáticas-y-cuándo-usarlas">post de EF6</a> os aconsejaba no utilizar las migraciones automáticas y tener más control sobre las mismas.
Pues bien, si seguís ese consejo vais por buen camino a la hora de migrar hacia EFCore ya que esta <em>“feature”</em> no ha sido incluida y tampoco se la espera. Como bien explica Diego Vega en este <a href="https://github.com/aspnet/EntityFrameworkCore/issues/6214#issuecomment-239519498" target="_blank">issue de github</a>, <em>la experiencia de migraciones basadas en código han mostrado ser más manejables</em>.</p>
<p>Por lo tanto, <strong>las migraciones automáticas son cosa del pasado</strong>. Si no las usabas en EF6 tienes camino aprendido ;-)</p>
<h2 id="habilitar-migraciones-en-una-bd-y-viendo-el-detalle">Habilitar migraciones en una BD y viendo el detalle</h2>
<p>Las migraciones son parte del cómo funciona EF Core y no es necesario ejecutar ningún comando ni realizar nada especial como se hace con EF6. Obviamente, sus paquetes nuget tienen que ser referenciados en el proyecto.
<br />Cuando creas la primera migración con el commanto <code class="language-plaintext highlighter-rouge">Add-migration</code> obtendrás una nueva carpeta <em>Migrations</em> y 3 ficheros:</p>
<ul>
<li>Carpeta <strong>Migrations</strong>:
<br />Aquí se almacenarán todos los ficheros con el código de las migraciones que vayas creando durante el desarrollo de tu proyecto.</li>
<li><strong>yyyymmdddhhMMss_MigrationName.cs</strong>
<br /> Esta clase contiene los métodos Up() y Down(). En su interior encontrarás el código de creación/borrado de tablas/columnas que el scaffolding detectó en los cambios de tus entidades.</li>
<li><strong>yyyymmdddhhMMss_MigrationName.Designer.cs</strong>
<br /> Si ojeas verás que es el snapshot de tu esquema en la migración que has creado.</li>
<li><strong>ContextNameModelSnapshot.cs</strong>
<br /> Este fichero contiene el snapshot de los cambios de tu migración. Sirve para que detectar tus cambios cuando vas creando migraciones.</li>
</ul>
<h2 id="como-crear-nuevas-migraciones">Como crear nuevas migraciones</h2>
<p>Cuando tengamos listos nuestros cambios ejecutamos el siguiente comando para crear una nueva migración.</p>
<div class="language-bat highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">Add</span><span class="na">-Migration -Name </span><span class="kd">NombreDeMigracion</span> <span class="na">-Project </span><span class="s2">"App1.Data"</span> <span class="na">-context </span><span class="kd">SampleDbContext</span>
</code></pre></div></div>
<p><br />Esto crea una clase en la carpeta Migrations con el formato <em>yyyyMMdd_NombreDeMigracion.cs</em>
<br />Aquí podemos personalizar lo que nos interese, como por ejemplo inicializar el valor de los campos, etc… Pero ten en cuenta que las personalizaciones son únicas, es decir, si eliminas la migración y la vuelves a crear, tendrás que codificarlas de nuevo.
<br />Y como siempre, para establecer los cambios ejecutamos el comando.</p>
<div class="language-bat highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">Update</span><span class="na">-Database -Project </span><span class="s2">"App1.Data"</span> <span class="na">-verbose
</span></code></pre></div></div>
<h2 id="editar-la-migración-creada">Editar la migración creada</h2>
<p>Cuando queremos incluir más cambios de nuestro modelo en una migración ya creada:</p>
<ul>
<li>Eliminar la última migración.
<div class="language-bat highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">remove</span><span class="na">-migration -context </span><span class="kd">SampleDbContext</span>
</code></pre></div> </div>
</li>
<li>Creamos de nuevo la migración. En este punto podríamos cambiar el nombre si lo deseamos.
<div class="language-bat highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">Add</span><span class="na">-Migration -Name </span><span class="kd">NombreDeMigracion</span> <span class="na">-Project </span><span class="s2">"App1.Data"</span> <span class="na">-context </span><span class="kd">SampleDbContext</span>
</code></pre></div> </div>
</li>
</ul>
<p>Recuerda que si tienes algún script sql o modificación que hayas realizado en la migración debes hacer una copia de ese código para añadirlo en el último paso.</p>
<h2 id="crear-una-migración-vacía">Crear una migración vacía</h2>
<p>Hay veces que podemos tener cambios en objetos de nuestra BD que no interfieren directamente sobre nuestro contexto y el scaffolding no lo detecta. Cambios por ejemplo en un <em>store procedure</em>, function, views, etc…
<br />Para tener controlados en nuestro historial de migraciones este tipo de cambios, tenemos que generar una migración vacía.
<br />¿Cómo se hace?
<br />Exactamente de la misma manera que creando una migración normal.</p>
<div class="language-bat highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">Add</span><span class="na">-Migration -Name </span><span class="kd">ChangesInStoreProcedures</span> <span class="na">-Project </span><span class="s2">"App1.Data"</span> <span class="na">-context </span><span class="kd">SampleDbContext</span>
</code></pre></div></div>
<p>Esto nos genera nuestra migración con la diferencia de que los métodos Up() y Down() están vacíos.
Si te fijas el fichero <em>SampleDbContextModelSnapshot.cs</em> no ha cambiado porque no se han detectado cambios.
Si utilizas git u otro control de versiones en tu proyecto es fácil observar los ficheros que se han creado y modificados, incluso comparar con versiones anteriores para observar que se hizo.</p>
<p>No olvidemos que ambos métodos deben ser implementados y probados. Es un error muy común centrarse solo en el código para el Up() olvidarse del Down(). Es tú responsabilidad que la migración pueda ser ejecutada en cualquier dirección. Así que codifiquemos ambos, ejecutemos el comando para upgrade y después para downgrade. Así aseguramos que no la líamos.</p>
<p><strong>Recuerda</strong>: tan importante es el método Down() como el Up().</p>
<h3 id="cómo-agregar-mi-script-sql-a-una-migración">Cómo agregar mi script SQL a una migración</h3>
<p>En los métodos Up() y Down() tienes el parámetro migrationBuilder que tiene un método SQL() para pasarle un string que ejecutar.</p>
<div class="language-c# highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">protected</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">Up</span><span class="p">(</span><span class="n">MigrationBuilder</span> <span class="n">migrationBuilder</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">migrationBuilder</span><span class="p">.</span><span class="nf">Sql</span><span class="p">(</span><span class="s">@"Create Function fn_MyFunction() [...] "</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="migrar-a-una-versión-concreta-downgrade">Migrar a una versión concreta (Downgrade)</h2>
<p>Para migrar la BD a una situación en concreto debe indicarse el parámetro <strong>-Migration</strong> al comando update-database.
Tal que así:</p>
<div class="language-bat highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">Update</span><span class="na">-Database </span>–Migration <span class="kd">NombreDeMigracion</span> <span class="na">-Project </span><span class="s2">"App1.Data"</span> <span class="na">-context </span><span class="kd">SampleDbContext</span>
</code></pre></div></div>
<p><br />Para volver al inicio del todo y a una base de datos “vacía” (sin ninguna migración aplicada):</p>
<div class="language-bat highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">Update</span><span class="na">-Database -Migration </span><span class="m">0</span> <span class="na">-Project </span><span class="s2">"App1.Data"</span> <span class="na">-Context </span><span class="kd">ClientDbContext</span>
</code></pre></div></div>
<h2 id="eliminar-una-migración">Eliminar una migración</h2>
<p>El comando Remove-Migration eliminar la última creada.</p>
<div class="language-bat highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">remove</span><span class="na">-migration -context </span><span class="kd">SampleDbContext</span>
</code></pre></div></div>
<p>Ten cuenta que si la migración ha sido aplicada a la base de datos con un <em>update-database</em> obtendrás un error avisándote que primero debes hacer un downgrade a la versión anterior.</p>
<h2 id="listado-de-migraciones-en-nuestra-bd">Listado de migraciones en nuestra BD</h2>
<p>Para saber que migraciones están aplicadas sobre la BD, la mejor opción es ejecutar la siguiente query:</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">select</span> <span class="o">*</span> <span class="k">from</span> <span class="n">__EFMigrationsHistory</span>
</code></pre></div></div>
<p>A día de hoy no tenemos comando como en EF6 desde la PCM para listar las migraciones, pero podemos hacerlo por CLI ejecutando:</p>
<div class="language-bat highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">dotnet</span> <span class="kd">ef</span> <span class="kd">migrations</span> <span class="kd">list</span> <span class="na">-Project </span><span class="s2">"App1.Data"</span> <span class="na">-context </span><span class="kd">SampleDbContext</span>
</code></pre></div></div>
<p><br /><br /></p>
<p>Happy coding!
<br />
David.</p>
Entity Framework 6 y sus migraciones2018-09-27T11:00:00+00:00https://dagope.com/2018/09/27/EF6-migraciones-comandos<p>En este artículo voy a intentar explicar cómo usar las migraciones en Entity Framework (EF6) y las operaciones que podemos realizar para acabar manejándolas sin ningún problema.
Para que esté bien claro, este post es sobre EF6, en uno posterior trataré sobre EF Core.
<!--break-->
<!-- TOC --></p>
<ul>
<li><a href="#migraciones-automáticas-y-cuándo-usarlas">Migraciones Automáticas y cuándo usarlas</a></li>
<li><a href="#habilitar-migraciones-en-una-bd-y-viendo-el-detalle">Habilitar migraciones en una BD y viendo el detalle</a>
<ul>
<li><a href="#extendiendo-y-personalizando-una-migración">Extendiendo y personalizando una migración</a></li>
</ul>
</li>
<li><a href="#como-crear-nuevas-migraciones">Como crear nuevas migraciones:</a></li>
<li><a href="#editar-una-migración-creada">Editar una migración creada</a></li>
<li><a href="#crear-una-migración-vacía">Crear una migración vacía</a>
<ul>
<li><a href="#cómo-agregar-mi-script-sql-a-una-migración">Cómo agregar mi script SQL a una migración</a></li>
</ul>
</li>
<li><a href="#migrar-a-una-versión-concreta-downgrade">Migrar a una versión concreta (Downgrade)</a></li>
<li><a href="#listado-de-migraciones-en-nuestra-bd">Listado de migraciones en nuestra BD</a></li>
<li><a href="#depurar-el-seed-de-inicializacion-de-datos">Depurar el Seed de Inicializacion de datos:</a></li>
<li><a href="#cómo-usar-las-migraciones-con-el-resto-del-equipo-de-desarrollo">Cómo usar las migraciones con el resto del equipo de desarrollo</a></li>
</ul>
<!-- /TOC -->
<p><br />Esto no pretende ser una guía de EF, por lo que doy por hecho que se conoce EF, y como funciona la creación de contextos. Sino para ello que mejor que un tutorial con toda la info: <a href="http://www.entityframeworktutorial.net" target="_blank">http://www.entityframeworktutorial.net</a>.</p>
<h2 id="migraciones-automáticas-y-cuándo-usarlas">Migraciones Automáticas y cuándo usarlas</h2>
<p>Tener habilitadas las migraciones automáticas puede ser muy útil cuando arrancamos un proyecto o para hacer pruebas de concepto, pero más allá de esos casos yo aconsejo tenerlas <strong>siempre desactivadas</strong>. Tendremos las migraciones totalmente controladas por código sin que la magia del automático trabaje por nosotros.</p>
<blockquote>
<p>La potencia sin control no sirve de nada. — Pirelli & C. S.p.A.</p>
</blockquote>
<p><br />Para desactivarlas, lo tenemos que marcar por código en la clase <em>Configuration</em> que herede de <em>DbMigrationsConfiguration</em>:</p>
<div class="language-c# highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">AutomaticMigrationsEnabled</span> <span class="p">=</span> <span class="k">false</span><span class="p">;</span>
</code></pre></div></div>
<h2 id="habilitar-migraciones-en-una-bd-y-viendo-el-detalle">Habilitar migraciones en una BD y viendo el detalle</h2>
<ul>
<li>Lo primero que tenemos que hacer para tener disponibles las migraciones, es agregar los paquetes Nuget a nuestro proyecto. Buscamos EntityFramework en el <em>Manage NuGet Package</em> o bien por la PMC (<em>Package Manage Console</em>):
Es bueno que nos acostumbremos a usar la consola porque a partir de aquí ejecutaremos todos los comandos de migraciones sobre ella.
<div class="language-bat highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">Install</span><span class="na">-Package </span><span class="kd">EntityFramework</span>
</code></pre></div> </div>
</li>
<li>A continuación, activamos las Migraciones en el proyecto que queramos de nuestra solución. Para indicarle un proyecto concreto usaremos el parámetro <em>-ProjectName “Nombre.Del.Proyecto”, en los siguientes ejemplos usaremos el valor *App1.Data</em>.
<br /> Recuerda que el comando se ejecuta desde la PCM
<div class="language-bat highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">Enable</span><span class="na">-Migrations -ProjectName </span><span class="s2">"App1.Data"</span>
</code></pre></div> </div>
<p>Esto nos creará una carpeta <em>Migrations</em> y un fichero <em>Configuration.cs</em> donde podemos cambiar el comportamiento de las migraciones sobre el contexto. Aqui es donde tenemos la opción de habilitar las migraciones automáticas, o de establecer una inicialización de datos.</p>
</li>
<li>Si no tenemos un contexto nos dará un error.</li>
<li>
<p>Si no tenemos configurada la cadena de conexión en nuestro fichero de configuración, nos dará error.</p>
</li>
<li>A modo de ejemplo pongo este contexto, y configurar una cadena de conexión con el nombre <em>Default</em></li>
</ul>
<div class="language-c# highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">SampleDbContext</span> <span class="p">:</span> <span class="n">DbContext</span>
<span class="p">{</span>
<span class="k">public</span> <span class="n">IDbSet</span><span class="p"><</span><span class="n">User</span><span class="p">></span> <span class="n">Users</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
<span class="k">static</span> <span class="nf">SampleDbContext</span><span class="p">()</span>
<span class="p">{</span>
<span class="p">}</span>
<span class="k">public</span> <span class="nf">SampleDbContext</span><span class="p">()</span> <span class="p">:</span> <span class="k">base</span><span class="p">(</span><span class="s">"name=Default"</span><span class="p">)</span>
<span class="p">{</span>
<span class="p">}</span>
<span class="k">protected</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">OnModelCreating</span><span class="p">(</span><span class="n">DbModelBuilder</span> <span class="n">modelBuilder</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nf">CommonModelCreating</span><span class="p">(</span><span class="n">modelBuilder</span><span class="p">);</span>
<span class="k">base</span><span class="p">.</span><span class="nf">OnModelCreating</span><span class="p">(</span><span class="n">modelBuilder</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">private</span> <span class="k">void</span> <span class="nf">CommonModelCreating</span><span class="p">(</span><span class="n">DbModelBuilder</span> <span class="n">modelBuilder</span><span class="p">)</span>
<span class="p">{</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<ul>
<li>Creamos la primera migración
<div class="language-bat highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">Add</span><span class="na">-Migration </span><span class="kd">Initial</span>
</code></pre></div> </div>
<p>Esto nos crea en la carpeta <em>Migrations</em> una clase <em>yyyymmdddhhMMss_Initial.cs</em> con dos ficheros más asociados:</p>
</li>
<li>yyyymmdddhhMMss_Initial.cs
<br /> Esta clase contiene los métodos Up() y Down(). En su interior encontrarás el código de creación/borrado de tablas/columnas que el scaffolding detectó en los cambios de tus entidades.</li>
<li>yyyymmdddhhMMss_Initial.Designer.cs
<br /> Genera el código con los datos referentes a la migración creada y la referencia al fichero de recursos resx.</li>
<li>
<p>yyyymmdddhhMMss_Initial.resx
A modo informativo este fichero de recursos contiene un snapshot del esquema de base de datos referente a nuestro contexto en la migración actual. Aunque el valor está serializado, le sirve a las herramientas para controlar el estado de nuestra DB en comparación a nuestro código. Si algo no fuese bien nos daría un error.
<br />
Ambos ficheros son de generación de código por parte del programa y no deberíamos hacer ningún cambio sobre ellos.</p>
</li>
<li>Para ejecutar la migración sobre la BD:
<div class="language-bat highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">Update</span><span class="na">-Database -ProjectName </span><span class="s2">"App1.Data"</span> <span class="na">-verbose -Script
</span></code></pre></div> </div>
<p>El parámetro -Script es opcional. Si lo indicamos obtendremos el script sql de los cambios que se ejecutarán.</p>
</li>
</ul>
<p>Ahora si inspeccionamos la tabla <em>__MigrationHistory</em> veremos el registro correspondiente a la migración creada.
<img src="https://dagope.com/public/uploads/2018/10/SelectMigration.jpg" alt="Select Migration" /></p>
<h3 id="extendiendo-y-personalizando-una-migración">Extendiendo y personalizando una migración</h3>
<p>Si la migración Initial queremos que sea un reset completo, automático, y que no falle aunque lo ejecutemos N veces, hay que ir un paso más allá personalizando y extendiendo operaciones de migración.
Si en el punto anterior hemos borrado la BD y volvemos a ejecutar el mismo update-database obtendremos un error.
<br /><em>¿Por qué?</em>
<br />Porque el script que se genera de un DropTable no comprueba si la tabla existe previamente.
<br /><em>¿Y ahora qué hacemos?</em>
<br />Para todo hay solución y EF nos permite crear nuestra propia operación y personalizar el código generado en la migración, así que podemos crear un método de extensión llamado <em>DropTableIfExists</em> y ejecutar el código sql para verificar que la tabla existe, antes de borrarlo.
La intención es que se ejecute el siguiente script sql (los ejemplos están contra una BD de SqlServer):</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">IF</span> <span class="k">EXISTS</span> <span class="p">(</span><span class="k">SELECT</span> <span class="n">name</span> <span class="k">FROM</span> <span class="n">sys</span><span class="p">.</span><span class="n">tables</span> <span class="k">WHERE</span> <span class="n">name</span> <span class="o">=</span> <span class="n">N</span><span class="s1">'tabla'</span> <span class="k">AND</span> <span class="n">object_id</span> <span class="o">=</span> <span class="n">object_id</span><span class="p">(</span><span class="n">N</span><span class="s1">'[dbo].[tabla]'</span><span class="p">,</span> <span class="n">N</span><span class="s1">'U'</span><span class="p">))</span>
<span class="k">DROP</span> <span class="k">TABLE</span> <span class="p">[</span><span class="n">dbo</span><span class="p">].[</span><span class="n">tabla</span><span class="p">]</span>
</code></pre></div></div>
<p>Para conseguir que EF genere nuestro sql personalizado seguiremos los siguientes pasos:</p>
<ul>
<li>Creamos la operación:
<div class="language-c# highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="k">public</span> <span class="k">class</span> <span class="nc">DropTableIfExistsOperation</span> <span class="p">:</span> <span class="n">MigrationOperation</span>
<span class="p">{</span>
<span class="k">public</span> <span class="kt">string</span> <span class="n">Table</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">private</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
<span class="k">public</span> <span class="kt">string</span> <span class="n">Schema</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">private</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
<span class="k">public</span> <span class="nf">DropTableIfExistsOperation</span><span class="p">(</span><span class="kt">string</span> <span class="n">table</span><span class="p">):</span> <span class="k">base</span><span class="p">(</span><span class="k">null</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">table</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="sc">'.'</span><span class="p">))</span>
<span class="p">{</span>
<span class="n">Schema</span> <span class="p">=</span> <span class="n">table</span><span class="p">.</span><span class="nf">Split</span><span class="p">(</span><span class="sc">'.'</span><span class="p">)[</span><span class="m">0</span><span class="p">];</span>
<span class="n">Table</span> <span class="p">=</span> <span class="n">table</span><span class="p">.</span><span class="nf">Split</span><span class="p">(</span><span class="sc">'.'</span><span class="p">)[</span><span class="m">1</span><span class="p">];</span>
<span class="p">}</span>
<span class="k">else</span>
<span class="p">{</span>
<span class="n">Schema</span> <span class="p">=</span> <span class="s">"dbo"</span><span class="p">;</span>
<span class="n">Table</span> <span class="p">=</span> <span class="n">table</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">override</span> <span class="kt">bool</span> <span class="n">IsDestructiveChange</span>
<span class="p">{</span>
<span class="k">get</span>
<span class="p">{</span>
<span class="k">return</span> <span class="k">false</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div> </div>
</li>
<li>Creamos la clase que genera el código sql sobre la operación que queremos realizar:
<div class="language-c# highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">CustomSqlServerMigrationSqlGenerator</span> <span class="p">:</span> <span class="n">SqlServerMigrationSqlGenerator</span>
<span class="p">{</span>
<span class="k">protected</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">Generate</span><span class="p">(</span><span class="n">MigrationOperation</span> <span class="n">migrationOperation</span><span class="p">)</span>
<span class="p">{</span>
<span class="kt">var</span> <span class="n">operation</span> <span class="p">=</span> <span class="n">migrationOperation</span> <span class="k">as</span> <span class="n">DropTableIfExistsOperation</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">operation</span> <span class="p">!=</span> <span class="k">null</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">using</span> <span class="p">(</span><span class="kt">var</span> <span class="n">writer</span> <span class="p">=</span> <span class="nf">Writer</span><span class="p">())</span>
<span class="p">{</span>
<span class="c1">//writer.WriteLine(template,operation.Table);</span>
<span class="n">writer</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">"IF EXISTS (SELECT name FROM sys.tables WHERE name = N'{0}' AND object_id = object_id(N'[{1}].[{0}]', N'U'))"</span><span class="p">,</span> <span class="n">operation</span><span class="p">.</span><span class="n">Table</span><span class="p">,</span> <span class="n">operation</span><span class="p">.</span><span class="n">Schema</span><span class="p">);</span>
<span class="n">writer</span><span class="p">.</span><span class="nf">Write</span><span class="p">(</span><span class="s">" DROP TABLE [{1}].[{0}]"</span><span class="p">,</span> <span class="n">operation</span><span class="p">.</span><span class="n">Table</span><span class="p">,</span> <span class="n">operation</span><span class="p">.</span><span class="n">Schema</span><span class="p">);</span>
<span class="nf">Statement</span><span class="p">(</span><span class="n">writer</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div> </div>
</li>
<li>Exponemos el método de extensión sobre la clase DbMigration
<div class="language-c# highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">static</span> <span class="k">class</span> <span class="nc">Extensions</span>
<span class="p">{</span>
<span class="k">public</span> <span class="k">static</span> <span class="k">void</span> <span class="nf">DropTableIfExists</span><span class="p">(</span><span class="k">this</span> <span class="n">DbMigration</span> <span class="n">migration</span><span class="p">,</span> <span class="kt">string</span> <span class="n">table</span><span class="p">)</span>
<span class="p">{</span>
<span class="p">((</span><span class="n">IDbMigration</span><span class="p">)</span><span class="n">migration</span><span class="p">).</span><span class="nf">AddOperation</span><span class="p">(</span><span class="k">new</span> <span class="nf">DropTableIfExistsOperation</span><span class="p">(</span><span class="n">table</span><span class="p">));</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div> </div>
</li>
<li>Ahora en nuestro método Down la migración Initial podemos sustituir los
<br /> <code class="language-plaintext highlighter-rouge">DropTable("dbo.tabla")</code>
<br />por nuestro <code class="language-plaintext highlighter-rouge">this.DropTableIfExists("dbo.tabla")</code></li>
</ul>
<p><br />Ahora podemos ejecutar:</p>
<div class="language-bat highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">Update</span><span class="na">-Database -ProjectName </span><span class="s2">"App1.Data"</span> <span class="na">-TargetMigration </span><span class="m">0</span> <span class="na">-verbose -Script
</span></code></pre></div></div>
<p>Estaremos bajando a la migración con nombre <em>Initial</em> (la cúal también se puede indicar con valor cero).
Y si ahora ejecutamos nuevamente veremos que no falla:</p>
<div class="language-bat highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">Update</span><span class="na">-Database -ProjectName </span><span class="s2">"App1.Data"</span> <span class="na">-verbose
</span></code></pre></div></div>
<p>Con esto ya que podemos eliminar por completo nuestra BD y actualizarla sin problemas a la última migración.
<br /><br />Esto es un ejemplo que puedes utilizar para crear tus propias operaciones.</p>
<h2 id="como-crear-nuevas-migraciones">Como crear nuevas migraciones:</h2>
<p>Nuestro código cambia y es normal que nuestra BD también evolucione, creando o borrando nuevos campos, tablas, etc…
<br />Cuando tengamos listos nuestros cambios ejecutamos el siguiente comando para crear una nueva migración.</p>
<div class="language-bat highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">Add</span><span class="na">-Migration -Name </span><span class="kd">NombreDeMigracion</span> <span class="na">-ProjectName </span><span class="s2">"App1.Data"</span>
</code></pre></div></div>
<p><br />Esto generará una clase en la carpeta Migrations con el formato <em>yyyyMMdd_NombreDeMigracion.cs</em>
<br />En este fichero podremos personalizar lo que nos interese, como por ejemplo inicializar el valor de los campos.
<br />Y para establecer los cambios nuevamente ejecutamos el comando.</p>
<div class="language-bat highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">Update</span><span class="na">-Database -ProjectName </span><span class="s2">"App1.Data"</span> <span class="na">-verbose
</span></code></pre></div></div>
<p>agregar el comando -Script si queremos el ScriptSQL de ejecución de cambios.
<br /><br /><strong>Oh! te has olvidado de añadir algo en la migración??</strong>
<br /> que nadie se asuste ;-) vamos a explicarlo en el siguiente punto.</p>
<h2 id="editar-una-migración-creada">Editar una migración creada</h2>
<p>¿Y si quiero editar una migración sin tener que crear otra nueva con el scaffolding aplicado?
Durante el desarrollo de una tarea, es muy probable que nos encontremos con la necesidad de cambiar nuestra migración por ejemplo para incluir esa columna que nos haya podido olvidar en un primer momento. Por otra parte tampoco queda muy elegante tener 5 o 6 migraciones para una misma tarea de desarrollo.
<br />Para editar nuestra migración:</p>
<ul>
<li>Revisar en que estado se encuentra nuestra BD. Y situarnos en la migración anterior a la que vamos a editar.
<div class="language-bat highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">Update</span><span class="na">-Database -ProjectName </span><span class="s2">"App1.Data"</span> <span class="na">-TargetMigration</span><span class="nl">:Migracionv</span><span class="m">1</span>
</code></pre></div> </div>
</li>
<li>Ejecutar el comando de Add-Migrations indicando el nombre exacto de la migración que queremos editar, por lo general <em>yyyymmdddhhMMss_NombreDeMigracion</em>, y agregar el parámetro <strong>-Force</strong> para forzar el re-scaffold de la migración entera. Si no lo haces verás un warning amarillo que te lo indicará.
<div class="language-bat highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">Add</span><span class="na">-Migration -Name </span><span class="kd">full_name_including_timestamp_of_last_migration</span> <span class="na">-ProjectName </span><span class="s2">"App1.Data"</span> <span class="na">-Force
</span></code></pre></div> </div>
</li>
<li>Una vez finalizado veremos como nuestro fichero de migración ha sido actualizado con los cambios que deseamos.</li>
</ul>
<p><br />Si intentamos hacer el proceso de forma manual y no usando los comandos, tendremos problemas al agregar próximas migraciones. El comando <em>add-migration</em> también se encarga de modificar el fichero .resx que contiene un valor serializado del estado de la BD.</p>
<p>podemos consultar la tabla __MigrationHistory order by MigrationId desc
<br />Para editar nuestra migración: tendremos que ejecutar el comando de Add-Migrations indicando el nombre exacto de la migración que queremos sobrescribir, por lo general <em>yyyymmdddhhMMss_NombreDeMigracion</em>, y agregar el parámetro <strong>-Force</strong> para forzar el re-scaffold de la migración entera. Si no lo haces verás un warning amarillo que te lo indicará.</p>
<div class="language-bat highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">Add</span><span class="na">-Migration -Name </span><span class="kd">full_name_including_timestamp_of_last_migration</span> <span class="na">-ProjectName </span><span class="s2">"App1.Data"</span> <span class="na">-Force
</span></code></pre></div></div>
<p><strong>OJO!</strong>
<br /> si ya ejecutaste un UpdateDatabase antes de editar la migración,
sería recomendable poner la BD en el estado anterior a la migración. Y posteriormente volver a realizar un update-database.</p>
<h2 id="crear-una-migración-vacía">Crear una migración vacía</h2>
<p>Hay veces que podemos tener cambios en objetos de nuestra BD que no interfieren directamente sobre nuestro contexto y el scaffolding no lo detecta. Cambios por ejemplo en un <em>store procedure</em>, function, views, etc…
<br />Para tener controlados en nuestro historial de migraciones este tipo de cambios , tenemos que generar una migración vacía.
<br />¿Cómo se hace?
<br />Exactamente de la misma manera que creando una migración normal.</p>
<div class="language-bat highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">Add</span><span class="na">-Migration -Name </span><span class="kd">ChangesInStoreProcedures</span> <span class="na">-ProjectName </span><span class="s2">"App1.Data"</span>
</code></pre></div></div>
<p>Esto nos genera nuestra migración con la diferencia de que los métodos Up() y Down() están vacíos.</p>
<h3 id="cómo-agregar-mi-script-sql-a-una-migración">Cómo agregar mi script SQL a una migración</h3>
<p>Para ejecutar cualquier script sql en los métodos Up() y Down() de nuestro fichero migration, tenemos el método Sql(…) heredado de DbMigration.
<br />¿Queremos agregar una vista al modelo de datos? Pues un ejemplo sería así:</p>
<div class="language-c# highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">partial</span> <span class="k">class</span> <span class="nc">ChangesInViews</span> <span class="p">:</span> <span class="n">DbMigration</span>
<span class="p">{</span>
<span class="k">public</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">Up</span><span class="p">()</span>
<span class="p">{</span>
<span class="kt">string</span> <span class="n">scriptSql</span> <span class="p">=</span> <span class="s">@"
CREATE VIEW [dbo].[vw_Users]
AS
select Id, Name, Email, IsBlocked
from dbo.Users
GO
"</span><span class="p">;</span>
<span class="nf">Sql</span><span class="p">(</span><span class="n">scriptSql</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">Down</span><span class="p">()</span>
<span class="p">{</span>
<span class="kt">string</span> <span class="n">scriptSql</span> <span class="p">=</span> <span class="s">@"DROP VIEW [dbo].[vw_Users]"</span><span class="p">;</span>
<span class="nf">Sql</span><span class="p">(</span><span class="n">scriptSql</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Obviamente, si el escript no es correcto la migración fallará. Es tú responsabilidad que se ejecute correctamente, tanto para Up() como para Down().</p>
<h2 id="migrar-a-una-versión-concreta-downgrade">Migrar a una versión concreta (Downgrade)</h2>
<p>Para migrar la Bd a una situación en concreto debe indicarse el parámetro <strong>-TargetMigration</strong> al comando update-database.
Tal que así:</p>
<div class="language-bat highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">Update</span><span class="na">-Database -ProjectName </span><span class="s2">"App1.Data"</span> –TargetMigration <span class="kd">NombreDeMigracion</span>
</code></pre></div></div>
<p><br />Para volver al inicio del todo y a una base de datos “vacía” (primera migración):</p>
<div class="language-bat highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">Update</span><span class="na">-Database -ProjectName </span><span class="s2">"App1.Data"</span> –TargetMigration $InitialDatabase
</code></pre></div></div>
<p>o también indicando un cero:</p>
<div class="language-bat highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">Update</span><span class="na">-Database -ProjectName </span><span class="s2">"App1.Data"</span> –TargetMigration <span class="m">0</span>
</code></pre></div></div>
<h2 id="listado-de-migraciones-en-nuestra-bd">Listado de migraciones en nuestra BD</h2>
<p>Para obtener las migraciones que están aplicadas sobre nuestra BD usaremos el siguiente comando</p>
<div class="language-bat highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">Get</span><span class="na">-Migrations -ProjectName </span><span class="s2">"App1.Data"</span>
</code></pre></div></div>
<h2 id="depurar-el-seed-de-inicializacion-de-datos">Depurar el Seed de Inicializacion de datos:</h2>
<p>Descomentar o añadir las siguientes líneas en la clase Configuration que sería algo como: <em>App1.Data.Migrations.Configuration</em></p>
<div class="language-c# highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">protected</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">Seed</span><span class="p">(</span><span class="n">App1</span><span class="p">.</span><span class="n">Data</span><span class="p">.</span><span class="n">Context</span><span class="p">.</span><span class="n">ApplicationDbContext</span> <span class="n">context</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">System</span><span class="p">.</span><span class="n">Diagnostics</span><span class="p">.</span><span class="n">Debugger</span><span class="p">.</span><span class="n">IsAttached</span> <span class="p">==</span> <span class="k">false</span><span class="p">){</span>
<span class="n">System</span><span class="p">.</span><span class="n">Diagnostics</span><span class="p">.</span><span class="n">Debugger</span><span class="p">.</span><span class="nf">Launch</span><span class="p">();</span>
<span class="p">}</span>
<span class="n">System</span><span class="p">.</span><span class="n">Diagnostics</span><span class="p">.</span><span class="n">Debugger</span><span class="p">.</span><span class="nf">Break</span><span class="p">();</span>
<span class="p">[...]</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="cómo-usar-las-migraciones-con-el-resto-del-equipo-de-desarrollo">Cómo usar las migraciones con el resto del equipo de desarrollo</h2>
<p>Hay un artículo muy completo en la documentación de Microsoft que explica con ejemplos claros y sencillos como manejar las migraciones y las diferentes situaciones que nos podemos encontrar al usarlas cuando nuestro equipo de trabajo hay varios programadores que pueden estar cambiando la base de datos.
<br /> Un artículo muy recomendado: <a href="https://msdn.microsoft.com/en-us/data/dn481501.aspx" target="_blank">https://msdn.microsoft.com/en-us/data/dn481501.aspx</a>.</p>
<p><br />
<br />
<br />
Espero que alguien más a parte de mi yo futuro encuentre útil estas notas.</p>
<p>Saludos.</p>
El error de Visual Studio al depurar un proyecto Web con SSL activado2017-11-02T11:00:00+00:00https://dagope.com/2017/11/02/error-de-vs-al-depurar-web-con-ssl-activado<p>Si estás intentando arrancar la depuración de un proyecto web en Visual Studio y obtienes el siguiente error:
“An error ocurred attempting to determine the process id of dontnet.exe which is hosting your application. One or more errores occurred.” Que no cunda el pánico.
<!--break-->
<img src="https://dagope.com/public/uploads/2017/11/error_vs_debug_ssl.jpg" alt="Error Visual Studio" /></p>
<p>Es verdad que el mensaje de error no te ayuda nada.
Y lo que está ocurriendo es que tienes activado el SSL en las propiedades de depuración del proyecto y algo ha pasado con el certificado instalado para localhost.
<br />Tenemos varias opciones para solucionarlo:</p>
<ul>
<li><strong>Opción 1 (rápida y barata)</strong>:
<br />Puedes iniciar una instancia sin depuración <code class="language-plaintext highlighter-rouge">CTRL</code> + <code class="language-plaintext highlighter-rouge">F5</code>. Esto arranca el navegador y te dice que la página es insegura y tendrás que pulsar en algún link donde ponga continuar.</li>
<li><strong>Opción 2 (reparando IISExpress)</strong>:
<br />Esta opción a mí no me ha funcionado pero a la mayoría sí y podría ser tu caso. Tendrás que reparar el IISExpress desde el panel de control de Windows.</li>
</ul>
<p><img src="https://dagope.com/public/uploads/2017/11/repair_iss_express.jpg" alt="Repair IISExpress" /></p>
<p><br />Una vez hecho verás en la consola MCC de Certificados como se habrá reinstalado localhost. (Debajo de Personal y Certificates). Si ya existía puedes eliminarlo y volver a reparar IIS Express y aparecerá de nuevo.
<img src="https://dagope.com/public/uploads/2017/11/mcc.jpg" alt="Consola certificados de windows" /></p>
<ul>
<li><strong>Opción 3 (hazlo tú mismo)</strong>:
<br />Esta opción a mi me funcionó y no tiene misterio ninguno más que instalar el correcto certificado para localhost.
<ul>
<li>Inicia la instancion con <code class="language-plaintext highlighter-rouge">CTRL</code> + <code class="language-plaintext highlighter-rouge">F5</code></li>
<li>Abrir la url en Internet Explorer</li>
<li>Cuando muestre se el aviso del certificado inseguro pulsamos en continuar.</li>
<li>Ahora pulsamos en el icono rojo del certificado que aparece de la barra de navegación. Pulsamos en la opción Ver Certificados.</li>
<li>Se nos abre una pantalla con el detalle del certificado. Pulsamos en Instalar.</li>
<li>En el asistente elegimos la opción de <em>Local Machine</em> y lo almacenamos en “Trusted Root Certification Authorities” o en español “Entidades de certificación raíz de confianza”.</li>
<li>Completamos la instalación y ahora ya podrás iniciar el proyecto pulsando F5 en Visual Studio.</li>
</ul>
</li>
<li><strong>Opción 4 (JexusManager)</strong>:
<br />Puedes usar una herramienta como JexusManager, en este enlace explican cómo:
<a href="https://blog.lextudio.com/why-chrome-says-iis-express-https-is-not-secure-and-how-to-resolve-that-d906a183f0" target="_blank">https://blog.lextudio.com/why-chrome-says-iis-express-https-is-not-secure-and-how-to-resolve-that-d906a183f0</a></li>
</ul>
<p>Espero sea de ayuda.</p>
<p>Saludos!</p>
Cómo montar un blog para dedicarme a lo importante, escribir.2017-10-04T11:00:00+00:00https://dagope.com/2017/10/04/comenzando-blog-creando-entorno<p>La finalidad de este artículo es detallar los pasos a seguir para crear un blog como este y tener un entorno de desarrollo en local (Windows) donde poder previsualizar de forma sencilla los artículos antes de publicarlos.
<!--break-->Y de esta manera, si cambio de PC, tengo todos los pasos detallados para que mi memoria no sufra, asi que vamos a ello.</p>
<h1 id="contenido">Contenido</h1>
<!-- TOC -->
<ul>
<li><a href="#contenido">Contenido</a></li>
<li><a href="#requisitos-para-que-todo-funcione">Requisitos para que todo funcione</a>
<ul>
<li><a href="#descargando-ruby-y-kit-de-desarrollo-utilizados">Descargando Ruby y kit de desarrollo utilizados:</a></li>
<li><a href="#instalando-ruby-windows">Instalando Ruby (Windows)</a></li>
<li><a href="#instalando-ruby-ubuntu">Instalando Ruby (Ubuntu)</a></li>
<li><a href="#instalando-jekyll-windowsubuntu">Instalando Jekyll (Windows/Ubuntu)</a></li>
</ul>
</li>
<li><a href="#creando-el-blog">Creando el blog</a>
<ul>
<li><a href="#preparando-el-repositorio">Preparando el repositorio</a></li>
<li><a href="#preparando-entorno">Preparando entorno</a></li>
<li><a href="#configurando-el-blog">Configurando el blog</a></li>
<li><a href="#compilando-y-arrancando-en-local">Compilando y arrancando en local</a></li>
<li><a href="#publicando-y-viendo-la-magia-de-githubpages">Publicando y viendo la magia de GitHubPages</a></li>
</ul>
</li>
<li><a href="#configurando-comentarios">Configurando comentarios.</a></li>
<li><a href="#trabajando-más-cómodo-con-vs-code">Trabajando más cómodo con VS Code.</a></li>
<li><a href="#configuración-en-localdev-y-en-producción">Configuración en Local/Dev y en Producción</a></li>
<li><a href="#guía-para-agregar-nuevos-artículos">Guía para agregar nuevos artículos</a></li>
<li><a href="#bonus-más-comandos-jekyll">Bonus: más comandos jekyll</a></li>
</ul>
<!-- /TOC -->
<h1 id="requisitos-para-que-todo-funcione">Requisitos para que todo funcione</h1>
<div class="message">Lo descrito en este blog hace referencia a un entorno con Windows instalado. Aunque se detalla la instalación sobre un Ubuntu.
</div>
<ul>
<li>Tener una cuenta en GitHub o <a href="https://github.com" target="_blank">crearla</a>.</li>
<li>Tener Git instalado en el equipo. <a href="https://git-scm.com/book/es/v1/Empezando-Instalando-Git" target="_blank">instalación y tutorial</a></li>
<li>Usaremos Visual Studio Code como editor. <a href="https://code.visualstudio.com/download" target="_blank">descargar</a></li>
</ul>
<h2 id="descargando-ruby-y-kit-de-desarrollo-utilizados">Descargando Ruby y kit de desarrollo utilizados:</h2>
<ul>
<li>
<p>Ruby: Ruby 2.3.3 (x64) <a href="https://dl.bintray.com/oneclick/rubyinstaller/rubyinstaller-2.3.3-x64.exe">descagar</a></p>
</li>
<li>
<p>Development KIT For use with Ruby 2.0 to 2.3 (x64 - 64bits only)
<a href="https://dl.bintray.com/oneclick/rubyinstaller/DevKit-mingw64-64-4.7.2-20130224-1432-sfx.exe">descargar</a></p>
</li>
</ul>
<p>Para probar otras versiones ir a la <a href="https://rubyinstaller.org/downloads/">página oficial</a> pero siempre cuidado con que el Development Kit y la versión de Ruby sean compatibles.</p>
<h2 id="instalando-ruby-windows">Instalando Ruby (Windows)</h2>
<ul>
<li>
<p>Instalar Ruby en C:\Ruby
<br />y marcar la opción de agregar ruby al path sistema
<img src="https://dagope.com/public/uploads/2017/10/install_ruby.png" alt="Install options ruby" /></p>
</li>
<li>Descomprimir DevKit en C:\RubyDevKit</li>
<li>Abrir línea de comandos y nos situamos en la carpeta donde descomprimimos c:\RubyDevKit y ejecutar:</li>
</ul>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell">ruby dk.rb init
ruby dk.rb <span class="nb">install</span></code></pre></figure>
<p><img src="https://dagope.com/public/uploads/2017/10/install_dev_kit_ruby.png" alt="Install dev kit ruby" /></p>
<h2 id="instalando-ruby-ubuntu">Instalando Ruby (Ubuntu)</h2>
<ul>
<li>Ejecutar los comandos:
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt-get update
<span class="nb">sudo </span>apt-get <span class="nb">install </span>ruby ruby-dev make gcc
<span class="nb">sudo </span>apt-get <span class="nb">install </span>gcc ruby-dev libxslt-dev libxml2-dev zlib1g-dev
</code></pre></div> </div>
</li>
</ul>
<h2 id="instalando-jekyll-windowsubuntu">Instalando Jekyll (Windows/Ubuntu)</h2>
<p>Desde la misma línea de comandos antes abierta:</p>
<ul>
<li>Instalar jekyll con el comando
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gem <span class="nb">install </span>jekyll
</code></pre></div> </div>
<p>Si te sale el bloqueo del Firewall de windows permitidle acceso.</p>
</li>
<li>Instalar bundler
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gem <span class="nb">install </span>bundler
</code></pre></div> </div>
</li>
<li>Instalar rouge
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gem <span class="nb">install </span>rouge
</code></pre></div> </div>
</li>
<li>Instalar jeykyll watch
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gem <span class="nb">install </span>wdm
</code></pre></div> </div>
</li>
</ul>
<h1 id="creando-el-blog">Creando el blog</h1>
<p>La manera más sencilla de crear nuestro blog es partir de uno ya existente y usarlo como base para luego personalizarlo a nuestro gusto.
Aquí tenéis una lista de temas de blogs Jekyll. <a href="https://github.com/jekyll/jekyll/wiki/Themes" target="_blank">Jekyll themes</a>
<br /><strong>¡NOTA!</strong> En este articulo los ejemplos son sobre el repositorio de este mismo blog. Donde ya está configurada y adaptada la plantilla. Si elijes otra plantilla tendrás que instalar los paquetes <em>gem</em> que necesite y revisar la configuración.</p>
<h2 id="preparando-el-repositorio">Preparando el repositorio</h2>
<p>Lo más importante, es tener cuenta en <a href="https://github.com" target="_blank">GitHub</a>. Si no la tienes créala.
GitHub Será nuestro repositorio y a la vez nuestro hosting. Todo de forma gratuita y libre.
Con tu cuenta creada, inicia sesión y te doy 2 opciones:</p>
<ul>
<li>
<p>Opción 1: Crear repositorio de cero.
<br />Puedes elegir esta opción si quieres empezar con un repositorio vacío y subir el proyecto más tarde. Únicamente ten en cuenta de nombrar al repositorio con <em>username</em>.github.io
<img src="https://dagope.com/public/uploads/2017/10/create_new_repository_blog.png" alt="New repository" /></p>
</li>
<li>
<p>Opción 2: Crear repositorio por Fork</p>
<ul>
<li>Hacer un Fork del proyecto plantilla elegido. Si no sabes cómo hacerlo <a href="https://frontendlabs.io/3266--que-es-hacer-fork-repositorio-y-como-hacer-un-fork-github" target="_blank">aquí te explican cómo.</a>.</li>
<li>Navegar hasta Settings de nuestro proyecto y cambiar el <strong>Repository name</strong> por el valor <em>username</em>.github.io siendo el <em>username</em> vuestro nombre de usuario de github. Como se ve en la imagen de ejemplo de la opción 1.</li>
</ul>
</li>
</ul>
<h2 id="preparando-entorno">Preparando entorno</h2>
<p>Tenéis que tener instalado el tooling de Git en vuestra máquina. <a href="https://git-scm.com/book/es/v1/Empezando-Instalando-Git" target="_blank">Mas info aquí</a></p>
<ul>
<li>Ir a vuestra carpeta de trabajo p.ej: c:\proyectos\</li>
<li>Abrir la consola en esa carpeta. Y ejecutar para este ejemplo:
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone https://github.com/dagope/dagope.github.io.git
</code></pre></div> </div>
<p><em>UPDATE 01/11/17</em>: he dejado en este repositorio una plantilla base:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone https://github.com/dagope/TemplateBlog.git
</code></pre></div> </div>
</li>
<li>
<p>Si fuieste por la opción 1 tendrás que descomprimir tu plantilla en esta carpeta.</p>
</li>
<li>Situar la consola sobre el repositorio y descargamos los paquetes utilizados:
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bundle <span class="nb">install</span>
</code></pre></div> </div>
</li>
</ul>
<h2 id="configurando-el-blog">Configurando el blog</h2>
<p>Abre el fichero <em>_config.yml</em> con tu editor favorito.
Debemos configurar:
<br /><strong>url</strong>: http://username.github.io
<br /><strong>baseurl</strong>: http://username.github.io/
<br /><strong>repository</strong>: <em>username</em>/<em>username</em>.github.io
<br /><strong>userdisqus</strong>: usuario de la plataforma disqus para los comentarios <a href="#configurando-comentarios-para-los-artículos">ver más abajo</a></p>
<p>Y personalizaremos las propiedades:
<br /><em>title</em>
<br /><em>tagline</em>
<br /><em>description</em>
<br /><em>author</em>
<br /><em>name</em>
<br /><em>twitter</em></p>
<h2 id="compilando-y-arrancando-en-local">Compilando y arrancando en local</h2>
<p>Llegados a este punto únicamente tendrás que ejecutar el siguiente comando sobre la carpeta de trabajo:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bundle <span class="nb">exec </span>jekyll serve <span class="nt">-w</span> <span class="nt">--config</span> <span class="s2">"_config.yml,_config_dev.yml"</span>
</code></pre></div></div>
<p>Si no hay errores se ejecutará un servidor web sobre el puerto 4000 y ya podremos navegar a la url http://127.0.0.1:4000
<img src="https://dagope.com/public/uploads/2017/10/runing_jekyll.png" alt="Start jekyll" /></p>
<p>¡Y ya tenemos nuestro blog en local!</p>
<h2 id="publicando-y-viendo-la-magia-de-githubpages">Publicando y viendo la magia de GitHubPages</h2>
<p>Cuando ya tengamos creado un nuevo articulo la magia de publicarlo en la web es de GitHubPages.
Solamente nos encargamos de hacer commit y push de los cambios hacia el repositorio.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git add <span class="nb">.</span>
git commit <span class="nt">-m</span> <span class="s2">"siempre un comentario"</span>
git push origin master
</code></pre></div></div>
<p>¡Así de fácil!</p>
<h1 id="configurando-comentarios">Configurando comentarios.</h1>
<p>Para que los visitantes puedan dejar comentarios en post utilizamos la plataforma disqus.com que ya se encuentra instalada en esta plantilla.
Crea una cuenta en Disqus y dar de alta tu blog.
Luego configura en el fichero <em>_config.yml</em> el parámetro <em>userdisqus</em> con el usuario que has dado de alta.</p>
<h1 id="trabajando-más-cómodo-con-vs-code">Trabajando más cómodo con VS Code.</h1>
<p>Este repositorio se encuentra ya configurado para trabajar con Visual Studio Code de una forma más rápida y cómoda.
La carpeta .vscode tiene definida los ficheros que automatizan para la compilación y lanzar el navegador.
Estos son los atajos de teclado:</p>
<ul>
<li>Ejecutando tarea:
<strong><code class="language-plaintext highlighter-rouge">CTRL</code> + <code class="language-plaintext highlighter-rouge">SHIFT</code> + <code class="language-plaintext highlighter-rouge">P</code></strong> –> Run Task Dev -> Local</li>
<li>Compilación:
<strong><code class="language-plaintext highlighter-rouge">CTRL</code> + <code class="language-plaintext highlighter-rouge">SHIFT</code> + <code class="language-plaintext highlighter-rouge">B</code></strong></li>
</ul>
<h1 id="configuración-en-localdev-y-en-producción">Configuración en Local/Dev y en Producción</h1>
<p>En este repositorio hay creados dos ficheros de configuración:</p>
<ul>
<li><strong>Configuración Local/Dev:</strong>
El fichero _config_dev.yml configura los valores que ejecutamos en nuestro entorno local, tomando de base el fichero de producción. En este blog modificamos el valor de la clave <em>baseurl</em> con <code class="language-plaintext highlighter-rouge">/</code> para el entorno local.</li>
<li><strong>Configuración Producción:</strong>
El fichero _config.yml es el fichero que será usado para el entorno de producción por GitHubPages, y el que modificamos en el punto de <a href="#configurando-el-blog">arriba</a>.</li>
</ul>
<h1 id="guía-para-agregar-nuevos-artículos">Guía para agregar nuevos artículos</h1>
<p>Abrimos la carpeta de trabajo con el editor Visual Studio Code (puedes usar el que más te guste).
<br />De la estructura del blog debemos de saber lo básico y seguir unas reglas para no generar desorden. Enumero las siguientes:</p>
<ul>
<li>Las articulos se crean en la carpeta <em>_posts</em></li>
<li>Los archivos seguirán la nomenclatura yyyy-mm-dd-titulo-de-mi-ariculo.md</li>
<li>Al ser archivos .md se acepta el lenguaje markdown. Compatible con html.</li>
<li>El contenido (imágenes, ficheros, etc…) se incluyen en la carpeta <em>public/uploads/yyyy/mm</em></li>
<li>En cada post tenemos habilitadas una serie de propiedades al inicio del fichero:
<ul>
<li><em>published</em>: true,false si no queremos que se publique el articulo</li>
<li><em>layout</em>: post = la plantilla a usar, que por normal no cambiaremos</li>
<li><em>title</em>: título del articulo</li>
<li><em>author</em>: autor</li>
<li><em>comments</em>: true,false para habilitar los comentarios</li>
<li><em>categories</em>: array con las categorías en las que incluir el artículo [categoria1, categoria2 …]</li>
</ul>
</li>
<li>Para conseguir un de resumen de las primeras lineas del blog hacer uso del código <code class="language-plaintext highlighter-rouge"><!--break--></code></li>
</ul>
<h1 id="bonus-más-comandos-jekyll">Bonus: más comandos jekyll</h1>
<p><em>bundle exec jekyll serve -w –drafts</em>
<br />Ejecuta incluyendo los archivos en carpeta _drafts
<em>bundle exec jekyll serve -w –config “_config.yml,_config_dev.yml”</em>
<br />Ejecuta incluyendo los archivos en carpeta _drafts</p>