HowTo: Send Notifications from SQL Server using SignalR

sr

Overview

In our current project we’ve to push some notifications directly from the database server (SQL Server 2014) to different apps. For example, we’ve scheduled some SSIS (SQL Server Integration Services) projects that pre-calculates large amounts of data. And well, should be nice notify all the apps when new data is available without unnecessary round trips to database. And you know? we’re already using SignalR in our apps… so, why not try to build a lightweight client for sending SignalR notifications?

Dear architects, please, don’t judge me :P

The idea

  1. I finally decided to create a small project in C# that connects to our SignalR (which is self-hosted in a Windows Service). That project contains a single method for sending notifications with a message (just a string for simplify).
  2. Once the project is ready and tested, the next step is create an ASSEMBLY, that is, a module that contains class metadata and managed code as an object in an instance of SQL Server. By referencing this module, common language runtime (CLR) functions, stored procedures, triggers, user-defined aggregates, and user-defined types can be created in the database.
  3. And finally, create a Stored Procedure that use our previous assembly CLR function (for isolation purposes and decoupling), and then use it in our SSIS projects with a simply ‘Execute SQL Task‘ when the process finalizes.

Creating the C# project

Create a blank class Library project and add the SignalR client (Nuget will add depending assemblies -like Newtonsoft.Json- automatically)

addSignalRClient

Once we added SignalR Client references to the project we need to create the C# method, so let’s add a class item to the project with this code:

public class SqlServerNotificationsClientService
{
    internal static HubConnection hubConnection = null;
    internal static IHubProxy hubProxy = null;
    [SqlFunction()]
    public static void SendMessageToHub(
        string severURL, string hubName, string hubMethod, params object[] args)
    {
        try
        {
            if (hubConnection == null || isConnectionClosed())
                hubConnection = new HubConnection(severURL.Trim()) 
                { Credentials = CredentialCache.DefaultNetworkCredentials };
            if (hubProxy == null || isConnectionClosed())
                hubProxy = hubConnection.CreateHubProxy(hubName.Trim());
            if (isConnectionClosed())
                hubConnection.Start().Wait();
            if(args.Length == 0)
                hubProxy.Invoke(hubMethod.Trim()).Wait();
            else
                hubProxy.Invoke(hubMethod.Trim(), args).Wait();
        }
        catch (Exception exc)
        {
            SqlContext.Pipe.Send(string.Format("{0}.{1} error: '{2}'{3}", 
                hubName.Trim(), hubMethod.Trim(), exc.Message, Environment.NewLine));
        }
    }

    private static bool isConnectionClosed()
    {
        return !(hubConnection.State == ConnectionState.Connected
            || hubConnection.State == ConnectionState.Reconnecting
            || hubConnection.State == ConnectionState.Connecting);
    }
}

Note: Before continue, take a look to the last parameter ‘args’. This parameter allows you to pass a different number of arguments to this method, which is nice because when we invoke the hub’s method we can use the same argument type. Unfortunately this argument type is not compatible with SQL CRL and will crash when creating the Stored Procedure in the database :( So we need to change this argument to a simple string.

This code is just an example, maybe would be a better idea split it into different methods (remember the ‘S’ of SOLID?) :)

Let’s assume the code is ok and is able to connect and send the notification. Now…

Create the SQL Assembly

In order to deploy your assemblies to a SQL Server you need the same version of the .NET Framework installed on the server. Yep, it’s too obvious but…

Create a folder in the server called ‘C:\SQLServerAssemblies’ (or wherever you want) and copy all the assemblies generated by the previous project.

signalRClientBaseAssemblies

But we’ve not finished yet… Now we’ve to add some related assemblies (assemblies dependencies). Otherwise, assembly creation will fail.

signalRClientFullListAssemblies

You can find these assemblies in the same server, in the correct version of .NET Framework. In my case:

C:\Windows\Microsoft.NET\Framework64\v4.0.30319 -> Microsoft Framework 4.5

Once we’ve all the necessary assemblies copied, let’s create the assembly. First we need to activate some things in the server:

ALTER DATABASE Falcon SET trustworthy ON

exec sp_configure 'show advanced options', 1;
GO
RECONFIGURE;
GO

exec sp_configure 'clr enabled', 1;
GO
RECONFIGURE;
GO

exec sp_configure 'show advanced options', 0;
GO
RECONFIGURE;
GO

CREATE ASSEMBLY Falcon_SqlProxy from 
  'C:\SQLServerAssemblies\Falcon.NotificationsClient.SqlProxy.dll' 
WITH PERMISSION_SET = UNSAFE 


signalRSqlAssemblies

Yes! Assemblies created successfully in SQL Server ;)

Creating the SP

Finally we only need to create the Stored Procedure. I recommend you use a custom schema. I’ve created one called ‘clr’

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

Remember the note? Have you changed the last argument of the method  SendMessageToHub to a string?

If you did it and copied all the assemblies to the folder it should have worked like a charm ;)

Now it’s time to test our SP… Success!

signalR_SPOk

Bonus:

If you wanna use this SP from a SSIS project to send notifications to your apps, just add a Execute SQL Task to your project and configure the SQL statement:

signalRExecuteSQLTask

Hope this helps! :)

Curiosity: What happens if you remove all the Trim() in the c# code? Why connection to SignalR fails? ;)

See you,

DotNet Spain Conference 2015

Este fin de semana he tenido la oportunidad de participar en el mayor evento de la comunidad .NET en España hasta la fecha.

Se ha realizado en la Universidad de Alcalá, con record de asistentes. Cosa que no me extraña viendo la cantidad de charlas y hands on lab que se han realizado en varios tracks en paralelo. La verdad es que ha sido un éxito a todos los niveles, y sólo puedo decir que va a ser difícil superarlo en futuras ediciones. Se ha puesto el listón muy alto! :D

dotnetspain

En lo que respecta a mi, tuve la gran suerte de presentar -y que me aprobaran- una charla conjunta con mi colega Alex Casquete, sobre buenas prácticas sobre async, algo que precisamente no es mainstream pero que curiosamente ha tenido una aceptación muy buena. De hecho ha sido mejor de la que yo pensaba que podía tener en un principio, así que muchas gracias a todos por tanto feedback positivo!

Os dejo aquí las slides de la charla y las demos que utilizamos, ya que -para mi sorpresa- me las ha pedido bastante gente:

Sin embargo, en un evento como este no todo son sesiones técnicas. Lo mejor de todo es que he tenido la oportunidad de volver a encontrarme con un montón de viejos conocidos, con los que compartir buenos momentos y algunas cervezas, de modo que el networking ha sido… fantástico!

Os dejo con algunas fotos del eventazo. Gracias a todos! :D

How to: Remove diacritics (accents) in C# strings

cs

Quick note: How to remove diacritics (accents) from a C# string?
Use this extension method ;)

public static string RemoveDiacritics(this string input)
{
    var stFormD = input.Normalize(NormalizationForm.FormD);
    var len = stFormD.Length;
    var sb = new StringBuilder();
    for (int i = 0; i < len; i++)
    {
        var uc = System.Globalization.CharUnicodeInfo.GetUnicodeCategory(stFormD[i]);
        if (uc != System.Globalization.UnicodeCategory.NonSpacingMark)
        {
            sb.Append(stFormD[i]);
        }
    }
    return (sb.ToString().Normalize(NormalizationForm.FormC));
}

How to: Server Side Filter database (Entity Framework + DevExpress Filter control)

Imagine you have a table with millions of rows. It’s not a good idea showing all these data in a grid or similar, and it doesn’t matter you’re developing a web or desktop application… It’s not a good idea at all.

In these cases we should use conditional filters applied in the data source (usually a database) to get part of the information and return only the rows that match with the specified criteria. Using this technique the amount of network traffic could be reduced to a hundreds of rows or less, depending on the criteria.

There are different ways of accomplish that, but today I’d like to focus on apply this technique using a control from the DevExpress suite: The Filter Editor control.

DXFilterControl

 

As you can see the control looks nice, and it’s quite simple to use: You just need to link this control to another (usually a Grid or Tree), and automatically will retrieve the columns and values information from the source control. Then you’ll be able to filter the linked control and get a filter string which you can reuse later in other controls of the suite.

However the previous filter has the disadvantage that is a client filter, and is applied after we retrieve all the rows from the data source, not before. Our goal have to be apply the filter before getting the information.

Today I’ve been playing a little bit with this control, using a SQL Server database that contains a products table with 3.7 million rows and Entity Framework 6.0 as an ORM.

Note: For this test I’d like to use a SQL Tabled-Valued Function to access the data instead of using the default DbSet, because it’s supposed that I’ve to do some hard calculations inside the function. But you can also use the EF DbSet directly, as usual.

EFModel

Now our goal here is:

  1. Create at runtime a filter over the Products table using the filter control.
  2. Parse that filter string and adapt it to the EF syntax.
  3. Append the filter to the mapped function (thx to IQueryable!) before execution.
  4. Finally, execute the filtered query against the data source

First we need to add an EF designer (we won’t use Code First because we’re going to map a Tabled valued function with is not supported). And add the table Products and the GetAllProducts function.

In our form, we just need to add a couple of Devexpress controls: A GridControl and a FilterControl, linked using the FilterControl SourceControl property (this allows the filter control to retrieve the names and data types of the grid columns).

DynamicFiltersEF

Then we’ll add three buttons. The first one will retrieve the first 25.000 rows from the database (previously I’ve tried to get all rows from table, but the app crashed with an OutOfMemory exception ;))

The second button (this is the interesting one) will execute the current filter from the FilterControl.

Let’s code:

The first button is really simple. It’s just a query over the GetAllProducts function adding a where clause which will be resolved later (because EF is based on IQueryable<T>) when calling ToListAsync method.

private async void simpleButton1_Click(object sender, EventArgs e)
{
    using (var context = new TestBigDbEntities())
    {
        var filteredproducts = context.GetAllProducts().Where(p => p.ProductId <= 25000);
        await ExecuteQueryAsync(filteredproducts);
    }
}

private async Task ExecuteQueryAsync(IQueryable filteredProducts)
{
    clock.Restart();
    productBindingSource.DataSource = await filteredProducts.ToListAsync();
    clock.Stop();
    Text = clock.ElapsedMilliseconds.ToString("n2");
}

For the second button we need something more. Something that allows us to translate the filter into a valid where clause, and append it to the function.  The good news are that this functionality is provided natively by Devexpress:

private async void simpleButton2_Click(object sender, EventArgs e)
{
    var converter = new DevExpress.Data.Linq.CriteriaToEFExpressionConverter();
    using (var context = new TestBigDbEntities())
    {
        var filteredproducts =
                DevExpress.Data.Linq.Helpers.CriteriaToQueryableExtender.AppendWhere(
                context.GetAllProducts(),
                converter,
                filterControl1.FilterCriteria);
        await ExecuteQueryAsync(filteredproducts);
    }
}

All we’ve to do is that:

Clean and simple. The first one creates a converter for translating the filter into EF syntax, while the second one appends the filter to the EF function using the previous converter.
And finally, here’s the SQL in the profiler. Exactly what I’ve expected.

SQLProfiler

So well… if you’re using Devexpress in your current project there’re no excuses for not including dynamic filters that user can save and reuse at runtime, uh? :P

Happy coding!

Bonus

The third button code shows how to get the TSQL syntax from the Filter control, that you can use it to query the database directly ;)

private void simpleButton3_Click(object sender, EventArgs e)
{
    var tsql = DevExpress.Data.Filtering.CriteriaToWhereClauseHelper.
        GetMsSqlWhere(filterControl1.FilterCriteria);
    MessageBox.Show(tsql);
}

BonusTSQLFilter

Extract extra large Icon from a file, including network paths!

Edit (04/16/2014): Added a new function called GetBitmapFromFolderPath, which returns the associated icon from the path to a Folder (or drive!).

image

Sometimes we are required to show a list of files with their associated icons. This task sounds quite easy, and in fact it is… Except if you have to deal with files in network paths or you want to get different icons sizes, apart the typical 32×32.

If you want to achieve this using managed code (the easy way), there is a static method called ExtractAssociatedIcon under the class Icon to achieve that, but sadly this method doesn’t work with UNC paths neither return other sizes that 32×32 pixels.

In my current project I needed to show four different icons sizes (including the extra-large icon, also called “jumbo”), so I’ve decided to use some functions and structures from the Win32 API. Of course, if anyone knows a better way to do it, please contact with me ASAP :)

First, let’s see the final code:


private static IntPtr GetIconHandleFromFilePath(
    string filepath, IconSizeEnum iconsize)
{
    var shinfo = new Shell.SHFILEINFO();
    const uint SHGFI_SYSICONINDEX = 0x4000;
    const int FILE_ATTRIBUTE_NORMAL = 0x80;
    const int ILD_TRANSPARENT = 1;
    uint flags = SHGFI_SYSICONINDEX;
    var retval = SHGetFileInfo(filepath, FILE_ATTRIBUTE_NORMAL,
        ref shinfo, Marshal.SizeOf(shinfo), flags);
    if (retval == 0) throw (new System.IO.FileNotFoundException());
    var iconIndex = shinfo.iIcon;
    var iImageListGuid = newGuid("46EB5926-582E-4017-9FDF-E8998DAA0950");
    Shell.IImageList iml;
    var hres = SHGetImageList((int)iconsize, ref iImageListGuid, out iml);
    var hIcon = IntPtr.Zero;
    hres = iml.GetIcon(iconIndex, ILD_TRANSPARENT, ref hIcon);
    return hIcon;
}

In this code we’re using several API calls. Here’s the tricky part:

First, we need to make call to the SHGetFileInfo function that receives a reference to a structure of type SHFILEINFO, which contains the index of the icon image within the system image list. We will use this index later.

Then we’ve to make is a second call to the SHGetImageList function that receives an output parameter with an IImageList structure, which is modified within the function.

This struct retrieve a COM interface and we need to keep in mind a couple of things:

a) We must use the GUID of this interface in the declaration:

[ComImportAttribute()]
[GuidAttribute(“46EB5926-582E-4017-9FDF-E8998DAA0950”)]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]

b) And in the improbable case you are going to deploy your project over XP, remember that SHGetImageList is not exported correctly in XP. For this reason you must hardcode the function’s entry point. Apparently (and hopefully) ordinal 727 isn’t going to change…

Once we have that COM interface, we only need to call its GetIcon method, passing a parameter with the desired size, and obtaining a handle to the icon by reference.

After having the handle its really simple create an Icon from the handle, and then convert to a Bitmap, BitmapSource or other:


public static System.Drawing.Bitmap GetBitmapFromFilePath(
 string filepath, IconSizeEnum iconsize)
{
    IntPtr hIcon = GetIconHandleFromFilePath(filepath, iconsize);
    var myIcon = System.Drawing.Icon.FromHandle(hIcon);
    var bitmap = myIcon.ToBitmap();
    myIcon.Dispose();
    DestroyIcon(hIcon);
    SendMessage(hIcon, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
    return bitmap;
}

Tip: It’s very important don’t forget to destroy the resources (Icon) when working with the Win32 API!

This method calls the previous one, obtains the icon’s handle and then creates the icon using the handle. Then creates a bitmap from the icon, destroys the icon and returns the bitmap.

I’ve also created an enumeration with the different flags values:

private const int SHGFI_SMALLICON = 0x1;
private const int SHGFI_LARGEICON = 0x0;
private const int SHIL_JUMBO = 0x4;
private const int SHIL_EXTRALARGE = 0x2;
private const int WM_CLOSE = 0x0010;

public enum IconSizeEnum
{
 SmallIcon16 = SHGFI_SMALLICON,
 MediumIcon32 = SHGFI_LARGEICON,
 LargeIcon48 = SHIL_EXTRALARGE,
 ExtraLargeIcon = SHIL_JUMBO
}

Finally, retrieving the icon from a file (inclusive if it’s in a network location) and specify the desired size it’s as easy as:

var size = ShellEx.IconSizeEnum.ExtraLargeIcon;
var ofd = new OpenFileDialog();
if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
    labelFilePath.Text = ofd.FileName;
    pictureBox1.Image = ShellEx.GetBitmapFromFilePath(ofd.FileName, size);

}

image

Hope this helps! ;)

Download the source code.

Global Windows Azure Bootcamp

On April 27, something very cool is happening. A bunch of Windows Azure MVP’s and community activists are organizing a Global Windows Azure Bootcamp. This is a completely free, one-day training event for Windows Azure, all organized by the community, and presented in person all over the World.

clip_image001

I’m not sure if this is the largest community event ever – it is very cool to see how many places this event is happening.  Below is the location map as it stands today – and new locations are being added daily. Right now there are almost 100 locations and several thousand attendees already registered to take part.  Browse the location listings to find a location near you.

clip_image003

If you are interested in learning about Windows Azure or want more info, checkout the Global Windows Azure Bootcamp website to learn more about the bootcamps.  Then find a location near you, sign-up and attend the event for free, and get involved with the Windows Azure community near you!

Hope this helps,

Serializar listas genéricas en aplicaciones WinRT

andorraenamora

Edit (11/03/2013): Gracias a una sugerencia del colega MVP Javier Torrecilla se ha modificado el código para especificar el Encoding (UTF8Encoding) y así evitar problemas. Thx dude! :)

 

Hola de nuevo,

Rompiendo un poco con la serie de posts sobre las ‘Parallel Series’, hoy quiero escribir acerca de algo totalmente distinto. Y es que ando haciendo mis pinitos con mi primera aplicación Windows Store, y me estoy encontrando con bastantes cosas que no conozco, y que desde mi absoluto desconocimiento de la plataforma, encuentro bastante tediosas de realizar.

Hoy por ejemplo estaba tratando de almacenar ciertos datos en local -ya que para esta aplicación no quiero depender de ninguna base de datos porque en realidad son cuatro datos- y no he encontrado una forma directa de persistir listas genéricas en el sistema de ficheros local.

Si que es cierto que las herramientas están ahí para ser usadas ‘a mano’, pero he echado en falta una forma más sencilla de hacerlo (que no quiere decir que no exista), de modo que he creado una clase para facilitarme el proceso.

Su propósito es muy específico, pero básicamente permite guardar y leer cualquier lista genérica List<T> a un fichero, que puede ser guardado en la carpeta Local, Temporal o de Roaming.

La clase es la siguiente (aunque supongo que es muy mejorable):

using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Xml.Serialization;
using Windows.Storage;

namespace MySampleApp.Domain
{
    public class LocalStorage<T>
    {
        public List<T> Data { get; set; }
        public string Filename { get; set; }
        public StorageFolder StorageFolder { get; set; }

        public LocalStorage(string filename)
        {
            Filename = filename;
            Data = new List<T>();
            StorageFolder = ApplicationData.Current.LocalFolder;
        }

        public async Task SaveAsync()
        {
            if (!await fileExistAsync(Filename))
            {
                await StorageFolder.CreateFileAsync(
                    Filename, CreationCollisionOption.ReplaceExisting);
            }
            await saveToLocalStorageAsync();
        }

        public async Task<List<T>> LoadAsync()
        {
            if (await fileExistAsync(Filename))
            {
                return await loadFromLocalStorageAsync();
            }
            else
            {
                await StorageFolder.CreateFileAsync(Filename);
                return null;
            }
        }

        public async void DeleteAsync()
        {
            try
            {
                if (await fileExistAsync(Filename))
                {
                    var file = await StorageFolder.GetFileAsync(Filename);
                    if (file != null) await file.DeleteAsync();
                }
            }
            catch (Exception)
            {
                throw;
            }
        }

        private async Task<bool> fileExistAsync(string fileName)
        {
            try
            {
                await StorageFolder.GetFileAsync(fileName);
                return true;
            }
            catch
            {
                return false;
            }
        }

        private async Task saveToLocalStorageAsync()
        {
            var sessionFile = await StorageFolder.CreateFileAsync(
                Filename, CreationCollisionOption.ReplaceExisting);
            using (var sessionRandomAccess = await sessionFile.OpenStreamForWriteAsync())
            {
                var serializer = new XmlSerializer(typeof(List<T>), new Type[] { typeof(T) });
                serializer.Serialize(new StreamWriter(sessionStream, new UTF8Encoding()), Data);
                await sessionStream.FlushAsync();
            }
        }

        private async Task<List<T>> loadFromLocalStorageAsync()
        {
            var sessionFile = await StorageFolder.CreateFileAsync(
                Filename, CreationCollisionOption.OpenIfExists);
            if (sessionFile == null) return null;
            using (var sessionInputStream = await sessionFile.OpenReadAsync())
            {
                var reader = XmlReader.Create(new StreamReader(
                    await sessionFile.OpenStreamForReadAsync(), new UTF8Encoding()));
                var serializer = new XmlSerializer(typeof(List<T>), new Type[] { typeof(T) });
                Data = (List<T>)serializer.Deserialize(reader);
                return Data;
            }
        }
    }
}

Y para utilizarla basta con disponer de una lista de objetos de un tipo T:

var customers = new List<Customer>();
customers.AddRange(getSampleCustomers());

Y posteriormente crear una instancia de LocalStorage:

var storage = new MySampleApp.Domain.LocalStorage<Customer>("customers.xml");
//Opcionalmente podemos cambiar la carpeta de destino (por defecto es LocalStorage)
storage.StorageFolder = Windows.Storage.ApplicationData.Current.RoamingFolder;
//Añadimos los datos a persistir
storage.Data.AddRange(customers );
//Guardar los datos al fichero customers.xml
await storage.SaveAsync();
//Recuperar los datos del fichero customers.xml
await storage.LoadAsync();
//Eliminar el fichero customers.xml
storage.DeleteAsync();

Si alguien tiene alguna sugerencia o mejora que no dude en escribir un comentario.

Nos vemos! :)

Problemas de concurrencia

Why concurrent collections?

En un mundo en el que los procesos ya no son secuenciales sino paralelos, es cada vez más posible encontrarnos con problemas de concurrencia al acceder a recursos compartidos. Conceptualmente hablando, esto es algo a los que los desarrolladores ya estamos acostumbrados cuando trabajamos con gestores de bases de datos como Oracle o SQL Server, ya que varios usuarios pueden acceder o modificar la información al mismo.

Sin embargo, la gran mayoría de los desarrolladores pocas veces hemos tenido que lidiar con bloqueos en colecciones en menoria, ya que no todo el mundo crea aplicaciones en las que varios threads acceden a recursos compartidos. De hecho, si alguna vez has lo tenido que hacer sabrás perfectamente que antes de la aparición de la TPL era una de las disciplinas más complejas dentro del desarrollo de software. Algo que favorece la calvície :)

Sin embargo, desde la aparición de la TPL en el .NET Framework 4.0 es mucho más sencillo desarrollar aplicaciones que ejecuten procesos en paralelo o de forma asíncrona, pero esto conlleva que en ocasiones nos olvidemos que hay algunos threads que se ejecutan al mismo tiempo, y esto podría llevar a producir efectos no deseados cuando se trata de acceder a recursos compartidos, como una colección de elementos en memoria.

Por ejemplo: supongamos un escenario en el que tenemos una lista de clientes en memoria y un par de tareas (Task) que acceden a esa colección. La primera tarea podría estar verificando si todos los clientes cumplen una condición X, y si no la cumplen eliminar el cliente de la colección. Mientras tanto la segunda tarea podría estar actualizando alguna propiedad de los clientes, como la edad.

Un escenario real

Encierra este escenario algún peligro? A priori podemos pensar que no. Basta con que la segunda tarea verifique si el cliente existe en la colección antes de actualizarlo. Seguro? Pues no. Al menos no basta si la colección que estamos usando no es una de las nuevas definidas dentro del namespace System.Collections.Concurrent. Y para verlo mejor, hagamos un pequeño ejemplo con código, que es lo que nos gusta a todos.

1 – Partiremos de una clase base Customers con un método que crea n objetos de ejemplo:

public class Customer
{
    public int CustomerId { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }

    public static List<Customer> GetSampleCustomers(int n)
    {
        Random r = new Random(Guid.NewGuid().GetHashCode());
        List<Customer> customers = new List<Customer>();
        for (int i = 0; i < n; i++)
        {
            customers.Add(new Customer()
            {
                CustomerId = i,
                Name = string.Format("Customer {0}", i),
                Age = r.Next(20, 80)
            });
        }
        return customers;
    }
}

2 – A continuación en un formulario declararemos e inicializaremos una colección de tipo Dictionary para almacenar estos objetos cliente:

Dictionary<int, Customer> dic1 = new Dictionary<int,Customer>();

3 – Y en el constructor la rellenamos con 100.000 clientes de ejemplo:

public Form1()
{
    InitializeComponent();
    dic1 = Customer.GetSampleCustomers(100000).ToDictionary(p => p.CustomerId);
}

4 – Ahora, vamos a complicar un poco más el ejemplo y crearemos dos métodos que simulen el borrado y la actualización de los objetos que contiene la colección. Luego llamaremos a éstos métodos para 100 objetos aleatorios de forma paralela para ver que efectos se producen:

private void deleteItem1(int id)
{
    if (dic1.ContainsKey(id) && dic1[id].Age < 30) dic1.Remove(id);
}

private void updateItem1(int id)
{
    if (dic1.ContainsKey(id)) dic1[id].Age++;
}

private void button1_Click(object sender, EventArgs e)
{
    for (int i = 0; i < 100; i++)
    {
        Random r = new Random(Guid.NewGuid().GetHashCode());
        int id = r.Next(1, dic1.Count);
        Task.Factory.StartNew(() => deleteItem1(id));
        Task.Factory.StartNew(() => updateItem1(id));
    }
}

Ejecutamos la aplicación y cuendo pulsemos el botón puede pasar que obtengamos un bonito error como este:

image

Concretamente en la línea que incrementa la edad del cliente:

image

Y digo que puede pasar, pero no es seguro. Tal vez tengamos que repetir el proceso varias veces, y es que en un entorno en el que varios threads acceden a un recurso compartido nada nos asegura que se produzca el error. A veces pasa a la primera y a veces a la cuarta, pero hay que tener presente que cuando esté en producción siempre pasará en viernes por la tarde cinco minutos antes de empezar el fin de semana ;)

Analicemos el porqué del error, por que ¿cómo es posible que no encuentre el elemento en la colección si precisamente en la línea anterior verificamos su existencia? Pues ahí está la gracia, que lo verificamos en la línea anterior. A nosotros -pobres developers- nos parece que la línea anterior en la que verificamos si existe y la línea actual en la que lo emininamos van seguidas una detrás de otra, pero realmente en un entorno asíncrono hay todo un mundo entre ambas. Y eso es porque ambas líneas se ejecutan de forma separada, sin ningún tipo de bloqueo, no existe Transaccionalidad al estilo de las bases de datos, de modo que mientras el primer thread verifica que existe ese elemento en la colección y lo borra, llega el segundo thread y… ZASCA! Lo elimina.

image

El nuevo ConcurrentDictionary

Para estos casos (y muchos otros) aparecen en escena un nuevo conjunto de colecciones especializadas en entornos multithreading. Una de las más populares es una variación del clásico Dictionary, el ConcurrentDictionary. Esta clase proporciona una série de métodos alternativos para agregar, modificar o eliminar elementos llamados TryAdd, TryUpdate o TryRemove. La ventaja de éstos métodos es que son transaccionales y proporcionan Atomicidad. Es decir, garantizan que en caso de que se invoquen, se ejecuten totalmente: O bien se añade/modifica/elimina el elemento de la colección o bien se devuelve el valor False porque no se ha podido realizar la operación. También proporciona un método TryGetValue que intenta obtener un elemento de la colección. Todos estos métodos implementan en su interior bloqueos (lock), para asegurar que nadie más accede al elemento mientras se realiza la operación solicitada.

Un ejemplo real que no falla ;)

Vamos a modificar el ejemplo anterior usando esta nueva colección, para observar su comportamiento en el entorno anterior.

1 – Agregaremos un ConcurrentDictionary después de la declaración del anterior:

ConcurrentDictionary<int, Customer> dic2 =
    new ConcurrentDictionary<int, Customer>();

2 – Lo rellenamos en el constructor, al igual que hacíamos antes. Aquí podemos ver la primera diferencia, ya que usamos uno de los nuevos métodos TryXXX:

Customer.GetSampleCustomers(100000).ForEach(c => dic2.TryAdd(c.CustomerId, c));

3 – A continuación vamos a crear los métodos equivalentes para eliminar y modificar el elemento en la colección:

private void deleteItem2(int id)
{
    Customer c;
    if(!dic2.TryRemove(id, out c)) Console.WriteLine("Error deleting!");
}

private void updateItem2(int id)
{
    Customer c;
    if (!dic2.TryGetValue(id, out c))
    {
        Console.WriteLine("Error getting value");
    }
    else
    {
        Customer newc = new Customer() { CustomerId = c.CustomerId, Name = c.Name, Age = c.Age };
        newc.Age++;
        if (!dic2.TryUpdate(id, newc, c)) Console.WriteLine("Error updating!");
    }
}

En el borrado usamos el método TryRemove que necesita el id del elemento a eliminar, y en caso que lo elimine con éxito, devuelve True y el objeto eliminado en el segundo parámetro out (de salida) del método. En caso que no lo elimine devuelve False y el valor default del objeto a eliminar en el segundo parámetro.

La actualización es un poco más compleja, ya que primero debemos asegurarnos de que el elemento a modificar existe en la colección mediante el método TryGetValue, que básicamente funciona igual que el método TryDelete anterior. Una vez hemos comprobado que dicho objeto existe en la colección creamos una réplica del cliente, modificamos su edad y llamamos al método TryUpdate, el cual se encarga de la modificación.

Es interesante notar que para que la modificación se realice correctamente debemos informar de la clave del objeto en la colección, el nuevo valor del objeto (en este caso la réplica del cliente a la que hemos modificado la edad) y un tercer parámetro que es el valor original del objeto, que es usado para comparar.

4 – Probemos los nuevos métodos en otro botón:

private void button2_Click(object sender, EventArgs e)
{
    for (int i = 0; i < 100; i++)
    {
        Random r = new Random(Guid.NewGuid().GetHashCode());
        int id = r.Next(1, dic1.Count);
        Task.Factory.StartNew(() => deleteItem2(id));
        Task.Factory.StartNew(() => updateItem2(id));
    }
}

Ejecutamos y si vamos a la ventana de consola podremos observar el resultado:

Error getting value
Error getting value
Error getting value
Error getting value
Error updating!
Error getting value
Error getting value
Error getting value

En la mayoría de los casos, obtenemos un error de lectura porque el elemento ya ha sido eliminado de la colección, pero en algunos casos el error se producirá en el método TryUpdate. Eso significa que la llamada a TryGetValue encuentra el elemento, pero cuando lo vamos a modificar éste ya no existe en la colección. Perfecto! :)

Conclusión

La ventaja de usar colecciones específicas en entornos multithreading es clara: Garantizan que el acceso a los recursos compartidos sea transaccional, al más puro estilo de las bases de datos. De acuerdo que su programación sea un poco más compleja, pero no tiene nada que ver con lo que se tenía que hacer antiguamente cuando no existía la TPL.

Por otro lado, el uso de estas colecciones debe hacerse sólo en aquelos casos en los que se accede a recursos compartidos, ya que si no, por un lado estamos complicando nuestro codigo y por otro lado un ConcurrentDictionary no es tan eficiente como lo és un Dictionary, sobre todo en entornos más de escritura que de lectura. Tenéis un post muy detallado al respecto del gran James (aka Black Rabbit) respecto a la diferencia de performance:

http://geekswithblogs.net/BlackRabbitCoder/archive/2010/06/09/c-4-the-curious-concurrentdictionary.aspx

Espero sacar tiempo para otro post en el que explicar otras colecciones de este namespace, como las ‘BlockingCollection’ o las ‘’ConcurrentBag’, que son colecciones más especializadas. La primera está optimizada para escenarios dónde el mismo thread produce y consume objetos, mientras que la segunda ha sido especialmente diseñada para escenarios de productor-consumidor.

Aquí tenéis un pequeño proyecto de ejemplo con el código:

https://www.dropbox.com/s/ej3u3s1v55ltb30/TPL04_Concurrency.zip

Nos leemos! :D

Volver al índice de contenidos

MVP Site interview (full version)

MVPPartySeattle2011_thumb[1]

Yesterday there was published my brief interview in the MVP site.

http://mvp.microsoft.com/en-US/Pages/default.aspx (english)

However, it is not the full version, that I publish here. Hope you enjoy it ;)

Lluís Franco Interview

Born in Barcelona (Spain) with more than 20 years of experience in the software development sector, Lluis Franco holds a number of Microsoft official certifications such as MCP and MCTS and has been recognized as MVP the past 10 years due to his active participation and moderation of the Spanish MSDN forums since 1997.

Lluis currently works as project development manager at FIMARGER a financial services company based in Andorra, where he founded the AndorraDotNet user group in 2007, being the first and almost unique user group based in this country.  Since 2010 he is also the president of the Andorra IT Professionals Virtual Association.

He maintains several blogs and is a regular host and speaker in local events in the region, such as the “Geek-A-Paloozaa” conference that took place in April 2012. He also contributes articles to the technological section of a local newspaper.

He resides in Andorra for more than 20 years, he is married and father of two girls.

1. What does it mean being an MVP?

It is something much more important to me now than when I was first awarded, as I wasn’t aware at that time of all the wonderful things that would happen to me and the great people I would meet over the time. Not only at a technical level, what the MVP community of course has, but also for the human quality of the majority of MVPs. I met many of my current best friends thanks to the MVP Program.

2. How was your start in the community?

It happened just by chance. As it was the case of many professionals in the IT industry I needed help to solve a problem and posted a question in the today disappeared Microsoft newsgroups, where I got the answer to my questions. I just loved this way of learning from other community members and one day I decided to give something back to them and I started to reply questions there. Sometime after I started running a webpage, a blog, another blog…. At some point I did a presentation at a local user group meeting and finally I started a new user group, hosting events… And I still really enjoy doing these things today.

3. What would you ask Steve Ballmer?

Let me see… The first thing that comes to my mind is to ask about some Microsoft business segments long-term strategy. But this is something that almost everybody would ask him, so I would rather ask about the good old times… So Steve, in your opinion, what has been the most important fact in Microsoft’s history? I am talking about the key factor or point in time that has made Microsoft what it is today.

4. What is the best software product in history?

I am in love with C#. For me this is the most beautiful, complete, and powerful programming language ever created. So therefore, the best software product for me is Visual Studio 2012, the best IDE in history (or at least the best I have ever used, that are many). Talking about operating systems, I have no doubt; Windows 8 is far the best one. At that point I have to say that I have not always worked with Microsoft products.

5. What would you change on Visual C#?

I would continuously add functional characteristics to it. In the latest versions we have seen the introduction of generic functions, delegates management, LINQ and moreover, lambda expressions have made of C# the programming language I have always dreamed about. It is really fun programming on C#! However some things are still missing such as Nested functions, for example. So as some of my friends mentioned to me, this would make functions to be considered 1st level citizens. There are also other things such as making the Framework and IDE a bit lighter. There are many different types of objects there and some collections could be eliminated.

6. Which is the last book you have read?

I am a compulsive technical book reader. But I also love fiction and science fiction books. I have last read ‘Ready Player One’, a novel with plenty of references of the 80’s : videogames, music, films, role games, comics and even toys. It has a young writing style and I could identify myself with the background described there. I also read technical books and of course, my favorite one: ‘The Silmarillion’.

7. Which music CD would you recommend?

Just one? In that case I would go for Radiohead’s ‘Ok Computer’. I really love this band’s the first albums. I like different kind of music from Bach to the heavy metal, including Jazz music too.

8. What makes you an excellent MVP?

I don’t consider myself an “excellent MVP”. For me being an MVP means a person with some technical skills but that is willing to share his knowledge and passion with the community in different ways, helping others that have helped them before. So the most important thing here is having the right attitude. So somebody that considers himself as being better than others because of the MVP Award probably wouldn’t deserve this recognition.

9. What is in your “Computer Bag”?

There is less and less software and more and more hardware in there. Many small devices, adaptors, that make life easier when I’m travelling. And of course, and old souvenir from the old MVP Program times, a Swiss knife with the MVP logo on it that contains a complete set of different tools, that I of course, have never used, but I hold it just in case.

10. What is the best thing that happened to you since you are an MVP?

Popularity, women, money… (I’m kidding). Maybe having being nominated as “C# MVP Of The Year 2011” as it is an Award that you can have once you are MVP and the winner is chosen by other MVPs within the same expertise. My first trip to Seattle was also great, but if I had to choose just one thing, this would be the people that you meet. There isn’t anything better than a “geeks & beers” meeting.

11. What is your motto?

It is something really simple: “Try to be a better person”, somebody able to be a model for my daughters when they will be older.

12. Who is your hero?

Sheldon Cooper, Yoda, Spock, Frodo, Cálico Electrónico and Fuckowsky from ‘Memories of an engineer’… But if I had to choose just one, this would be Tony Stark: Playboy, millionaire, genius, and with his own armor. However in real life the true heroes are those that do the most difficult thing: doing what is right under any circumstance, for eg. a child with a serious disease that encourages his parents in the bad moments…  This is really a true hero.

13. What does success mean to you?

Enjoying your daily work! In fact this would include two things: first of all having a job, what is not easy in today’s world and secondly, doing what you really enjoy for life. This is a true luck in life and what really can make you happy.

14. What would be your personal Project for this year?

Let’s see… I have already been in Seattle this year, attending to the MVP Summit, and also have organized the “Geek-a-paloozaaa” event in Andorra. So I would like to finish a series of technical posts about parallel computing that I have started some months ago, for what I don’t find the time to at this moment. In the personal side, I would like to spend more time with my loving ones.

See you :-D

Entrevista para el MVP Site (versión completa)

MVPPartySeattle2011

Ayer me publicaron una entrevista en el sitio internacional del programa MVP:

http://mvp.microsoft.com/es-ES/Paginas/default.aspx (spanish)

Sin embargo, no es la versión completa. Ésta la publico aquí, sin cortes. Espero que os guste ;)

Entrevista a Lluís Franco

Nacido en Barcelona y con 20 años de experiencia en el sector del desarrollo de software, Lluís Franco posee varias certificaciones MCP y MCTS y ha sido reconocido como Microsoft MVP durante los últimos 10 años, principalmente por haber sido colaborador activo y moderador en los grupos de noticias y en los foros MSDN de Microsoft desde 1997.

Actualmente trabaja como responsable de desarrollo de proyectos en FIMARGE una empresa del sector financiero situada en Andorra. En 2007 fundó AndorraDotNet, el primer grupo de usuarios y en 2010 la asociación virtual de informáticos del país. Escribe en varios blogs, y es organizador y ponente habitual en los eventos de los grupos de usuarios de la región. También colabora con un periódico local escribiendo artículos para su sección tecnológica.

En lo personal vive en Andorra desde hace más de una década, está felizmente casado con una mujer encantadora y es padre de las dos niñas más guapas del mundo.

1. Que significa ser MVP para ti?

Algo mucho más importante ahora que cuando recibí mi primera nominación, ya que por aquel entonces no era consciente de toda la gente que iba a conocer ni de todo lo que iba a suceder en los próximos 10 años. Y con esto no me refiero sólo al nivel técnico de este colectivo -que lo tiene, y mucho- sino a la calidad humana, porque a muchos de mis mejores amigos actuales los he conocido a través del programa MVP.

2. Cómo empezaste a colaborar en la comunidad?

Sinceramente? Pues de casualidad. La verdad es que como muchos otros profesionales, un día hace unos cuantos años necesitaba ayuda para resolver una duda, y buscando me encontré los ya desaparecidos grupos de usuarios, en los que me ayudaron a resolver la mayoría de mis dudas. Eso me encantó y poco a poco decidí devolver el buen trato recibido ayudando también a otros. Después empecé con una web, un blog, otro blog, a hablar en pequeños eventos, luego también a organizarlos… y mira. Hasta hoy :)

3. Si pudieras preguntarle a Steve Ballmer una pregunta, que te gustaría preguntar?

Ufff… lo primero que me viene a la cabeza es preguntarle por la estrategia a largo plazo de algunas de las líneas de negocio. Aunque pensándolo mejor, creo que todo el mundo debe preguntarle ese tipo de cosas, así que mejor le preguntaría por los viejos tiempos: Así que Steve, si me estás oyendo… ¿cuál crees que ha sido el hecho más importante en la historia de Microsoft? Me refiero a algún punto de inflexión que según tu opinión -y no la de un libro o película- sea lo que ha hecho de Microsoft lo que es hoy en día.

4. Cual crees que ha sido el mejor Software de la historia?

Soy un enamorado de C#. Para mí es, sin ningún lugar a dudas el lenguaje más bello, completo y potente jamás creado, así que por extensión VS2012 es el mejor IDE de la historia (o al menos de los que he usado, que son unos cuantos). En cuanto a sistemas operativos, para mí tampoco no hay duda: Windows 8 con mucha diferencia sobre el segundo. Y que conste que no siempre he trabajado con productos de Microsoft, y algunos me gustan mucho, pero a día de hoy ésta es mi opinión.

5. Si tú fueras el responsable de C# qué cambio te gustaría hacer?

Seguir implementando características de la programación funcional. En las últimas versiones, la aparición de genéricos, el manejo de delegados, LINQ y sobretodo las expresiones lambda han convertido C# en el lenguaje que siempre había soñado. ¡Créeme si te digo que hace muy divertido el desarrollar! Sin embargo todavía faltan cosas, y aunque no son imprescindibles (como las Nested functions) sí que harían -como dicen unos buenos amigos míos- que en C# las funciones fueran consideradas ciudadanos de primer nivel, y así dejarían de darme la paliza. Ah! Y permíteme agregar también otra cosa: Aligerar un poco el Framework y el IDE. Porque después de tantas versiones hay ciertos objetos y unas cuantas colecciones que deberían ser eliminadas… y perder unos Kb nunca viene mal.

6. Cuál fue el último libro que has leído?

Suelo ser un lector compulsivo de libros técnicos, fantasía y ciencia ficción, así que no esperes que te diga que un superventas. A ver, mi último libro ha sido ‘Ready Player One’, una novela llena de referencias de los años 80: Hay videojuegos clásicos, música, películas, rol, comics y hasta juguetes. Si bien el estilo es un poco juvenil, no deja de ser una perla para cualquier freak nacido en los 70, porque al fin y al cabo nos hemos criado en esa época. También suelo tener en mi mesilla de noche algún libro técnico del cuál ojeo fragmentos de vez en cuando y cómo no, mi biblia particular: El Silmarillion.

7. Que CD de música recomiendas?

Uno solo? Entonces supongo que me quedaría con el ‘Ok Computer’ de Radiohead. Lo encuentro un disco redondo dentro del género alternativo y como banda me encantan, sobre todo los primeros discos. Con todo, mi criterio musical va desde Bach hasta el buen Metal, pasando por mucho Jazz, así que no se si mi criterio es el más fiable.

8. Que hace que tú seas un excelente MVP?

No soy un excelente MVP, eso lo tengo clarísimo. Verás, déjame matizar lo que para mí significa ser un MVP: Un profesional con un cierto nivel técnico pero que destaca por querer contribuir a la comunidad, ayudando a otros como antes lo ayudaron a él. Y para eso la actitud es básica, y debe ser más importante que las cualidades técnicas. De modo que si alguna vez veis a alguien decir “Ves? Soy mejor que tú porque soy MVP”, pensar que esta persona no merece formar parte del programa. Es más, tenéis mis datos arriba. Poneros en contacto conmigo, ok? No bromeo.

9. Que contiene tu “Computer Bag”?

Cada vez menos software y más hierro. Y con la proliferación de pequeños dispositivos cada vez llevo más adaptadores, cargadores, conectores universales para los viajes y por supuesto una reliquia de mis primeros días de MVP: Una navaja suiza con el logo del programa MVP que contiene un montón de herramientas. No la he usado nunca, pero no pierdo la esperanza.

10. Cuál es la mejor cosa que te ha pasado desde que eres MVP?

La fama, las mujeres, el dinero… es difícil. Podría decirte que ser escogido como C# MVP of the year, ya que es un premio que se otorga una vez ya eres MVP por parte de tus propios compañeros. O mi primer viaje a Seattle, que también fue algo apoteósico. Pero si tengo que escoger una única cosa me quedo con la gente. Ya lo he dicho antes, pero me ha permitido conocer profesionales como la copa de un pino, que además son gente muy maja y dispuesta a ayudar a la comunidad sin reservas. Y lo mejor de todo es que la gran mayoría son unos geeks como yo. No hay nada como un buen geeks & beers…

11. Cuál es tu lema?

Me encantaría poder soltaros una frase molona, pero la verdad es que mi lema es bastante corriente: Intentar ser mejor persona y alguien en quién mis dos hijas se puedan identificar cuando crezcan. No pretendo cambiar la sociedad, con lo anterior ya tengo trabajo de sobras.

12. Quien es tu héroe?

Sheldon Cooper. Yoda. Spock. Frodo. Cálico. Fuckowsky de ‘Memorias de un ingeniero’, y algún otro que se me olvida. Pero supongo que si debo elegir me quedo con Tony Stark: Un tío que es un genio en varios campos, playboy y que viste su propia armadura de combate… ¡Venga ya! ¿A quién no le gustaría? Sin embargo en el mundo real un héroe es algo mucho más difícil: Alguien que hace lo correcto a pesar de complicadas que están las cosas y sin que nadie lo obligue, un niño con una enfermedad grave dando ánimos a sus padres… Eso si que son héroes!

13. Que significa el éxito para ti?

¡Trabajar de algo que te gusta! Fíjate que en realidad son dos cosas: La primera trabajar, que con la que está cayendo por todos lados, es casi un lujo. Y la segunda, encima, hacer algo que realmente te apasiona. Porque al fin y al cabo es algo que vas a tener que hacer durante muuuuchos años, y si no te apasiona estás perdido, ya que la pasión es el motor que va a hacer que evoluciones con los años. Intentad no perderla!

14. Mencione un proyecto que desearía cumplir este año.

Ya he ido a Seattle al MVP Summit, y también he organizado el primer Geek-a-paloozaaa en Andorra, así que a nivel de trabajo me gustaría terminar una larga serie de posts y videos sobre programación paralela que empecé hace unos meses y la he pausado temporalmente por falta de tiempo. He recibido mucho feedback pidiéndome que la continúe, y eso es lo que voy a hacer. Por otro lado a nivel más personal me gustaría no trabajar tanto, pasar más tiempo con mis seres queridos y no ser tan gruñón a veces. Por pedir que no quede!

Nos vemos! :-D