…y tú, ¿Qué máquina virtual necesitas?…

Navegando el otro día por la red encontré un interesante recurso muy útil para todos los que nos necesitamos probar cosas, y muchas veces no podemos probarlas porque no tenemos tiempo para crear la infraestructura necesaria. Este recurso no es otra cosa que una lista de discos duros virtuales en las que tenemos instalados y listos para probar distintas plataformas,tecnologías y herramientas de Microsoft. Podéis acceder a la página con los discos duros en este enlace. De todos modos, os dejo aquí el listado de discos duros virtuales disponibles por producto.

A disfrutar…

ADO.NET EF: …enredando y más ejemplos (I)

En el último post sobre ADO.NET EF & LINQ To SQL os hablaba de que ya había algún ejemplo concreto para la Beta1 del SP1 de Visual Studio 2008. En concreto os hablaba del ejemplo ADO.NET Entity Framework Query Samples que está disponible en MSDN Code Gallery. El caso es que el equipo de ADO.NET nos comenta en una de sus últimas entradas que se han actualizado todos los ejemplos que había para ADO.NET EF a la Beta1 del SP1 de Visual Studio 2008. Entre los ejemplos actualizados, y que tendré que probar, está la herramienta eSqlBlast de la que os hablé hace tiempo y que me pareció espectacular…pero analizando un poquillo ADO.NET EF Query Samples, he podido comprobar que tal y como se comenta en la página del proyecto es una interesante herramienta de aprendizaje y de pruebas de los distintos tipos de consultas que podemos realizar contra un modelo de ADO.NET EF.

ADO.NET Entity Framework Query Samples

Una vez descargado el proyecto, y tras retocar la cadena de conexión a la BD NorthwindEF del App.Config, no basta con compilar y ejecutar la aplicación para empezar a probarla. La aplicación contempla tres grandes secciones:

image

  • Entity SQL over ObjectQuery<T>, que nos permite definir consultas utilizando servicios del objeto. Esta alternativa permite devolver estructuras de información del EDM a través del método CreateQuery asociado al objeto ObjectContext (y que implementa una interfaz de tipo ObjectQuery<T>), que acepta una instrucción eSQL o LINQ To Entities con parámetros que define una consulta que recuperará una cierta lista de entidades.
  • LINQ To Entities, que nos permite definir consultas LINQ contra el Entity Data Model definido. LINQ To Entities que nos dota de toda la potencia y capacidades de LINQ sobre ADO.NET Entity Framework, así como funcionalidad de ORM (Object Relational Model), de manera que los desarrolladores pueden materializar sus entidades en tipos del CLR y programar de forma más sencilla contra el EDM y la base de datos.
  • Query Builder Method, que se asienta en la capacidad que da la clase ObjectQuery para definir de forma más flexible consutas de LINQ To Entities o Entity SQL contra el EDM. Otro punto muy importante de ObjectQuery es que implementa una serie de query builder methods que facilitan la construcción de consultas de forma equivalente a lo que podemos hacer con eSQL.
image image  

image

Para cada tipo de consulta no sólo veremos su implementación en C# y el resultado, sino también la traducción interna que hace de la sentencia LINQ y el comando T-SQL (por si había dudas, la consulta que he probado en el ejemplo ya tiene su complejidad) que se envía a la BD.

image image

Cómo veis, ADO.NET EF Query Samples es una aplicación realmente pensada para agilizar el aprendizaje de distintas formas de realizar consultas a EDM’s.

Enredando un poquillo con ADO.NET EF

Para finalizar el post, os voy a contar algunas cosas que he podido probar de ADO.NET EF en la versión Beta1 del SP1 de Visual Studio 2008. Algunas de estas cosas ya nos las anticipo Unai en este post:

  • Nuevo diseñador, más ligero y funcional:
ADO_NET_EF_SP1_2 ADO_NET_EF_SP1_3  

ADO_NET_EF_SP1_4

  • Los ficheros de metadatos ya no aparecen físicamente en el directorio bin, sino que forman parte del ensamblado:

ADO_NET_EF_SP1_5

Además de esto, otra aspecto interesante que he intentado probar pero que no hay manera (no se la razón) es como llamar a un procedimiento almacenado…como esto espero contarlo en el próximo post, sólo os diré que una de las formas es mapeando el SP definido en la capa storage a una función importada en la capa conceptual…y hasta aquí puedo leer 😉

ADO_NET_EF_SP1_6 ADO_NET_EF_SP1_7  

ADO_NET_EF_SP1_8Y hasta aquí llega lo que os quería contar hoy sobre ADO.NET EF. Espero que el post os haya resultado interesante.

Windows 7: …ya tenemos los primeros detalles…

Aunque ya había leído algo acerca de que Microsoft está trabajando en la nueva versión de su S.O y que vendrá a sustituir a nuestro querido Windows Vista, no ha sido hasta hoy cuando he visto una primera noticia oficial en la que ya se empieza a hablar de detalles de la nueva versión, denominada de momento Windows 7. En concreto, la noticia aparecida en la sección de tecnología del diario El Mundo se hace eco del hecho de que Windows 7 estará optimizado para funcionar con pantallas táctiles gracias a la tecnología Multi-Touch y que posibilitará visualizar fotografías o navegar en Internet a golpe de dedo…como siempre, ya tenemos los primeros vídeos con características que podrían aparecer en la nueva versión de Windows (no he encontrado ninguno en el que se demuestre la funcionalidad táctil que se describe en el artículo).

Por cierto, como podréis leer en la noticia, la previsión de lanzamiento de Windows 7 es finales del año que viene. De esta manera, parece que Microsoft es fiel a su palabra de reducir el tiempo de desarrollo de las nuevas versiones de su S.O.

WSS 3.0 & MOSS: Extendiendo las capacidades de búsqueda (III)!

Como toda saga debería tener su fin, con este post vamos a completar la serie de artículos sobre como es posible ir más allá de las capacidades de búsqueda out-of-the-box en plataforma SharePoint que iniciamos con las partes I y II. En este post haremos un recorrido por la tercera opción que nos ofrece SharePoint para extender las capacidades de búsqueda: los servicios web de búsqueda. Empecemos.

Query Web Service en SharePoint

Además de poder realizar consultas a través del modelo de objetos, la plataforma SharePoint permite realizar búsquedas a través del Query Web Service. Esta opción de búsqueda soporta las dos posibilidades vistas para realizar búsquedas mediante el modelo de objetos: SQL Syntax y Keyword Syntax. Entonces, ¿cuándo usamos el Query Object Model y cuando el Query Web Service? La respuesta es bastante sencilla. Usaremos la primera opción cuando la aplicación de búsquedas resida en el mismo servidor que está ejecutando WSS 3.0 o MOSS. Usaremos la segunda opción cuando se trate de aplicaciones remotas.

Los servicios web de búsqueda de WSS 3.0 & MOSS nos permite acceder a las funcionalidades de búsqueda desde aplicaciones cliente que estén fuera del contexto de sitios de SharePoint:

Los métodos web para WSS 3.0 y MOSS son los mismos, si bien, y como ya sabemos, las capacidades de búsqueda son mucho más potentes en MOSS que en WSS 3.0.

Uso del Query Web Service

Para probar esta funcionalidad, partiremos de la misma aplicación web que vimos en el post anterior, al que vamos a añadir al proyecto un nuevo ítem de tipo Web Form. Abrimos la página que acabamos de crear en modo de diseño y añadimos los siguientes elementos (ver Figura):

  • Un control de tipo Table de la sección HTML de la Toolbox.
  • Un control de tipo TextBox en el que configuramos la propiedad TextMode con la opción MultiLine.
  • Un control de tipo DrowDownList.
  • Un control de tipo Label en el que configuramos la propiedad Text con el texto Tipo de Consulta.
  • Un control de tipo TextBox en el que configuramos la propiedad TextMode con la opción MultiLine.
  • Un control de tipo Button en el que configuramos la propiedad Text con el texto Buscar.
  • Un control de tipo Label.
  • Un control de tipo GridView.

Como veis, el aspecto de la web es casi igual al de la realizada en el post previo…de hecho el copy & paste ha sido radical 😛 (cuestión de aprovechar tiempos).

image image

Una vez que hemos diseñado la página de búsquedas en la que habilitaremos SQL Syntax, nos vamos a la vista de código de la misma presionando F7. En esta vista de código vamos a añadir toda la lógica necesaria para poder probar la funcionalidad Query Web Service:

  • Lo primero que vamos a hacer es añadir la referencia al servicio web de búsqueda. Para ello, utilizamos la opción Add Web Reference… disponible cuando clic con el botón derecho del ratón sobre el nombre de la solución.
  • Hacemos clic sobre la opción Web services on the local machine.
  • Buscamos el servicio web de búsquedas de MOSS. Como hemos comentado, la url característica de este servicio web es: http://Nombre_Servidor/[Sites/][Site_Name/]_vti_bin/search.asmx.
  • Hacemos clic sobre el servicio web search y en la siguiente pantalla le damos a la referencia web el nombre de QueryWebServiceProxy.
  • Pulsamos Add Reference y ya tentemos añadido el servicio web de búsqueda de MOSS a nuestro proyecto.
image image  

image

Una vez que hemos añadido la referencia web al servicio de búsqueda de MOSS, ya podemos comenzar a añadirle la lógica necesaria a la página ASP.NET:

  • Añadimos las siguientes constantes a la clase parcial asociada a la página:

    #region Variables & Constates

    //Constantes valores combo

    const string ID_QUERY = «ID_Query»;

    const string NOMBRE_QUERY = «Query»;

    public const string TIPO_QUERY0_ID = «Q0»;

    public const string TIPO_QUERY0_TEXT = «Seleccione un tipo de query…»;

    public const string TIPO_QUERY1_ID = «Q1»;

    public const string TIPO_QUERY1_TEXT = «Keyword Query»;

    public const string TIPO_QUERY2_ID = «Q2»;

    public const string TIPO_QUERY2_TEXT = «Búsqueda por Scope»;

    public const string TIPO_QUERY3_ID = «Q3»;

    public const string TIPO_QUERY3_TEXT = «Búsqueda por Scope y Campo»;

    public const string TIPO_QUERY4_ID = «Q4»;

    public const string TIPO_QUERY4_TEXT = «Búsqueda con LIKE»;

    public const string TIPO_QUERY5_ID = «Q5»;

    public const string TIPO_QUERY5_TEXT = «Búsqueda con CONTAINS y *»;

    public const string TIPO_QUERY6_ID = «Q6»;

    public const string TIPO_QUERY6_TEXT = «Búsqueda con FreeText»;

    public const string TIPO_QUERY7_ID = «Q7»;

    public const string TIPO_QUERY7_TEXT = «Búsqueda con FreeText y Op. Exclusión»;

    //Constantes para las queries

    public const string QUERY1 = «Introduzca la palabra clave a buscar…»;

    public const string XML_TAG_START1 = «<QueryPacket xmlns=’urn:Microsoft.Search.Query’>»

            + «<Query><SupportedFormats><Format revision=’1′>»

            + «urn:Microsoft.Search.Response.Document:Document</Format>»

            + «</SupportedFormats><Context><QueryText language=’en-US’ type=’STRING’>»;

    public const string XML_TAG_START2 = «<QueryPacket xmlns=’urn:Microsoft.Search.Query’>»

                + «<Query><SupportedFormats><Format revision=’1′>»

                + «urn:Microsoft.Search.Response.Document:Document</Format>»

                + «</SupportedFormats><Context><QueryText language=’en-US’ type=’MSSQLFT’>»;

    public const string XML_TAG_END = «</QueryText></Context></Query></QueryPacket>»;

    public const string QUERY2 = «SELECT Rank, Title, Author, Size, Path, Description» +

                            » FROM SCOPE() WHERE \»scope\»=’All Sites'»;

    public const string QUERY3 = «SELECT Rank, Title, Author, Size, Path, Description» +

                            » FROM SCOPE() WHERE \»scope\»=’All Sites'» +

                            » AND  Author=’LitwareInc Administrator’ ORDER BY Rank DESC»;

    public const string QUERY4 = «SELECT Rank, Title, Author, Size, Path, Description» +

                            » FROM SCOPE() WHERE \»scope\»=’All Sites'» +

                            » AND TITLE LIKE ‘Bla%’ ORDER BY Rank DESC»;

    public const string QUERY5 = «SELECT Rank, Title, Author, Size, Path, Description» +

                            » FROM SCOPE() WHERE CONTAINS (‘\»Bla*\»‘)»;

    public const string QUERY6 = «SELECT Rank, Title, Author, Size, Path, Description» +

                            » FROM SCOPE() WHERE \»scope\»=’All Sites'» +

                            «AND FreeText (‘Bla%’) ORDER BY Rank DESC»;

    public const string QUERY7 = «SELECT Rank, Title, Author, Size, Path, Description» +

                            » FROM SCOPE() WHERE \»scope\»=’All Sites'» +

                            «AND FreeText (‘Bla% -CRM’) ORDER BY Rank DESC»;

    #endregion

Como vemos, estas constantes nos van a permitir realizar varios tipos de consultas mediante utilizando el servicio web de búsqueda. Las consultas son las mismas vistas con SQL Syntax y Keyword Syntax, pero con la particularidad de que las envolveremos con unas etiquetas XML particulares:

  • Consultas utilizando una palabra clave.
  • Consultas a un determinado scope (en este caso All Sites).
  • Consultas a un scope y filtrando por un cierto campo (Author).
  • Consultas utilizando la cláusula LIKE que permite extender las búsquedas en SharePoint al habilitar el uso de operadores comodín (%).
  • Consultas utilizando el predicado CONTAINS(), que permite buscar el término de búsqueda utilizando caracteres comodín (*) y utilizando proximidad. Con este operador se utiliza habitualmente el comodín *.
  • Consultas utilizando el predicado FreeText(), que permite buscar el término de búsqueda dentro de los documentos.

Además, en el código anterior también podemos observar lo siguiente:

  • Hay dos tipos de etiquetas XML que envuelven estos tipos de consultas. Por un lado tenemos:

    public const string XML_TAG_START1 = «<QueryPacket xmlns=’urn:Microsoft.Search.Query’>»

            + «<Query><SupportedFormats><Format revision=’1′>»

            + «urn:Microsoft.Search.Response.Document:Document</Format>»

            + «</SupportedFormats><Context><QueryText language=’en-US’ type=’STRING’>»;

Esta etiqueta lo que indica es que la consulta a ejecutar es de tipo STRING y es el el propio componente de búsqueda de MOSS el que se encarga de construir la query a partir de la palabra clave especificada.

  • Por otro lado, nos encontramos con:

    public const string XML_TAG_START2 = «<QueryPacket xmlns=’urn:Microsoft.Search.Query’>»

                + «<Query><SupportedFormats><Format revision=’1′>»

                + «urn:Microsoft.Search.Response.Document:Document</Format>»

                + «</SupportedFormats><Context><QueryText language=’en-US’ type=’MSSQLFT’>»;

Esta etiqueta indica que la consulta a ejecutar es de tipo MSSQLFT, es decir, es la lógica de nuestra aplicación la que se encarga de construir la consulta parametrizada y pasársela al componente de búsqueda de SharePoint.

  • Los resultados se ordenan utilizando la cláusula ODER BY Rank DESC, es decir, los resultados se devuelven ordenador de forma descendente por la propiedad Rank que indica el nivel de raking de un cierto resultado devuelto por el motor de búsquedas de SharePoint (MOSS en este caso).
  • En alguna de las consultas se utiliza el operador de exclusión -, que nos permite especificar que se excluyan aquellos resultados que contengan el término que aparece a continuación del operador.

En el evento Page_Load() de la página ASP.NET añadimos el siguiente código:

   protected void Page_Load(object sender, EventArgs e)

    {

        if (!IsPostBack)

        { 

            this.Label1.Text = «»;

            this.Button.Enabled = false;

            //Filling the combo

            DataTable dtlQueries = new DataTable();

            dtlQueries.Columns.Add(ID_QUERY);

            dtlQueries.Columns.Add(NOMBRE_QUERY);

            Random rdIDProducto = new Random();

            DataRow dtrFila;

            //Default Data

            dtrFila = dtlQueries.NewRow();

            dtrFila[ID_QUERY] = TIPO_QUERY0_ID;

            dtrFila[NOMBRE_QUERY] = TIPO_QUERY0_TEXT;

            dtlQueries.Rows.Add(dtrFila);

            //First Query

            dtrFila = dtlQueries.NewRow();

            dtrFila[ID_QUERY] = TIPO_QUERY1_ID;

            dtrFila[NOMBRE_QUERY] = TIPO_QUERY1_TEXT;

            dtlQueries.Rows.Add(dtrFila);

            //Second Query

            dtrFila = dtlQueries.NewRow();

            dtrFila[ID_QUERY] = TIPO_QUERY2_ID;

            dtrFila[NOMBRE_QUERY] = TIPO_QUERY2_TEXT;

            dtlQueries.Rows.Add(dtrFila);

            //Third Query

            dtrFila = dtlQueries.NewRow();

            dtrFila[ID_QUERY] = TIPO_QUERY3_ID;

            dtrFila[NOMBRE_QUERY] = TIPO_QUERY3_TEXT;

            dtlQueries.Rows.Add(dtrFila);

            //Fourth Query

            dtrFila = dtlQueries.NewRow();

            dtrFila[ID_QUERY] = TIPO_QUERY4_ID;

            dtrFila[NOMBRE_QUERY] = TIPO_QUERY4_TEXT;

            dtlQueries.Rows.Add(dtrFila);

            //Fith Query

            dtrFila = dtlQueries.NewRow();

            dtrFila[ID_QUERY] = TIPO_QUERY5_ID;

            dtrFila[NOMBRE_QUERY] = TIPO_QUERY5_TEXT;

            dtlQueries.Rows.Add(dtrFila);

            //Sixth Query

            dtrFila = dtlQueries.NewRow();

            dtrFila[ID_QUERY] = TIPO_QUERY6_ID;

            dtrFila[NOMBRE_QUERY] = TIPO_QUERY6_TEXT;

            dtlQueries.Rows.Add(dtrFila);

            //Seventh Query

            dtrFila = dtlQueries.NewRow();

            dtrFila[ID_QUERY] = TIPO_QUERY7_ID;

            dtrFila[NOMBRE_QUERY] = TIPO_QUERY7_TEXT;

            dtlQueries.Rows.Add(dtrFila);

            //Adding the data to the dropdownlist

            this.DropDownList1.DataSource = dtlQueries;

            this.DropDownList1.DataTextField = NOMBRE_QUERY;

            this.DropDownList1.DataValueField = ID_QUERY;

            this.DropDownList1.DataBind();

            this.DropDownList1.AutoPostBack = true;

            this.DropDownList1.SelectedItem.Value = TIPO_QUERY0_ID;

        }

    }

Como vemos, el código anterior nos permite configurar en la primera carga de la página los parámetros iniciales de los controles de la misma. En particular, estamos realizando las siguientes tareas:

  • Rellenar el control DropDownList con los distintos tipos de consultas que vamos a realizar.
  • Configuramos la propiedad AutoPostBack a true para que la página se enteré de que el usuario ha seleccionado una cierta opción y se llame al correspondiente manejador.
  • Especificar el valor del DropDownList que se muestra por defecto.

Codificamos el manejador del evento SelectedIndexChanged del control DropDownList añadiéndolo directamente en la vista de código de la página o bien haciendo doble clic sobre dicho control desde la vista de diseño de la página:

  protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e)

    {

        if (this.DropDownList1.SelectedItem.Value != «0»)

        {

            this.Label1.Text = «»;

            string sOpcionQuery = this.DropDownList1.SelectedItem.Value;

            switch (sOpcionQuery)

            {

                case TIPO_QUERY1_ID:                                                             

                    this.Button.Enabled = true;

                    this.TextBox1.Text = QUERY1;

                    break;

                case TIPO_QUERY2_ID:

                    this.Button.Enabled = true;

                    this.TextBox1.Text = QUERY2;

                    break;

                case TIPO_QUERY3_ID:

                    this.Button.Enabled = true;

                    this.TextBox1.Text = QUERY3;

                    break;

                case TIPO_QUERY4_ID:

                    this.Button.Enabled = true;

                    this.TextBox1.Text = QUERY4;

                    break;

                case TIPO_QUERY5_ID:

                    this.Button.Enabled = true;

                    this.TextBox1.Text = QUERY5;

                    break;

                case TIPO_QUERY6_ID:

                    this.Button.Enabled = true;

                    this.TextBox1.Text = QUERY6;

                    break;

                case TIPO_QUERY7_ID:

                    this.Button.Enabled = true;

                    this.TextBox1.Text = QUERY7;

                    break;

                default:

                    this.Label1.Text = «»;

                    this.Button.Enabled = false;

                    break;

            }

        }

        else

        {

            this.Label1.Text = «»;

            this.Button.Enabled = false;

        } 

    }

Con el código anterior, estamos pintando en el control TextBox de la página la consulta, seleccionada por el usuario.

Finalmente, codificamos el manejador del evento clic del control Button utilizando una de las dos opciones comentadas para el manejador SelectedIndexChanged del control DropDownList:

   protected void Button1_Click(object sender, EventArgs e)

    {

        try

        {

            int totalNumRecords=0;

            string keywordString = this.TextBox1.Text;

            string qXMLString;

            DataSet queryResults=new DataSet();

            QueryWebServiceProxy.QueryService queryService =

               new QueryWebServiceProxy.QueryService();

            queryService.Credentials = System.Net.CredentialCache.DefaultCredentials;

            if (this.DropDownList1.SelectedItem.Value==TIPO_QUERY1_ID)

            {

               qXMLString = XML_TAG_START1 + keywordString + XML_TAG_END;                

            }

            else

            {

                qXMLString = XML_TAG_START2 + keywordString + XML_TAG_END;         

            } 

            queryResults = queryService.QueryEx(qXMLString);

            totalNumRecords = queryResults.Tables[0].Rows.Count;         

            if (totalNumRecords > 0)

            {

                this.Label1.Text = «Número de coincidencias » + totalNumRecords.ToString();

                this.GridView1.DataSource = queryResults.Tables[0];

                this.GridView1.DataBind();

            }

            else

            {

                this.Label1.Text = «No hay resultados para esta búsqueda»;

                this.GridView1.DataSource = null;

                this.GridView1.DataBind();

            }          

        }

        catch (Exception ex)

        {

            this.Label1.Text = ex.Message;

        }

    }

Como vemos en el listado del código (en negrita las partes clave), los pasos necesarios para realizar consultas utilizando el servicio web de búsqueda son los siguientes:

  • Definimos un objeto de tipo QueryService() a partir de la referencia creada al servicio web y que nos permitirá realizar los dos tipos de búsquedas comentados.
  • Especificamos las credenciales de acceso al servicio web de búsqueda (ponemos las credenciales por defecto).
  • En función de la opción seleccionada por el usuario, búsqueda por palabra clave o mediante una consulta SQL Syntax, parametrizamos de forma adecuada la consulta que tiene que ejecutar el servicio web de búsqueda de MOSS:
    • En el caso de Keyword Syntax, es el propio componente de búsqueda de MOSS el que se encarga de construir la query a partir de la palabra clave especificada.
    • En el caso de SQL Syntax, es la lógica de nuestra aplicación la que se encarga de construir la consulta parametrizada y pasársela al componente de búsqueda de SharePoint.

En cualquiera de las dos situaciones, la consulta se ejecuta mediante el método QueryEx() y el resultado devuelto (una colección de registros que cumplen las condiciones de búsqueda definidas) lo almacenamos en un objeto de tipo DatSet.

  • Sin más, comprobamos que hay resultados en el tabla con índice 0 del Dataset y los renderizamos en el control GridView e la página.

Probando la solución

Una vez diseñada la aplicación de búsquedas preparada para definir búsquedas el servicio web de búsquedas de MOSS, así como implementada la lógica de búsquedas, vamos a probar dos de las situaciones modelada:

  • Búsqueda mediante palabra clave:
    • Escogemos la opción Keyword query.
  • Búsqueda mediante SQL Syntax:

    o Seleccionamos la opción Búsqueda con FreeText y Op. de Exclusión.

image image

Y aquí concluye esta serie de post sobre extensibilidad de las capacidades de búsqueda en plataforma SharePoint. Espero que el tema os haya resultado interesante.

Microsoft Office soportará de forma nativa ODF en 2009!

Habitualmente suelo leer la sección de tecnología de el diario El Mundo (elmundo.es navegante), y que opino está bastante bien y actualizada en cuanto a temas y noticias tratadas. El caso es que acabo de leer lo que se puede calificar como una noticia excelente para la interoperabilidad (aunque no lo será tanto para los que han presionado lo indecible para que el formato Microsoft Office XML, OOXML,  se convirtiera en un estándar ISO) entre sistemas: Microsoft Office 2007 soportará con el service pack 2 Open Document Format (ODF) v1.1. Además de este, soportará XML Paper Specification y Portable Document Format (PDF), es decir, Microsoft sigue apostando por la interoperabilidad basada en estándares ampliando con estos nuevos soportes los 20 formatos de ficheros diferentes que soporta en la actualidad.

Como podéis leer en la noticia de El mundo, el SP2 de Microsoft Office 2007 está previsto para el primer semestre de 2009 y permitirá trabajar con archivos que usen estos formatos sin necesidad de tener que instalar ningún tipo de Add-In. Y no sólo eso, Microsoft prevé colaborar con la comunidad open source para que las versiones anteriores de Office (XP y 2003) puedan utilizar también ODF. En partícular, Microsoft se ha comprometido a mejorar el traductor OOXML-ODF SourceForge.net.

ADO.NET Entity Framework y LINQ To SQL: Ejemplos de las novedades del SP1 Beta 1!

Poco a poco empiezan a aparecer ejemplos de algunos de los cambios que vienen con el SP1 Beta 1 de .NET Framework 3.5. En esta ocasión, os dejo algunos recursos interesantes sobre el tema:

XNA Game Studio 3.0 CTP: Ahora también podemos crear juegos para el Zune!

Aunque no he probado a hacer cosas con XNA, navegando por la blogosfera me he enterado que Microsoft liberó el pasado 7 de mayo la primera CTP de XNA 3.0 Game Studio, específicamente preparada para crear juegos para su reproductor de música y video Zune:

Para probar esta primera CTP sólo se necesita Visual Studio C# 2008 Express o bien Visual Studio 2008 Standar Edition o superior. Como os he comentado, esta CRP sólo está operativa para Zune y Windows, y no para la XBox 360. Sin más, os dejo unos enlaces para los que seáis realmente intrépidos y queráis probar esta CTP:

WSS 3.0 & MOSS: Extendiendo las capacidades de búsqueda (II)!

Siguiendo con la serie de post, cuya primera parte podéis encontrar aquí,  acerca de las posibilidades que brinda la plataforma SharePoint para extender la funcionalidad de búsqueda que proporciona a la caja, en este segundo post vamos a ver como podemos definir aplicaciones de búsqueda más granulares que permitan el uso de caracteres comodín como * o %. Empecemos.

Query Syntax y Keyword Syntax en SharePoint

Las búsquedas en plataforma SharePoint proporcionan un nuevo modelo de objetos que se puede utilizar en web parts o aplicaciones de búsqueda personalizada para ejecutar consultas contra el servicio de búsqueda. Todo el modelo de objetos de búsqueda de la plataforma SharePoint se encuentra implementado en dos espacios de nombres clave:

  • Microsoft.SharePoint.Search.Query para Windows SharePoint Services 3.0 (WSS 3.0).
  • Microsoft.Office.Server.Search para Microsoft Office SharePoint Server (MOSS).

Este modelo de objetos habilita dos posibilidades de búsqueda en plataforma SharePoint:

  • SQL Syntax, que proporciona el soporte necesario para construir consultas complejas similares a las consultas T-SQL que enviamos a SQL Server en aplicaciones con acceso a datos. De hecho, esta modalidad es la que nos permite definir consultas en las que utilicemos caracteres comodín como * o %, operadores como <,>, AND, OR, cláusulas como SELECT, WHERE, ORDER BY, LIKE o predicados como CONTAINS() y FREETEXT (). Lógicamente, SQL Syntax es la opción que habilita poder definir escenarios de búsquedas más flexibles.
  • Keyword Syntax, que permite realizar búsquedas por palabra clave. Esta opción devuelve aquellos resultados que contienen de manera exacta el término, frase o prefijo a buscar.

Implementando Query Syntax y SQL Syntax

Para probar esta funcionalidad, vamos a crear una aplicación ASP.NET que iremos extendiendo para probar las distintas alternativas de búsqueda comentadas. Abrimos Visual Studio 2005 / 2008 y a través de menú File -> New Web Site creamos una nueva aplicación web a la que denominaremos Extending_SP_Searches. Lo primero que tenemos que hacer es referenciar el ensamblado adecuado:

  • Microsoft.SharePoint.Search.dll y Microsoft.SharePoint.dll para WSS 3.0.
  • Microsoft.Office.Server.Search.dll y Microsoft.SharePoint.Server.dll para MOSS.

En nuestro caso, vamos a añadir los ensamblados necesarios para extender las búsquedas de MOOS. Para ello, hacemos clic en el nombre del proyecto y a través de la opción Add refence añadimos las referencias a los ensamblados de MOSS comentados. Estos ensamblados se encuentran en el siguiente path del servidor:

    C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\ISAPI

Dentro del ensamblado Microsoft.Office.Server.Search, tenemos el espacio de nombres Microsoft.SharePoint.Search.Query que incluye tres clases:

  • Query, que no está pensada para uso directo, sino que es la implementación base de las clases de búsqueda dentro del Search Query Object Model.
  • FullTextSqlQuery, que permite ejecutar consultas de búsqueda utilizando SQL Syntax. Es esta clase la que soporta los elementos que nos permitirán construir consultas al estilo SQL: FREETEXT(), CONTAINS(), LIKE, ORDER BY, etc.
  • KeywordQuery, que permite ejecutar consultas de búsqueda utilizando Keyword Syntax.

A la hora de utilizar una clase u otra, tenemos que considerar cuál es el nivel de complejidad de las consultas de búsqueda que necesitamos implementar:

  • Para búsquedas por palabra clave, usaremos la clase KeywordQuery, que nos permite pasar directamente el término a buscar al componente de búsqueda. De esta forma, nos evitamos el parseo a través de los términos de búsqueda para construir la consulta.
  • Para búsquedas más complejas, usaremos la clase FullTextSqlQuery, que supera las limitaciones de la opción Keyword Syntax en cuanto a que sólo devuelve aquellos resultados que contengan de manera exacta el término de búsqueda especificado. Además, SQL Syntax permite construir agrupaciones complejas y soporta elementos de consulta adicionales que no son soportados por la opción Keyword Syntax.

Una vez añadidas las referencias, añadimos al proyecto un nuevo ítem de tipo Web Form y le damos el nombre de SQL Syntax:

image image  

imageAbrimos la página que acabamos de crear en modo de diseño y añadimos los siguientes elementos (ver Figura):

  • Un control de tipo Table de la sección HTML de la Toolbox.
  • Un control de tipo TextBox en el que configuramos la propiedad TextMode con la opción MultiLine.
  • Un control de tipo DrowDownList.
  • Un control de tipo Label en el que configuramos la propiedad Text con el texto Tipo de Consulta.
  • Un control de tipo TextBox en el que configuramos la propiedad TextMode con la opción MultiLine.
  • Un control de tipo Button en el que configuramos la propiedad Text con el texto Buscar.
  • Un control de tipo Label.
  • Un control de tipo GridView.

El aspecto en vista de diseño de la web debería ser el siguiente:

image 

Una vez que hemos diseñado la página de búsquedas en la que habilitaremos SQL Syntax, nos vamos a la vista de código de la misma presionando F7. En esta vista de código vamos a añadir toda la lógica necesaria para poder probar SQL Syntax fuera del entorno de la plataforma SharePoint:

  • Lo primero que vamos a hacer es añadir las correspondientes sentencias using para poder utilizar los espacios de nombres clave para acceder a las clases de búsquedas de MOSS:

using Microsoft.Office.Server;

using Microsoft.Office.Server.Search;

using Microsoft.Office.Server.Search.Query;

using Microsoft.SharePoint;

  • Añadimos las siguientes constantes a la clase parcial asociada a la página (os marco en negrita las constantes en las que he reflejado las consultas que incluye la página ASP.NET):

    #region Variables & Constates

    //Variables y constantes necesarias   

    public const int DefaultRowLimit = 10;

    public const int startRecordNum = 1;

    //Constantes valores combo

    const string ID_QUERY = «ID_Query»;

    const string NOMBRE_QUERY = «Query»;

    public const string TIPO_QUERY0_ID = «Q0»;

    public const string TIPO_QUERY0_TEXT = «Seleccione un tipo de query…»;

    public const string TIPO_QUERY1_ID = «Q1»;

    public const string TIPO_QUERY1_TEXT = «Keyword Query»;

    public const string TIPO_QUERY2_ID = «Q2»;

    public const string TIPO_QUERY2_TEXT = «Búsqueda por Scope»;

    public const string TIPO_QUERY3_ID = «Q3»;

    public const string TIPO_QUERY3_TEXT = «Búsqueda por Scope y Campo»;

    public const string TIPO_QUERY4_ID = «Q4»;

    public const string TIPO_QUERY4_TEXT = «Búsqueda con LIKE»;

    public const string TIPO_QUERY5_ID = «Q5»;

    public const string TIPO_QUERY5_TEXT = «Búsqueda con CONTAINS y *»;

    public const string TIPO_QUERY6_ID = «Q6»;

    public const string TIPO_QUERY6_TEXT = «Búsqueda con FreeText»;

    public const string TIPO_QUERY7_ID = «Q7»;

    public const string TIPO_QUERY7_TEXT = «Búsqueda con FreeText y Op. Exclusión»;

    //Constantes para las queries

    public const string QUERY1 = «Introduzca la palabra clave a buscar…»;

    public const string QUERY2 = «SELECT Rank, Title, Author, Size, Path, Description» +

                            » FROM SCOPE() WHERE \»scope\»=’All Sites'»;

    public const string QUERY3 = «SELECT Rank, Title, Author, Size, Path, Description» +

                            » FROM SCOPE() WHERE \»scope\»=’All Sites'» +

                            » AND  Author=’LitwareInc Administrator’ ORDER BY Rank DESC»;

    public const string QUERY4 = «SELECT Rank, Title, Author, Size, Path, Description» +

                            » FROM SCOPE() WHERE \»scope\»=’All Sites'» +

                            » AND TITLE LIKE ‘Bla%’ ORDER BY Rank DESC»;

    public const string QUERY5 = «SELECT Rank, Title, Author, Size, Path, Description» +

                            » FROM SCOPE() WHERE CONTAINS (‘\»Bla*\»‘)»;

    public const string QUERY6 = «SELECT Rank, Title, Author, Size, Path, Description» +

                            » FROM SCOPE() WHERE \»scope\»=’All Sites'» +

                            «AND FreeText (‘Bla%’) ORDER BY Rank DESC»;

    public const string QUERY7 = «SELECT Rank, Title, Author, Size, Path, Description» +

                            » FROM SCOPE() WHERE \»scope\»=’All Sites'» +

                            «AND FreeText (‘Bla% -CRM’) ORDER BY Rank DESC»;

    #endregion

Como vemos, estas constantes nos van a permitir realizar varios tipos de consultas mediante SQL Syntax:

  • Consultas utilizando una palabra clave.
  • Consultas a un determinado scope o ámbito (en este caso All Sites).
  • Consultas a un scope y filtrando por un cierto campo (Author).
  • Consultas utilizando la clausula LIKE que permite extender las búsquedas en SharePoint al habilitar el uso de operadores comodín (%).
  • Consultas utilizando el predicado CONTAINS(), que permite buscar el término de búsqueda utilizando caracteres comodín (*) y utilizando proximidad. Con este operador se utiliza habitualmente el comodín *.
  • Consultas utilizando el predicado FreeText(), que permite buscar el término de búsqueda dentro de los documentos.

Además, en el código anterior también podemos observar lo siguiente:

  • Los resultados se ordenan utilizando la cláusula ODER BY Rank DESC, es decir, los resultados se devuelven ordenador de forma descendente por la propiedad Rank que indica el nivel de raking de un cierto resultado devuelto por el motor de búsquedas de SharePoint (MOSS en este caso).
  • En alguna de las consultas se utiliza el operador de exclusión -, que nos permite especificar que se excluyan aquellos resultados que contengan el término que aparece a continuación del operador.

En el evento Page_Load() de la página ASP.NET añadimos el siguiente código:

if (! IsPostBack)

        {

            this.Label1.Text = «»;

            this.Button.Enabled = false;

            //Filling the combo

            DataTable dtlQueries= new DataTable();

            dtlQueries.Columns.Add(ID_QUERY);

            dtlQueries.Columns.Add(NOMBRE_QUERY);         

            DataRow dtrFila;

            //Default Data

            dtrFila = dtlQueries.NewRow();

            dtrFila[ID_QUERY] = TIPO_QUERY0_ID;

            dtrFila[NOMBRE_QUERY] = TIPO_QUERY0_TEXT;

            dtlQueries.Rows.Add(dtrFila);

            //First Query

            dtrFila = dtlQueries.NewRow();

            dtrFila[ID_QUERY] = TIPO_QUERY1_ID;

            dtrFila[NOMBRE_QUERY] = TIPO_QUERY1_TEXT;

            dtlQueries.Rows.Add(dtrFila);

            //Second Query

            dtrFila = dtlQueries.NewRow();

            dtrFila[ID_QUERY] = TIPO_QUERY2_ID;

            dtrFila[NOMBRE_QUERY] =TIPO_QUERY2_TEXT;

            dtlQueries.Rows.Add(dtrFila);

            //Third Query

            dtrFila = dtlQueries.NewRow();

            dtrFila[ID_QUERY] = TIPO_QUERY3_ID;

            dtrFila[NOMBRE_QUERY] = TIPO_QUERY3_TEXT;

            dtlQueries.Rows.Add(dtrFila);

            //Fourth Query

            dtrFila = dtlQueries.NewRow();

            dtrFila[ID_QUERY] = TIPO_QUERY4_ID;

            dtrFila[NOMBRE_QUERY] = TIPO_QUERY4_TEXT;

            dtlQueries.Rows.Add(dtrFila);

            //Fith Query

            dtrFila = dtlQueries.NewRow();

            dtrFila[ID_QUERY] = TIPO_QUERY5_ID;

            dtrFila[NOMBRE_QUERY] = TIPO_QUERY5_TEXT;

            dtlQueries.Rows.Add(dtrFila);

            //Sixth Query

            dtrFila = dtlQueries.NewRow();

            dtrFila[ID_QUERY] = TIPO_QUERY6_ID;

            dtrFila[NOMBRE_QUERY] = TIPO_QUERY6_TEXT;

            dtlQueries.Rows.Add(dtrFila);

            //Seventh Query

            dtrFila = dtlQueries.NewRow();

            dtrFila[ID_QUERY] = TIPO_QUERY7_ID;

            dtrFila[NOMBRE_QUERY] = TIPO_QUERY7_TEXT;

            dtlQueries.Rows.Add(dtrFila);

            //Adding the data to the dropdownlist

            this.DropDownList1.DataSource = dtlQueries;

            this.DropDownList1.DataTextField = NOMBRE_QUERY;

            this.DropDownList1.DataValueField = ID_QUERY;

            this.DropDownList1.DataBind();

            this.DropDownList1.AutoPostBack = true;

            this.DropDownList1.SelectedItem.Value = TIPO_QUERY0_ID;

        }

Como vemos, el código anterior nos permite configurar en la primera carga de la página los parámetros iníciales de los controles de la misma. En particular, estamos realizando las siguientes tareas:

  • Rellenar el control DropDownList con los distintos tipos de consultas que vamos a realizar.
  • Configuramos la propiedad AutoPostBack a true para que la página se enteré de que el usuario ha seleccionado una cierta opción y se llame al correspondiente manejador.
  • Especificar el valor del DropDownList que se muestra por defecto.

Codificamos el manejador del evento SelectedIndexChanged del control DropDownList añadiéndolo directamente en la vista de código de la página o bien haciendo doble clic sobre dicho control desde la vista de diseño de la página:

  protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e)

    {

        if (this.DropDownList1.SelectedItem.Value != «0»)

        {

            this.Label1.Text = «»;

            string sOpcionQuery = this.DropDownList1.SelectedItem.Value;

            switch (sOpcionQuery)

            {

                case TIPO_QUERY1_ID:

                    this.Button.Enabled = true;

                    this.TextBox1.Text = QUERY1;

                    break;

                case TIPO_QUERY2_ID:

                    this.Button.Enabled = true;

                    this.TextBox1.Text = QUERY2;

                    break;

                case TIPO_QUERY3_ID:

                    this.Button.Enabled = true;

                    this.TextBox1.Text = QUERY3;

                    break;

                case TIPO_QUERY4_ID:

                    this.Button.Enabled = true;

                    this.TextBox1.Text = QUERY4;

                    break;

                case TIPO_QUERY5_ID:

                    this.Button.Enabled = true;

                    this.TextBox1.Text = QUERY5;

                    break;

                case TIPO_QUERY6_ID:

                    this.Button.Enabled = true;

                    this.TextBox1.Text = QUERY6;

                    break;

                case TIPO_QUERY7_ID:

                    this.Button.Enabled = true;

                    this.TextBox1.Text = QUERY7;

                    break;

                default:

                    this.Label1.Text = «»;

                    this.Button.Enabled = false;

                    break;

            }

        }

        else

        {

            this.Label1.Text = «»;

            this.Button.Enabled = false;

        }

    }

Con el código anterior, estamos pintando en la control TextBox de la página la consulta, utilizando SQL Syntax o Keyword Syntax, seleccionada por el usuario.

  • Codificamos el manejador del evento clic del control Button utilizando una de las dos opciones comentadas, SQL Syntaxt o Keyword Syntax, para el manejador SelectedIndexChanged del control DropDownList (os marco de nuevo en negrita las partes clave del código):

      protected void Button1_Click(object sender, EventArgs e)

    {

        try

        {

            this.Label1.Text = «»;           

            int endRecordNum, totalNumRecords;

            SPSite spsSitio =

                new SPSite(http://litwaredemo:12000/sites/Intranet/default.aspx);

            //Keyword syntax

            KeywordQuery qRequest = new KeywordQuery(spsSitio);

            //SQL Syntax

            FullTextSqlQuery searchQuery = new FullTextSqlQuery(spsSitio);

            ResultTableCollection searchResultTableCollection;

            if (this.DropDownList1.SelectedItem.Value

                    == TIPO_QUERY1_ID)

            {

                qRequest.EnableStemming = true;

                qRequest.KeywordInclusion = KeywordInclusion.AllKeywords;

                qRequest.ResultTypes = ResultType.RelevantResults;

                qRequest.TrimDuplicates = true;

                qRequest.QueryText = this.TextBox1.Text;

                qRequest.StartRow = startRecordNum;

                qRequest.RowLimit = DefaultRowLimit;

                searchResultTableCollection = qRequest.Execute();

            }

            else

            {

                 searchQuery.EnableStemming = true;

                searchQuery.KeywordInclusion = KeywordInclusion.AllKeywords;

                searchQuery.ResultTypes = ResultType.RelevantResults;

                searchQuery.TrimDuplicates = true;

                searchQuery.QueryText = this.TextBox1.Text;

                searchQuery.StartRow = startRecordNum;

                searchQuery.RowLimit = DefaultRowLimit; 

                searchResultTableCollection = searchQuery.Execute();

            }        

            ResultTable searchResultTable = null;

            if ((int)ResultType.RelevantResults != 0)

            {

                searchResultTable =

                    searchResultTableCollection[ResultType.RelevantResults];

            } 

            totalNumRecords = searchResultTable.TotalRows;

            endRecordNum = startRecordNum + searchResultTable.RowCount – 1;

            if (totalNumRecords > 0)

            {

                this.Label1.Text = «Número de coincidencias » + endRecordNum.ToString();

                DataTable resultDataTable = new DataTable();

                resultDataTable.TableName = «SearchResultTable»;

                DataSet resultDataSet = new DataSet(«SearchResultDataSet»);

                resultDataSet.Tables.Add(resultDataTable);

                resultDataSet.Load(searchResultTable,

                    LoadOption.OverwriteChanges, resultDataTable);

                this.GridView1.DataSource = resultDataSet;

                this.GridView1.DataBind();

            }

            else

            {

                this.Label1.Text = «No hay resultados para esta búsqueda»;

                this.GridView1.DataSource = null;

                this.GridView1.DataBind();

            }

        }

        catch (Exception ex)

        {

            this.Label1.Text = ex.Message;

        }

    }

Como vemos en el listado del código, los pasos necesarios para realizar consultas utilizando SQL Syntax o Keyword Syntax son los siguientes:

  • Definir un objeto de tipo SPSite en el que especificaremos el sitio de SharePoint dónde vamos a realizar búsquedas.
  • Definimos un objeto de tipo KeywordQuery para poder realizar búsquedas por palabra clave en el sitio indicado.
  • Definimos un objeto de tipo FullTextSqlQuery para poder realizar consultas en el sitio indicado utilizando SQL Syntax.
  • En función de la opción seleccionada por el usuario, búsqueda por palabra clave o mediante una consulta SQL Syntax, parametrizamos de forma adecuada el objeto correspondiente y ejecutamos la consulta:
    • En el caso de Keyword Syntax, es el propio componente de búsqueda de MOSS el que se encarga de construir la query a partir de la palabra clave especificada.
    • En el caso de SQL Syntax, es la lógica de nuestra aplicación la que se encarga de construir la consulta parametrizada y pasársela al componente de búsqueda de SharePoint.

En cualquiera de las dos situaciones, la consulta se ejecuta mediante el método Execute() y el resultado devuelto (una colección de registros que cumplen las condiciones de búsqueda definidas) lo almacenamos en un objeto de tipo ResultTableCollection.

  • A continuación, comprobamos si la búsqueda devuelve resultados considerados por el motor de búsqueda como relevantes de acuerdo a los criterios de búsqueda. Si es así, almacenamos estos resultados relevantes en un objeto de tipo ResultTable.
  • Sin más, comprobamos que hay resultados en el objeto de tipo ResultTable y los renderizamos en el control GridView e la página.

Probando la solución de búsqueda con SQL Syntax y Keyword Syntax

Una vez diseñada la aplicación de búsquedas preparada para definir búsquedas con SQL Syntax y Keyword Syntax, así como implementada la lógica de búsquedas, vamos a probar dos de las situaciones modelada:

  • Búsqueda mediante palabra clave:
    • Escogemos la opción Keyword query.
  • Búsqueda mediante SQL Syntax:
    • Seleccionamos la opción Búsqueda con FreeText y Op. de Exclusión.
image image

Como conclusión, el modelo de objetos de búsqueda de WSS 3.0 & MOSS habilita la definición de escenarios de búsqueda más flexibles y completo que la funcionalidad que viene a la caja en ambos entornos. Particularmente flexible es la funcionalidad SQL Syntax. Espero que el post os haya resultado interesante. En la próxima entrega (la última de la serie) veremos como usar el servicio web de búsqueda de SharePoint.

S + S: Microsoft Dynamics CRM Online!

Aunque ya hace casi un mes del anuncio, no quería dejar pasar por alto el hecho de que en línea con la estrategia Software + Services (S+S) de Microsoft, el pasado 22 de abril realizó el lanzamiento oficial de Microsoft Dynamics CRM Online. De momento, esta versión de Microsoft Dynamics CRM sólo estará disponible en Norteamerica. Más información sobre Microsoft Dynamics CRM Online:

Visual Studio 2008 SP1 Beta: Novedades en LINQ To SQL!

El otro día Unai nos comentaba algunos de las principales novedades que aparecen en ADO.NET Entity Framework con la beta1 del SP1 de Visual Studio 2008 y .NET Framework 3.5. Pues bien, tal y como nos comentan en el blog del equipo de ADO.NET, no sólo tenemos novedades en ADO.NET Entity Framework, sino que también las habrá en LINQ To SQL (ya nos la habían anunciado previamente). En concreto, la lista de novedades y mejoras es la siguiente:

  • Se han solucionado numerosos bugs reportados.
  • Mejor traducción de consultas en el caso de comparar columnas nullable en Visual Basic.
  • Soporte a nivel de diseñador y runtime para SQL Server 2008:
    • Soporte de conexión a bases de datos SQL Server 2008 en el server explorer.
    • Soporte de drag & drop de tablas de bases de datos de SQL Server 2008.
    • Soporte para algunos de los nuevos tipos de datos que aparecen con SQL Server 2008: Date, Time, DateTime2, DateTimeOffset y Filestream.

…en resumen, creo que como a muchos, me tocará rehacer y testear parte de los ejemplos y demos que tengo tanto de LINQ To SQL como de ADO.NET Entity Framework para evitar sorpresas si uso la beta 1 del SP1 de Visual Studio 2008 y .NET Framework.

image

image

image