Cómo reutilizar el código de una biblioteca de clases .NET desde una aplicación Silverlight

Existe cierta confusión con la compatibilidad entre binarios de Silverlight y binarios de .NET, y a más de uno le pilla por sorpresa que en un proyecto de Visual Studio de una aplicación Silverlight, no se pueden referenciar bibliotecas de clases (class library) desarrolladas en .NET de escritorio (para diferenciarlo del .NET de Silverlight). El motivo es que el runtime y la biblioteca de clases de .NET para Silverlight es un subconjunto del runtime y la biblioteca de clases de .NET de escritorio, y por lo tanto no son directamente compatibles.

Se puede migrar el código de las clases .NET a clases .NET para Silverlight, y en ocasiones puede ser tan sencillo como recompilar el mismo código fuente (por ejemplo, como una Silverlight Class Library). En otras ocasiones esta migración no es directa, dado que la biblioteca de clases de Silverlight no contiene todas las clases que la biblioteca de clases del Framework .NET.

.NET Framework Class Library

https://msdn.microsoft.com/en-us/library/ms229335.aspx

.NET Framework Class Library for Silverlight

https://msdn.microsoft.com/en-us/library/cc838194.aspx

Supongamos que queremos reutilizar unas bibliotecas de clases que manejan objetos DataSet, y dado que .NET para Silverlight (hasta la versión Silverlight 4 RC) no contiene el namespace System.Data, no podemos simplemente recompilar el código fuente (fallaría la compilación dado que no estarían implementada ninguna clase DataSet).

Una posible implementación que nos permite reutilizar al máximo nuestras bibliotecas de clases .NET desde aplicaciones Silverlight sería implementar una interfaz, expuesta como un web service, y consumir dicho web service desde la aplicación Silverlight.

De esta manera, todo el código de .NET de escritorio se ejecutará en el servidor web, y la aplicación Silverlight que se ejecutará en el navegador del cliente únicamente representará la capa de presentación.

Os muestro un pequeño ejemplo en C# como prueba de concepto. En este ejemplo, el web service está implementado como un web service de ASP.NET 2.0 (*.asmx), pero considerad la posibilidad de implementarlo como un web service WCF (Windows Communication Foundation), que es la siguiente generación de web services de .NET.

Supongamos que tenemos la siguiente clase en nuestra biblioteca de lógica de negocio. Esta es la clase cuyo código nos gustaría reutilizar en Silverlight, pero dado que hace uso de System.Data no podemos recompilar el código sin más.

using System;

using System.Data;

namespace SilverlightTest.Web

{

    public class BusinessLogic

    {

        public DataSet getCustomerData(int customerID)

        {

            DataSet dataSet = new DataSet();

            DataTable dataTable = new DataTable();

            dataTable.Columns.Add("Columna 1");

            dataTable.Columns.Add("Columna 2");

            dataTable.Rows.Add(new object[] { "celda 0-0", "celda 0-1" });

            dataTable.Rows.Add(new object[] { "celda 1-0", "celda 1-1" });

            dataTable.Rows.Add(new object[] { "celda 2-0", "celda 2-1" });

            dataTable.Rows.Add(new object[] { "celda 3-0", "celda 3-1" });

            dataSet.Tables.Add(dataTable);

            return dataSet;

        }

    }

}

Para solventar este problema, podemos crear un web service que exponga el método getCustomerData. En este caso, para hacerlo compatible con Silverlight, convertiremos el objeto DataSet en uno o varios arrays bidimensionales (uno por cada DataTable del DataSet) antes de enviar la respuesta al cliente Silverlight:

using System;

using System.Data;

using System.Web;

using System.Web.Services;

using SilverlightTest.Web;

[WebService]

public class SilverlightInterface : System.Web.Services.WebService

{

    [WebMethod]

    public object[][] getCustomerData(int customerID)

    {

        //Utilizamos la lógica de la biblioteca de clases de .NET de escritorio

        BusinessLogic businessLogic = new BusinessLogic();

        DataSet dataSet = businessLogic.getCustomerData(customerID);

  int x = dataSet.Tables[0].Rows.Count;

        int y = dataSet.Tables[0].Columns.Count;

        //Declaramos y demensionamos el Array

        object[][] dataArray = new object[x][];

        for (int i = 0; i < x; i++)

        {

            dataArray[i] = new object[y];

        }

        //Rellenamos el Array con los datos del DataSet

        for (int i = 0; i < x; i++)

        {

            for (int j = 0; j < y; j++)

            {

                dataArray[i][j] = dataSet.Tables[0].Rows[i][j];

            }

        }

        return dataArray;

    }

}

Por último, desde el cliente Silverlight consumimos el web service de la siguiente manera tras haber incluido la referencia web (que hemos llamado BusinessLogicWS):

public partial class SilverlightPage : UserControl

{

    public SilverlightPage()

    {

        InitializeComponent();

        //Creamos el objeto proxy para interactuar con el web service

        BusinessLogicWS.SilverlightInterfaceSoapClient wsProxy =

            new BusinessLogicWS.SilverlightInterfaceSoapClient();

        wsProxy.getCustomerDataCompleted

            += new EventHandler

                <BusinessLogicWS.getCustomerDataCompletedEventArgs>

                (getCustomerDataCompletedHandler);

        int CustomerID = 1234;

        //Realizamos la llamada asíncrona al web service

        wsProxy.getCustomerDataAsync(CustomerID);

    }

    void getCustomerDataCompletedHandler(object sender,

        BusinessLogicWS.getCustomerDataCompletedEventArgs e)

    {

        //Obtenemos los datos de la respuesta del web service

        ObservableCollection<SilverlightTest.BusinessLogicWS.ArrayOfAnyType>

            data = e.Result;

        //Hacemos algo con esos datos

        for (int i=0; i<data.Count; i++)

        {

            for (int j = 0; j < data[i].Count; j++ )

            {

                Debug.WriteLine(data[i][j].ToString());

            }

        }

    }

}

De esta forma, hemos construido una interfaz entre la aplicación Silverlight y nuestra biblioteca de clases .NET, pudiendo reutilizar el código de las bibliotecas de clases .NET y solventando los problemas de incompatibilidades entre tipos de datos.

Hasta el próximo post,

- Daniel Mossberg