How To: Getting events from SQL Server


Sometimes we need to execute some long-running SQL Server processes (Stored procedures or functions) and it’d be nice to get progress messages, and why not, a percent value in order to show a progress bar in our application.

In the past I’ve used different techniques to archive this, from SQL Assemblies for calling a SignalR server, to execute xp_cmdshell, and so many other esoteric ways.

None of that solved the problem in an elegant way, but well, it worked… more or less…

Changing the point of view

Today I noticed the SqlConnection class has an InfoMessage event, and it can be used for “Clients that want to process warnings or informational messages sent by the server” which is EXACTLY what I was looking for.

It looks promising but, how can I trigger an error from my SQL?

The answer is really E-A-S-Y :)

If you are a TSQL programmer you surely know RAISERROR, which generates an error message in a similar way the throw C# keyword does.

The trick here is its severity argument: It indicates the type of problem encountered by SQL Server, and values under 10 indicate that these errors are not severe. Just warnings or information that SQL server sends to anyone is listening.

And who’s listening? You can imagine…

Putting it all together (show me the code)

Let’s assume we’ve this long-running stored procedure in our database:

CREATE PROCEDURE [dbo].[TestLongRunningSP]
RAISERROR('1) Starting', 0, 0) WITH NOWAIT;
WAITFOR DELAY '00:00:03'; --Wait for 3 seconds

RAISERROR('2) Fetching data', 0, 0) WITH NOWAIT;
WAITFOR DELAY '00:00:05'; --Wait for 5 seconds

RAISERROR('3) Some other actions', 0, 0) WITH NOWAIT;
WAITFOR DELAY '00:00:03'; --Wait for 3 seconds

RAISERROR('4) Calculating', 0, 0) WITH NOWAIT;
WAITFOR DELAY '00:00:02'; --Wait for 2 seconds

RAISERROR('5) Finishing', 0, 0) WITH NOWAIT;

Now let’s create some C# code:

First: A class to send the received messages from our repository to its consumer, which in this case the UI will be just a Windows Form (good old boys never die).

public class SqlMessageEventArgs {
public string Message { get; set; }
public int Progress { get; set; }

Notice the Progress property. We will use it in the last part, be patient ;)

Second: A repository class that open a connection, execute the stored procedure and send messages using a ProcessMessage event. We must explicitly enable the connection to receive these errors and raise handling the InfoMessage event:

public class SqlServerRepo
public event EventHandler ProcessMessage;
public string ConnectionString { get; internal set; }
public SynchronizationContext SyncContext { get; internal set; }
public async Task GetDataAsync() {
using (var con = new SqlConnection(ConnectionString))
con.FireInfoMessageEventOnUserErrors = true;
con.InfoMessage += (s, e) =>
SyncContext.Post(_ => ProcessMessage?.Invoke(
s, new SqlMessageEventArgs
Message = e.Message,
}), null);
var sql = "EXEC dbo.TestLongRunningSP";
var commandDefinition = new CommandDefinition(sql);
await con.ExecuteAsync(commandDefinition);

Note: When using async pattern the received messages come from a different thread and cannot be used to update the UI directly. For this reason I always recommend send to the Repo the current synchronization context and use this Context to post to the main thread.


BOOM: Typical cross thread exception when updating UI from a different thread.

Third: Finally let’s create a simple Windows Form with just a button, a progress bar and a list box).

const string cs = "Data Source=YOUR_SERVER;Initial Catalog=YOUR_DATABASE;Integrated Security=True;"
private async void button1_Click(object sender, EventArgs e) {
var repo = new SqlServerRepo() {
ConnectionString = cs,
SyncContext = SynchronizationContext.Current
repo.ProcessMessage += (ms, me) => {
listBoxLog.Items.Add($"{DateTime.Now} - {me.Message}");
await repo.GetDataAsync();
listBoxLog.Items.Add($"Finished Process!");

This code creates the repo, pass the connection string and the current synchronization context, handles the ProcessMessage event and invoke the GetDataAsync method.

Simply and elegant. No dependent assemblies or external resources.

One more thing

What about the progress bar? Also easy. Just use the state argument of the RAISERROR method on the SP like this:

RAISERROR('1) Starting', 0, 10) WITH NOWAIT;
WAITFOR DELAY '00:00:03'; --Wait for 3 seconds

RAISERROR('2) Fetching data', 0, 30) WITH NOWAIT;
WAITFOR DELAY '00:00:05'; --Wait for 5 seconds

RAISERROR('3) Some other actions', 0, 60) WITH NOWAIT;
WAITFOR DELAY '00:00:03'; --Wait for 3 seconds

RAISERROR('4) Calculating', 0, 70) WITH NOWAIT;
WAITFOR DELAY '00:00:02'; --Wait for 2 seconds

RAISERROR('5) Finishing', 0, 100) WITH NOWAIT;

Then update the GetDataAsync method:

public async Task GetDataAsync() {
using (var con = new SqlConnection(ConnectionString))
con.FireInfoMessageEventOnUserErrors = true;
con.InfoMessage += (s, e) =>
SyncContext.Post(_ => ProcessMessage?.Invoke(
s, new SqlMessageEventArgs
Message = e.Message,
Progress = e.Errors[0]?.State ?? 0
}), null);
var sql = "EXEC dbo.TestLongRunningSP";
var commandDefinition = new CommandDefinition(sql);
await con.QueryAsync(commandDefinition);

And finally the code in the form:

repo.ProcessMessage += (ms, me) => {
listBoxLog.Items.Add($"{DateTime.Now} - {me.Message}");
progressBar.Value = me.Progress;

And that’s all!

Hope you enjoy it, and please, send me your thoughts about this solution ;)

How To: Get data from OLAP cubes

cuberubikToday I had to read some data from different OLAP cubes and show this information in our application. This could be accomplished using ADOMD.NET client library, which is the multidimensional equivalent to the ADO.NET libraries.

This library provides a set of classes (Connections, Commands and Readers) that allows developers to connect, query and get data from these multidimensional sources in the same way the classical ADO.NET library does.

Maybe the easiest way to read data is using the class AdomdDataReader. This object retrieves a read-only, forward-only, flattened stream of data from an analytical data source, and also implements the IDataReader interface (like the good-old DataReader).

In non dynamic scenarios, we developers, tend to use ORMs like NHibertate, EF or Dapper (my favorite), to translate data from our models to classes. But these ORMs doesn’t support multidimensional sources, because after all, they are only supersets or extensions of the ADO.NET model and are based on this existing object model.

So, our goal here will be replicate in a multidimensional environment the way Dapper allows you to connect to a server, read some data and (the most important part) return these data as a typed List<T> of objects.

Let’s see how it works before dig into the details:

First of all we have a SQL script that returns some sample data of 3 different measures year by year (gains in period, gains year-to-date and the amount of money).


And we’d like to translate these data into a generic List of my own DTO:

Notice that in C# properties can not contain spaces so we previously have to ‘map’ the DataReader column names into valid property names. The trick here is create an attribute for decorating each property with the equivalent column name in the reader.

The magic here is inside the generic Query<T> Method. This method is just an extension method of the AdomdConnection object (Dapper style) and it’s mission will be:

  1. Replace the parameters in the command string “…Where ({ @PortfolioId })”
  2. Execute the command against a valid connection
  3. Transform the returned IDataReader into a List<MyResultDTO>

We’ll focus on the third one. Let’s create another extension method of the IDataReader interface. OMG! Code on interfaces? Yes… I’ll burn in hell, I know it… :D

This is the extension method with some auxiliary functions:

EDIT (2018-12-11) : Added custom attribute to the post, used to decorate our DTO.

As you can see the method iterates over the reader’s data and uses reflection to create an object per each row, map each column’s value to the correct property and then add the new object to the returned collection (*). Easy!


(*) image courtesy of oz-code, the best debugger addin for visual studio #imho ;)

Hope this helps! ;)

How To: Connect via SFTP using SSH.NET


In these times of APIs-everywhere, it may sound like an anachronism the use SFTP to connect to a remote server and get a list of files for exchanging information, but in the financial world (sadly) it’s more common than you think.

In my current project I’ve to connect to a remote server via Secure File Transfer Protocol (aka SFTP) using a user name, a RSA private key and a phassphrase. Once connected, the goal is to read some files from a remote folder and download them to a local folder.

To accomplish this I recommend you to use SSH.NET, one of the most popular SSH libraries for .NET, available on nuget.


More info about SSH.NET here:

Using SSH.NET to connect to the server

The most difficult part here is configure the connection, as usual. We need to provide the server url and port, in combination with the username and a file that contains the private key of the RSA certificate, and -of course- the passphrase.

Here’s the final code, and it works :)

Bonus * the trick *

I’ve spent a lot of time before it works, receiving a SSH exception “Invalid private key file”, but the same key file works fine when using the app Filezilla to connect, so… #WhatTheHell :S

PuTTY-User-Key-File-2: ssh-rsa
Encryption: aes256-cbc
Comment: rsa-key-KBL-20171006
Public-Lines: 6
Private-Lines: 14
Private-MAC: d288fffe72914eb62ed60a7e50fd8ce775

The key file (*.ppk) looks something similar to this

This is because the private key must be compatible with SshNet, so we have to convert the private key using PuTTY key Generator (the same app we used to create the certificate). Once opened in PuTTY, just export your file key to OpenSSH and use this new ppk file instead of the previous one.


If you open the new file you will see that the key file now begins with


And ends with


Finally, this is a valid SSH private key and it works like a charm ;)

Hope you enjoy it!

Upgrade SQL Server Assemblies .NET version

(This is a personal post, just a reminder)


Damn! :/

Each time a new version of .NET Framework is installed (or upgraded) in our SQL Server I need to update some assemblies we’re using in some projects.

Related posts

HowTo: Send Notifications from SQL Server using SignalR

HowTo: Redeploy CLR assemblies to SQL Server (Newtonsoft.Json version issue)

The issue

Executing the send message to hub stored procedure throws an exception:

EXEC clr.SendMessageToHub 'http://THESERVER:8082', 'NotificationsHub','SendMessageNotification', @jsonmsg

‘Could not load file or assembly ‘System.Runtime.Serialization, Version=, Culture=neutral, PublicKeyToken=b77a5c561934e089′ or one of its dependencies. Assembly in host store has a different signature than assembly in GAC.

Solution (it works for me):

1 – Remove clr stored procedure

2 – Remove assemblies (removing Falcon_SqlProxy should remove other dependences, if not, remove manually)

3 – Go to the GAC folder.: C:\Windows\Microsoft.NET\assembly\GAC_MSIL

4 – Find the assemblies (in this case only ‘System.Runtime.Serialization.dll and System.Neh.Http.dll were needed) and copy them to the SQL Assemblies folder (C:\Falcon\SqlServerAssemblies)

5 – Create assembly Falcon_SqlProxy again:

CREATE ASSEMBLY Falcon_SqlProxy from

6 – Create clr stored procedure again:

CREATE PROCEDURE [clr].[SendMessageToHub]
 @url [NCHAR](125),
 @hubName [NCHAR](75),
 @hubMethod [NCHAR](75),
 @message [NVARCHAR](MAX)
EXTERNAL NAME [Falcon_SqlProxy].[Falcon.NotificationsClient.SqlProxy.SqlServerNotificationsClientService].[SendMessageToHub]

7 – Check if SP is working:

SET @jsonmsg = '{"message": "GenerateProposalProgress", "description": "Resolving Portfolios", "userid": "' +
 '", "formName" : "Falcon.Client.Strategies.Proposals.Forms.ObjectSelectorForm", "value" : '
EXEC clr.SendMessageToHub 'http://elsuper:8082', 'NotificationsHub','SendMessageNotification', @jsonmsg

Hope this helps to my future me :P

Note: Today (12/07/2017) after moving some databases to other disks I had to do it again, so I’ve discovered other related issue.

It seems sometimes the USER SID changes after a backup/restore and throws an error:

The database owner SID recorded in the master database differs from the database owner SID recorded in database ‘Falcon’. You should correct this situation by resetting the owner of database ‘Falcon’ using the ALTER AUTHORIZATION statement.

So, before create the assemblies you (I mean me) you’ll need to change the database owner to sa.

EXEC sp_changedbowner 'sa'

exec sp_configure 'show advanced options', 1;

exec sp_configure 'clr enabled', 1;

exec sp_configure 'show advanced options', 0;

Despedida del programa Microsoft MVP

Después de 13 años como Microsoft MVP, este año me despido del programa. Y quiero hacerlo dando las gracias a todos, a la propia Microsoft y -sobre todo- a la gente que he conocido en este periodo: empleados de la propia Microsoft, toda la gente del programa MVP o de las comunidades, etc.

Al fin y al cabo, dejando a un lado los reconocimientos, conocer a toda esta gente ha sido lo mejor y más importante de este periodo, hasta el punto de poder decir que a algunos de mis mejores amigos los he conocido aquí. Y eso es mucho decir.

A decir verdad desde hace un par de años ya le había comentado a mamá Cristina (la gran madre de todos los MVP) mi intención de dejar el programa por diversos motivos, pero al final siempre me terminaba convenciendo para seguir por un tiempo…  que grande eres Cris y que razón tenías, muchas gracias por todo!

Entre estos motivos, destacaría el hecho de que hace tiempo que: ya no creo que por méritos propios merezca estar entre los reconocidos. Y esto es básicamente porque yo mismo elegí cambiar y hacer menos pequeñas contribuciones diarias (o casi) y centrarme en hacer dos o tres más grandes al año.

Ya no tengo ni el mismo tiempo ni -porque no decirlo- las mismas ganas que hace unos años… y sinceramente, hay gente que contribuye de ese modo mucho mejor que yo. Así que decidí que donde puedo aportar más, es por ejemplo en los eventos grandes tipo DotNetConference o organizando el GAPAND anual, que de momento seguirá.

Claro que no es el único motivo, siempre hay cosas en las que uno no está 100% de acuerdo, pero ahora no es el momento ni el lugar para hablar de ello. En este post sólo quiero, como he dicho al principio, dar las gracias a todos y decir que me lo he pasado pipa durante este tiempo. Siempre recordaré este periodo con muchísimo cariño!

Yo por mi parte bajaré el próximo día 5/6 de Octubre al Tech Summit en Madrid y aprovecharé para despedirme de todos los que me encuentre, ya que aprovecharé la excusa y me pillaré un tiempo sabático de todo esto, que ya me toca :P

Gracias a todos y ahora que sigan otros tirando del carro :D #NewBlood


Había pensado en esperar al 1 de Octubre para escribir esto, pero al final he decidido hacerlo ahora para no coincidir con la alegría de todos los nuevos (o renovados) MVP. Buen trabajo y felicidades, chicos! :D



HowTo: Redeploy CLR assemblies to SQL Server (Newtonsoft.Json version issue)



The Story

Hi again,

Some time ago I published a post about how to send notifications from SQL Server using SignalR. We are using SignalR self-hosted in a Windows Service in a separated server, and it had been running fine until now.

This week I had to set up a new SQL server which will replace the old one, which will replace the current ‘preprod’ one, and (entering recursive mode…).

After configuring the new SQL Server settings, I tried to update the previously imported assemblies to the database to the latest versions I’m currently using in the .NET project (Newtonsoft.Json & Microsoft.AspNet.SignalR.Client).

References map:

  • MyAssembly
    • Microsoft.AspNet.SignalR.Client
      • Newtonsoft.Json

As you can see, the app references the SignalR client, which references Newtonsoft dll.

So, in order to redeploy the assemblies it’s necessary to:

  • Remove all the CLR Stored Procedures that are using these assemblies, otherwise you cannot delete the assemblies.
  • Remove the main assembly and then all its dependencies in the correct order (Note: It’s not necessary to remove Framework assemblies if it’s not changed).
  • At this point you’ll be able to create the assembly again via CREATE ASSEMBLY

All dependent assemblies will be created again in the database (in this case Newtonsoft.Json & Microsoft.AspNet.SignalR.Client). Easy…


However, when tried to import the assembly to SQL Server I’ve got this error:

“Assembly ‘Microsoft.AspNet.SignalR.Client’ references assembly ‘newtonsoft.json, version=, culture=neutral, publickeytoken=XXXXXXXXX.’, which is not present in the current database. SQL Server attempted to locate and automatically load the referenced assembly from the same location where referring assembly came from, but that operation has failed (reason: version, culture or public key mismatch). Please load the referenced assembly into the current database and retry your request.”

I decided to import the assemblies one by one, from down to top.

Firstly I imported Newtonsoft.Json with no problems, but when I tried to import the SignalR client I received this error:


It seems a versions issue, because I’ve imported Newtonsoft.Json version 9, not 6! Let’s see what’s in the assemblies view:

select * from sys.assemblies


It’s correct. I’ve imported version 9 of the library… so why the hell SQL is looking for version 6? Well, if you inspect Microsoft.AspNet.SignalR.Client you’ll see that this library has a reference to Newtonsoft.Json version :S


This version number is used to determine the minimum version number (the library won’t work with previous versions), but it seems SQL Server is trying to match this version number with the assembly version number in its catalog, and if the number is different is unable to import the assembly.

If I’m wrong, please correct me! Thx :)


After spending some hours trying different solutions , the only way to solve that -at this time- is register a previous version of Newtonsoft.Json instead of the latest one.

Any version? Nope.

The only version it worked for me is 6.0.8, previous ‘6’ versions didn’t work because of this error:

“‘Method not found: ‘Newtonsoft.Json.Linq.JValue Newtonsoft.Json.Linq.JValue.CreateNull()’.'”

Finally, once I’ve copied the 6.0.8 version of the Newtonsoft.Json to the server path the import process has worked like a charm.


Now I’m looking for a better solution, because I don’t like using old versions of assemblies. I will update this post with a better solution as soon as possible :D

In the meanwhile, hope this helps.

GAPAND 2016 is over



Otro año ha pasado, y otra edición más del GAPAND a la saca. No, si al final esto va a convertirse en un clásico y todo… bueno, el tiempo lo dirá. Lo que si os puedo asegurar es que ha sido la mejor edición hasta la fecha, ya que el feedback de la gente (que al fin y al cabo es la única métrica que me interesa) es, a falta de una palabra más ‘fisna’: COJONUDO :D

Y eso no se paga con nada, sabéis? Porque vale, si, seguro que ya os imagináis que organizar un evento como este es un currazo. Y vaya si lo es! Lleva meses de trabajo duro, de coordinarse con mucha gente, de lidiar con 1000 temas distintos, de solucionar un montón de problemillas sobre la marcha, y todo por un día… un mísero día al año. Como nos gusta complicarnos la vida, eh? ;)

Pero es que todo eso no es nada cuando ves el ambientazo que se respira ese día. Cuando tienes la oportunidad de volver a ver a viejos amigos, y de hacer otros nuevos. Porque lo bueno del GAPAND (como en tantos otros eventos 100% comunidad) no está sólo en las sesiones, está en el contacto con la gente, y en el buen ambiente que se respira.



Así que desde aquí quiero daros las gracias a todos los que en mayor o menos medida habéis ayudado a que este evento hay sido posible: GRACIAS! GRACIAS! Y MUCHAS GRACIAS! :D

A todos, a los colaboradores porque sin ellos no tendríamos un sitio en el que hacer el evento (Universitat d’Andorra), ni desayuno (Sant Eloi), ni comida (DevsDNA), ni camisetas para los ponentes (Eset) y para la gente del staff (Semic), ni camisetas de regalo (Microsoft), ni cervezas (Hack & Beers), ni cosas para sortear (Caldea, Andorra Turisme, etc.).

A los ponentes, porque además de ser unos cracks hacen el esfuerzo de prepararse contenido súper interesante para crear una agenda de los más chula. Y todo eso lo hacen sin recibir nada a cambio… es más, incluso tienen que costearse su hotel! Así que imaginaros su compromiso con la comunidad! Si eso no es amor y pasíon por lo que te gusta, no se qué puede serlo. Un aplauso enorme… sois muy grandes!

Y cómo no, a todos los que habéis asistido al evento… sin vosotros esto no tendría sentido. Me alegra especialmente haber visto este año a bastante gente joven (venga, que los viejos necesitaremos el relevo, más pronto que tarde :D), estudiantes de la propia universidad, y gente que ha venido de bastante lejos a pasar un día con nosotros en este pequeño país… GRACIAS!

Mención aparte para mi colega Jordi, que ha tenido la paciencia de aguantarme otro año, y de tirar del carro cuando era menester. Ah! Y de buscarse la vida como nadie, y hasta de hacer de Community Manager. Thx mate! ;)

Os dejo con algunas fotos del evento. Si tenéis algunas fotos chulas por favor contactad con nosotros para ampliar el álbum de recuerdos… gracias! :)


Y un resumen en forma de Timeline, con algunos tweets del #gapand2016 :D

Video de mi charla en la #dotNetSpain2016

Hola de nuevo,

Lo prometido es deuda, he subido a Channel9 un video sobre mi sesión de ‘async Best practices’ de la pasada #DotNetSpain2016 :)


El vídeo dura una hora y veinte minutos aproximadamente y trata todos los contenidos de la sesión y la última demo con un poco más de profundidad.

Espero que os guste. Lo podéis ver online o descargar para más tarde.

Muchas gracias Isa por haber insistido, y al resto por el buen feedback! ;)

Materiales++ de mi sesión ‘async best practices’

Hola de nuevo,

Durante los últimos días me ha contactado bastante gente haciéndome preguntas sobre mi sesión de la pasada DotNet Spain Conference.

Aquí van un par de ellas con sus respuestas, por si son de vuestro interés ;)

“Las demos eran un proyecto de consola, pero qué consola era esa? No es el clásico cmd de Windows, verdad?”

Pues no, desde hace tiempo he sustituido el cmd por la estupenda ConEmu, que permite abrir distintos shells (PowerShell, PuTTY, mintty, GViM, etc) y distribuirlos como tabs o mosaicos.

Podéis descargarla desde:


En lugar de partir de cero para configurarla, os paso también mis settings por si queréis utilizarlas de punto de partida. Basta con ir a ‘Settings’/’Import’ y posteriormente ‘Save’ :)

ConEmu Settings (XML):

Y si no os gustan siempre podéis hacer un ‘Reset’ y empezar de cero :P

“En la última demo (la de cancelaciones a la BD) mostraste trazas en el SQL Profiler. Dijiste que tenías una plantilla creada para eso? La podrías compartir si no te importa?”

Of course! Esta plantilla básicamente muestra los eventos Starting y Completed de cada vez que se ejecuta un SQL statement (en este caso un Stored Procedure que tarda unos 5 segundos en completarse). Lo más interesante es que gracias a esta plantilla podemos ver si las peticiones terminan con éxito o han sido canceladas por el usuario, amén de la duración, que también nos puede indicar si ha terminado con éxito o no.


SQL Profiler template:

Para instalar la plantilla en tu SQL Profiler basta con descargarla y hacer doble click. Se abrirá el profiler y os informarà de que la plantila se ha importado con éxito.

A partir de aquí, cada vez que queráis usar esta plantilla de traza, basta con usarla como las que vienen ‘out of the box’:


Intentaré ir ampliando este post con aquellas que crea que pueden ser de interés general. Si tenéis alguna pregunta, ya sabéis :)

Nos vemos,


DotNet Spain Conference 2016… I was there!


El pasado miércoles se celebró en Madrid el mayor evento del año en tecnologías de desarrollo Microsoft. Un evento al que han asistido más de 1700 personas de forma presencial y unos cuantos miles más en streaming por Internet.

Y sólo puedo decir una cosa: ha sido una experiencia cojonuda a todos los niveles.

Me he rencontrado con viejos amigos y he conocido otros nuevos, he podido asistir a algunas sesiones ‘pata negra’ de otros colegas, he visto y oído al gran jefe Satya Nadella, y para rematarlo he tenido la oportunidad de poner mi granito de arena con una sesión sobre ‘async best practices’ la cual, por el feedback recibido parece ser que ha gustado a mucha gente, así pues… ¿qué más se puede pedir?

Como bastante gente me ha preguntado, aquí os dejo las slides de mi sesión, así como el código con las demos y la demo sorpresa final:

Slides on Slideshare

Code on GitHub


La verdad es que hacía bastantes años que no hacía una sesión para tanta gente, y os tengo que confesar que los 5 minutos antes de empezar fueron ‘complicadillos’. La ventaja es que al fin y al cabo todos los frikis somos como una gran familia, de modo que al empezar la sesión y empezar a hablar de nuestras cositas los nervios desaparecieron desde el minuto cero y todo fue como una seda.


Un 10 para la organización. Después de haber podido organizar unos cuantos eventos por mi cuenta y saber lo complicado que es, no quiero ni imaginarme lo que tiene que ser montar un evento de este calibre… chavales, lo habéis bordado. Os podéis tomar unos días de merecidas vacaciones :D

Creo poder decir sin lugar a dudas que ha sido el mejor evento al cual he asistido en España. Felicidades a todos!

Y como al fin y al cabo este es mi blog, me voy a dar un poco de autobombo, que coño! ;)

Aquí van algunos tuits, gracias a todos por el buen feedback!