GoogleMaps.Subgurim.NET versión 2.0

Hoy mismo ha salido la versión 2.0 del control de GoogleMaps para ASP.NET. Los cambios con respecto a la versión 1.4 son enormes:

Cambios
  • Resueltos problemas ocultos con el ViewState
  • Ahora se pueden asignar eventos a los markers de dentro de un MarkerManager
Añadidos
  • Soporte DataSource al control para markers e infowindows.
  • GeoCoding avanzado, ahora se puede recoger absolutamente toda la información que provee la API de Google (que no es poca).
  • Eventos de servidor 100% AJAX. Hay varios eventos predefinidos (OnClick, OnZoomEnd, etc.), que se manejan del mismo modo que los eventos de servidor de cualquier otro control.
  • Con el customJavascript se puede añadir javascript propio con total disponibilidad de las variables creadas por el GoogleMaps.Subgurim.NET
  • Añadidos "disableDragging" y "hookMouseWheelToZoom"

Recordad que disponéis de la microweb en googlemaps.subgurim.net.



La capa DAL (Data Access Layer – Capa de Acceso a Datos)

Descargar Ejemplo

1.- Introducción

Seguro que más de una vez habéis oído hablar de la programación en 3 capas aplicada a Web. Ya sabéis, que si la capa de presentación (capa UI), la capa de negocios (capa BLL) o la capa de acceso a datos (capa DAL).

Gracias a su total orientación a objetos con C# (y otros lenguajes) y ASP.NET podemos trabajar con las 3 capas de forma bastante sencilla.

Según mi opinión, nos plantearemos la realización de un proyecto Web de tres maneras:

- Modo A: podemos pasar olímpicamente de las capas DAL y BLL, no queremos saber nada de ellas. ¡¡Simplemente queremos que nuestra Web quede chula!! Estás aquí dentro si usas SqlDataSource y como máximo te has permitido crear tú mismo las sentencias SQL.
- Modo B: queremos hacer las capas DAL y BLL... pero que nos cueste muy poquito o que se encargue de todo ello el Visual Studio. Es decir, que (como mucho) hemos creado los procedimientos almacenados, usamos el DataSet que provee el Visual Studio, o armándonos de valor, recogemos el dataset nosotros mismos y ponemos una clase entre la recogida del dataset y la presentación de datos (algo análogo hacemos con el insert, update y delete).
- Modo C: tenemos un control total y absoluto de todo lo que sucede en todo momento. Sólo en contadas excepciones usamos el DataSet (casi siempre el DataReader), tenemos nuestros procedimientos almacenados, e incluso evitamos al máximo usar el ObjectDataSource.

Desde luego esta clasificación no es ni mucho menos técnica, pero está basada en el conocimiento del trabajo práctico de muchos proyectos propios y ajenos. Y ni mucho menos hay que desmerecer ninguno del los tres modos.

El Modo A puede ser válido para una Web que queramos hacer en apenas un par de días.

El Modo B, puede ser completamente válido para Webs que no vayamos a modificar mucho ni requieran demasiada carga de trabajo. De hecho hay un muy buen tutorial aquí.

Pero el Modo C (sobre el que se va a basar este artículo), es el ideal para aplicaciones de gran carga de trabajo y que puedan ser fácilmente mantenibles, esto es, que en cualquier momento podamos añadir funcionalidades, tablas a la bases de datos, campos a nuestras tablas, procedimientos almacenados, etc.

Y de las varias formas de trabajar con el “Modo C”, yo os voy a proponer la mía. Ya sabéis que cada maestrillo tiene su librillo, y que ningún librillo es perfecto, por lo que en cualquier momento se aceptan sugerencias

Pero vayamos al grano. En el artículo vamos simplemente a ver una capa DAL genérica (en futuros artículos la completaremos con la capa BLL y la de presentación). Y cómo no, qué mejor que un ejemplo sencillito para explicarnos mejor. Es totalmente imprescindible que os bajéis el ejemplo para seguir correctamente las explicaciones.
 

2.- Nuestro ejemplo

Imaginemos una pequeña aplicación en la que queremos agrupar a usuarios dentro de una comunidad de usuarios. Una parte de nuestra aplicación consistirá en crear a usuarios (podemos utilizar los providers membership que vienen por defecto en ASP.NET), otra parte en gestionar las comunidades y otra en asignar usuarios a una comunidad.

Nosotros simplemente vamos a controlar la creación de comunidades de usuarios, así como la modificación, la selección y la eliminación de éstas.
 

2.1.- La base de datos

Por tanto, y simplificando mucho, proponemos una base de datos con la tabla (Comunidad de Usuarios) con dos simples campos (CU = ComunidadUsuarios):
- CU_Id: un autonumérico (int identity) que hará de clave principal.
- CU_Nombre: el nombre de la comunidad. (nvarchar(250)).


2.2.- La clase ComunidadUsuarios

En código, representaremos a la comunidad de usuarios mediante la clase “ComunidadUsuarios” (comunidadusuarios.cs) que -al ser un caso tan simple- sus propiedades coinciden con los campos de la base de datos (id y nombre).
 

2.3 La ConnectionString: constr

Antes de poder realizar acciones sobre la base de datos, hay que poder interactuar con con ella... y para eso tenemos que contectarnos. El ConnectionString es la llave que le dirá a nuestra aplicación todo lo necesario: tipo de base de datos, dónde se ubica ésta, nombre de usuario, contraseña, etc.

En nuestro ejemplo vamos a trabajar con SqlServer Express, ubicada dentro del directorio App_Data. Para conocer las connectionstring de cualquier tipo de base de datos, os recomiendo: http://www.connectionstrings.com/

Tal y como es habitual en ASP.NET, ubicaremos la definición de nuestro connectionstring en el archivo web.config (echadle un vistazo en el ejemplo), y accederemos rápidamente a su valor según lo especificado en la propiedad “constr” de nuestra clase “ComunidadUsuariosDAL”.
 

2.4.- La clase ComunidadUsuariosDAL

Las cuatro operaciones básicas de cualquier base de datos son las de creación (INSERT), selección (SELECT), modificación (UPDATE) y eliminación (DELETE). Sin embargo, en nuestra capa DAL sólo nos interesan 3 operaciones básicas:
- Listado/Selección de datos.
- Ejecutar sin requerir retorno.
- Ejecutar requiriendo un único valor.

Durante lo siguientes puntos vamos a describir cada una de las tres operaciones relacionándolas con nuestro ejemplo.

El código de todo el ejemplo está escrito para que sea compatible con diferentes bases de datos, no sólo con SQLServer. Para ello utilizaremos las clases del namespace “System.Data.Common”, y definiremos el tipo de base de datos desde nuestro web.config. De ese modo, usar este código para SQLServer, oracle, mySql, etc. Sólo supondrá cambiar el proveedor de datos desde nuestro web.config. Desde nuestra clase “ComunidadUsuariosDAL” accederemos a su valor desde la propiedad “myProvider”.
 

2.4.1.- Listado/Selección de datos

Aplicable cuando queremos recoger uno o varios registros de la base de datos.

Lo que nosotros queremos es poder acceder tanto a todas las comunidades de nuestra base de datos como a un única comunidad a partir de su Id.

Lo primero que debemos hacer es crear los procedimientos almacenados. En nuestro ejemplo, crearemos dos:
- CU_Select_All: devuelve todos los registros de la tabla ComunidadUsuarios
- CU_Select_ById: devuelve el registro según la Id que le pasemos.

Posteriormente creamos las funciones que se encargan de ejecutar los procedimientos almacenados y recoger los datos. Son “select_byId” y “select_All”.

Básicamente, lo que se hace en ambos casos sigue la siguiente estructura:
1.- Creamos la conexión, asignándole el ConnectionString. Para ello necesitamos el “DbProviderFactory”, con el que crearemos las conexiones y comandos según el provider que le hayamos especificado en el web.config (en nuestro caso “System.Data.SqlClient”). Lo hemos puesto como propiedad porque va a ser accedido desde varias funciones.
2.- Creamos el comando con el que vamos a trabajar, relacionándolo con la conexión y asignádole el procedimiento almacenado definido previamente. En el caso de “select_byId” se le añade el parámetro entero “id”.
3.- Según corresponda, recorremos los resultados con un datareader y los añadimos a una lista genérica, o recogemos el primer (y único) datareader.

Ahora ya tenemos lo que queríamos, una lista genérica con todas las comunidades de usuarios, o una ComunidadUsuarios según la CU_Id especificada.

NOTA: fijaos que hemos usado la sentencia “using”. Ésta se encarga de cerrar todos los recursos utilizados siempre (aunque surja un error). Es importante, porque dejarnos un simple datareader sin cerrar puede consumir muchos recursos.


2.4.2 Ejecutar sin requerir retorno

La ejecución si requerir retorno envío una orden con unos parámetros y no espera ningún resultado de la base de datos, por lo que es un modo de proceder muy rápido y limpio. Es aplicable cuando borramos un registro, lo modificamos o insertamos uno nuevo.

Conceptualmente podemos creer que “eliminar”, “modificar” o “insertar” son cosas diferentes, y que cada uno debe procederse de manera diferente... sin embargo como las tres responden a la definición de “enviar una orden (procedimiento almacenado) con parámetros sin esperar respuesta”, crearemos una función que les servirá a las tres “ejecutaNonQuery”. No sin antes haber creado los procedimientos almacenados correspondientes a cada una: “CU_Delete”, “CU_Update” y “CU_Insert”.

La función “ejecutaNonQuery” es muy similar a las creadas anteriormente: creamos conexión, creamos comando, añadimos los parámetros, etc. La única diferencia es que ahora el procedimiento almacenado y un listado de parámetros se lo pasamos como parámetros, y cuando antes hacíamos un “ExecuteReader()” para leer el datareader, ahora hacemos un “ExecuteNonQuery()”.

Mientras tanto, en las funciones “insert()”, “update()” y “delete()” se crean los parámetros necesarios, y se mandan junto con el nombre del procedimiento almacenado a ejecutar a “ejecutaNonQuery()”.
 

2.4.3 Ejecutar requiriendo un único valor

Hay ocasiones en las que querremos recoger un único valor de la base de datos. Por ejemplo, podríamos aplicarlo a una inserción de la que queramos saber el identificador creado. Otro ejemplo sería el de hacer un select que nos devolviera un solo dato.

En nuestro ejemplo, cuando hacemos un select de comunidades de usuarios según su id, se nos devuelve un solo dato, por tanto, a parte de lo hecho hasta ahora, podría hacerse de esta otra forma.

El modo de proceder será idéntico al de “ejecutar sin requerir retorno”. Tendremos una función central (“ejecutaScalar”) que recibirá el nombre del procedimiento almacenado y los parámetros necesarios. Lo único que cambiará es que llamará a “ExecuteScalar” en lugar de a “ExecuteNonQuery”, y que se devolverá un valor (tipo object, para hacerlo más genérico).

En nuestra función “select_byId2”, mandamos el parámetro “CU_Id” junto con el anteriormente creado procedimiento almacenado “CU_Select_ById”, y recogeremos el “nombre” de la comunidad. Sólo hay que pasarla a string, y devolver una instancia de “ComunidadUsuarios”.
 

3.- Ampliaciones

Como podréis imaginar, el código del ejemplo es totalmente académico. En muchos lugares y de muchos modos se podría optimizar el código para hacerlo más reutilizable.

Por ejemplo, podríamos crear una clase madre con las funciones básicas y hacer que todos heredaran de ella. Funciones como “EjecutaScalar()” o “EjecutaNonQuery()” serían las primeras candidatas a formar parte de esa clase madre.

Del mismo modo, en lugar de usar un código genérico que vale para diferentes tipos de bases de datos, podríamos usar código específico para SqlServer, Oracle y OleDb en general.

Tampoco se ha hecho en ningún momento comprobaciones de errores ni nada similar, algo que requeriría todo un artículo completo.
 

4.- El ejemplo

Para probar el ejemplo, se provee de “default.aspx”. Es importante reseñar que sólo se trata de probar el correcto funcionamiento de la capa DAL.

Si lo analizamos, vemos que default.aspx representa la capa de presentación, y ComunidadUsuariosDAL representa a la capa DAL: ¡¡¡no hemos creado una capa intermedia (la BLL) propiamente dicha!!! Sólo “default.aspx.cs” hace las veces de capa BLL, pero muy ligeramente.

“Default.aspx” está dividida en cinco partes, desde las que se puede:
1.- Insertar una nueva comunidad.
2.- Seleccionar una comunidad según su id.
3.- Modificar el nombre de una comunidad.
4.- Borrar una comunidad.
5.- Listar todas las comunidades.

Descargar Ejemplo




File Upload AJAX para ASP.NET

¿Os imagináis el clásico FileUpload de ASP.NET, pero con características AJAX?, ¿Subir ficheros sin tener que recargar la página?
Pues si a eso le sumamos un montón de nuevas funcionalidades, obtenemos el FileUpload AJAX.

Su uso es terriblemente sencillo (tan sencillo como cualquier otro control Web), pero su potencia es enorme.

En la MicroWeb podréis descargaros el control, tenéis instrucciones de instalación, podréis ver ejemplos, entrar en foros, etc...

Lo podéis encontrar todo en: http://fileuploadajax.subgurim.net



Cómo hacer un control de usuario (.ascx)


Los controles de usuario (esos ficheritos .ascx) son controles que nos creamos nosotros mismo, perfecto complemento de los controles Web que nos provee ASP.NET.

Son como plantillas que podremos ubicar en tantos sitios de nuestra Web como queramos, de forma que todo queda centralizado, y cualquier cambio en nuestro fichero .ascx se verá reflejado allá donde lo hayamos ubicado.

Realmente es más difícil explicar lo que es que el utilizarlo

Y como siempre un ejemplo vale más de mil palabras, pongamos uno:

Supongamos que en varios sitios de nuestra Web, necesitamos que el usuario nos diga una fecha. Existen mil formas de hacerlo, y en todas ellas acabaremos encontrando los mismo problemas, muchos de los cuales radican en que el usuario debe poner la fecha en el formato correcto para que nuestra aplicación lo entienda.

Como es trabajoso hacerlo una vez, más lo es si lo tenemos que hacer en varios sitios de nuestra Web. Proponemos pues el siguiente control .ascx, que no sólo evita el problema del formato correcto, sino que se le hace muy cómodo al usuario:

Componentes
- Un Calendar
- Un TextBox

Funcionalidades
- El TextBox será ReadOnly, de modo que el usuario no podrá escribir en él.
- Presionando sobre una fecha en el calendar se modificará el TextBox.

Propiedades
- FechaElegida: nos devolverá un DateTime con la fecha que hemos elegido.


Ahora vamos a lo importante: ¿cómo se crea y se usa un control de usuario?

Crearlo es realmente sencillo, añadamos un nuevo item y elijamos un Web User Control:

Upload/control-de-usuario-1.jpg


Una vez creado, lo usamos como si de una página .aspx normal se tratara, con su codebehind, su html, etc. (descargaos el control para ver el código de nuestro ejemplo).

Para añadir el control a cualquier sitio de nuestra Web, no tenemos más que arrastrarlo al lugar que queremos... y ya está!!

En nuestro ejemplo, nos queda este código:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="Varios_ASCX_Default" %>
<%@ Register src="Subgurim.ascx" TagName="Subgurim" TagPrefix="uc1" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Subgurim.NET ejemplo de control de usuario</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>  
        <uc1:Subgurim id="Subgurim1" runat="server"></uc1:Subgurim>      
    </div>
    </form>
</body>
</html>


Destacamos en negrita lo que se ha añadido: un register apuntando a dónde se ubica el control, y un tag que indica donde poner el control dentro de nuestra página .aspx.

Sólo indicar una cosa que no me gusta de lo que hace ASP.NET, y es que si el control está en un directorio diferente al que se encuentra tu página, comenzará a poner tantos "../" como le haga falta. Por eso yo siempre aconsejo cambiar el "Src" a mano y poner el path de forma absoluta. Y eso es más urgente aún si el control lo añadimos a una masterpage y vamos a navegar por más directorios.

Por ejemplo, si todos nuestros controles los pusiéramos en el directorio "UserControls", el Src que aconsejo sería:

<%@ Register src="~/UserControls/Subgurim.ascx" TagName="Subgurim" TagPrefix="uc1" %>

De este modo nos evitamos problemas.
La (fea) Web que nos queda con lo que hemos hecho se parece a:

Upload/control-de-usuario-2.jpg

¡¡Descargaos libremente el control!!




Hash con ASP.NET

Según la Wikipedia, un hash o función resumen se refiere a una función o método para generar claves o llaves que representen de manera casi unívoca a un documento, registro, archivo, etc.

De este modo, por ejemplo, a partir de un texto my largo, podemos conseguir un "resumen" que en la práctica será irrepeteible de apenas unos pocos bytes de longiud (depende del algoritmo). Utilidades podemos encontrar muchas. Por ejemplo, una de las más comunes que le podemos dar es la de demostrar que un mensaje no ha cambiado.

También puede servir como índice de una base de datos. Por ejemplo, imaginaos la situación de una tabla de una base de datos con un campo con un texto muy largo (1000 páginas impresas) y lo único que tenemos que hacer es insertar registros asegurándonos de que no hay dos textos iguales. Una opción sería hacer un SELECT que recorriera todos los enormes campos; la otra sería añadir otro campo con un hash y dicho SELECT hacerlo de éste.. ganaríamos infinitamente en eficiencia.

Nosotros lo que vamos a hacer es conseguir un hash (en formar de string) a partir de un texto plano mediante el algoritmo de hash MD5:

    using System.Security.Cryptography;
    ...
    ...
    public static string GetHash(string textoPlano)
    {
        byte[] data = System.Text.UTF8Encoding.ASCII.GetBytes(textoPlano);
        MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
        byte[] hashbyte = md5.ComputeHash(data, 0, data.Length);
        return BitConverter.ToString(hashbyte);
    }


¿Demasiado fácil para ser verdad?

Pues bien, depués de algo tan sencillo, vemos que hay algo mucho más sencillo aún, y es que todos los tipos de datos descendientes de object tienen el método GetHashCode(), por lo que esto también valdría:

    public static int GetHash(string textoPlano)
    {      
        return textoPlano.GetHashCode();

    }

Esta claro que dejamos de tener cierto control sobre lo que ocurre, pero sigue siendo un hash. Eso sí, ahora el valor que se devuelve es un entero, no un string.



Versión 1.4 de GoogleMaps.Subgurim.NET

Acaba de salir la última versión del ya famoso control de GoogleMaps para ASP.NET.

Las evoluciones con respecto a la versión 1.3 son:

Cambios
  • Resolución de muchos bugs.
  • Reestructuración completa de código.
  • Ahora el control podrá mostrarse dentro de un repeater, datalist o gridview.
  • El TextualCoordinatesControl abre una ventana cuando se le hace click mostrando las coordenadas en que se encuentra.
  • Se puede elegir el evento que abre un InfoWindow, InfoWindowTabs o un ShowMapBlowUp.
  • La key puede configurarse desde el web.config.
  • Cambios generales y mejoras en los ejemplos.
  • Mejoras en el geoCoding: cuandro se presiona enter cuando estamos dentro del cuadro de búsqueda, no se produce un postback.

Añadidos

  • Función Add genérica.
  • Dos nuevos extrabuilt controls: TextualOnClickCoordinatesControl y MarkCenter.
  • GOverViewMap con muchas funcionalidades no existentes en en el control prebuilt existente hasta ahora.
  • Serie de CustomOverlays extras con un primer overlay (Rectangle).
  • Soporte para el MarkerManager, recientemente inaugurado por la API de Google.

Además, sé de buena tinta que la próxima versión será la 2.0, que traerá enormes cambios y que, encima, no tardará mucho en llegar (se dice por ahí que antes de acabar 2006).

Para descargarlo: googlemaps.subgurim.net/descargar.aspx
Para ver la microweb: googlemaps.subgurim.net/



Posición del cursor dentro de un texto

Es extraño, pero algo tan aparentemente fácil como ubicar la posición en que se encuentra el cursor dentro de un cuadro de texto (un input type=text o un textarea), no ha sido fácil de googlear.

Y una vez has sabido como hacerlo y has resuelto todos los pequeños e interminables problemas que aparecen, te queda un código terriblemente sencillo que, tras verlo, se te puede decir "¿y para esto tanto lío?"... pues sí, tanto lío

Bueno, a lo que íbamos. De la función javascript sólo hay que asignar el valor de "tb" en base al id del elemento sobre el que vamos a trabajar y te devuelve la posición del cursor:

function posicionCursor()
{
       var tb = document.getElementById("miCuadrodeTexto")
        var cursor = -1;
       
        // IE
        if (document.selection && (document.selection != 'undefined'))
        {
            var _range = document.selection.createRange();
            var contador = 0;
            while (_range.move('character', -1))
                contador++;
            cursor = contador;
        }
       // FF
        else if (tb.selectionStart >= 0)
            cursor = tb.selectionStart;
   
       return cursor;
}



Rellenar un dataset en código y de forma general

A partir de una pregunta en los foros de www.es-asp.net, he comenzado a contestar... y he visto que bien merecía un artículo.

La duda en cuestión era la de cómo rellenar un DataSet en código... una posible respuesta hubiera sido "con un DataAdapter"... pero eso queda muy soso, por lo que me ha dado por ponerme a hacer una función (seguramente ya la haya hecho alguien por ahí, pero yo no he encontrado nada tras cerca de 30 segundos googleando).

La función se autoexplica bastante con sus comentarios. Sólo deciros que (tras una correcta configuración) sirve para cualquier tipo de Base de Datos, así que aquí la tenéis:

    using System;
    using System.Configuration;
    using System.Data;
    using System.Data.Common;

    ...

        /// <summary>
        /// Como el select se realiza mucha veces, y el modo es siempre muy similar
        /// hacemos un metodo general. Devuelve un dataset.
        /// </summary>
        /// <param name="SP_or_SQL">Nombre del procedimiento almacenado o sentencia SQL</param>
        /// <param name="parameters">Colección de parámetros</param>
        /// <param name="Conn">
        /// Nombre de la conexión (Ubicado en el Web.Config)
        /// </param>
        /// <param name="sProvider">
        /// Nombre del proveedor de datos (Ubicado en el Web.Config)
        /// </param>
        /// <param name="cType">
        /// Tipo de comando. Generalmente será un procedimiento almacenado o una sentencia SQL
        /// </param>
        /// <returns></returns>
        public static DataSet DSSelectGeneral(string SP_or_SQL, DbParameterCollection parameters, string Conn, string sProvider, CommandType cType)
        {
            string DataProviderName = ConfigurationManager.AppSettings[sProvider];
            DbProviderFactory dpf = DbProviderFactories.GetFactory(DataProviderName);

            DbConnection con = dpf.CreateConnection();
            con.ConnectionString = ConfigurationManager.ConnectionStrings[Conn].ConnectionString;

            DbCommand command = dpf.CreateCommand();
            command.Connection = con;
            command.CommandType = cType;
            command.CommandText = SP_or_SQL;

            foreach (DbParameter param in parameters)
                command.Parameters.Add(param);

            DbDataAdapter adapt = dpf.CreateDataAdapter();
            adapt.SelectCommand = command;

            DataSet ds = new DataSet();

            adapt.Fill(ds);

            return ds;
        }



Sólo hay dos puntos a recalcar:

            string DataProviderName = ConfigurationManager.AppSettings[sProvider];
            con.ConnectionString = ConfigurationManager.ConnectionStrings[Conn].ConnectionString;

¿Qué son sProvider y Conn?
Ambos son claves que apuntan a la sección de Configuration de nuestro web.config.

Por ejemplo, si en nuestro web.config tenemos:

<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
  <appSettings>
    <add key="myProvider" value="System.Data.SqlClient" />
  </appSettings>
  <connectionStrings>
    <add name="Conn" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\personal.mdf;Integrated Security=True;User Instance=True" providerName="System.Data.SqlClient"/>
  </connectionStrings>

    ....

<
/configuration>

Haremos que sProvider = "myProvider" y Conn = "Conn".
El primero indicando qué tipo de base de datos vamos a usar (En este caso "System.Data.SqlClient") y el segundo indicando el ConnectionString.

Haciéndolo de este modo, podríamos cambiar de base de datos y nos bastaría con cambiar el value de "myProvider".

Espero que os haya sido útil!!