Agregar ensamblados adicionales a nuestra solución de SharePoint 2010

En esta entrada veremos cómo agregar ensamblados adicionales a nuestras soluciones de SharePoint 2010 y poder implementarlos en el GAC o a nivel WebApplication en nuestros servidores.

El primer paso es abrir el paquete de nuestra solución.

Una vez tengamos el paquete abierto, accedemos a la sección “Avanzadas” que encontraremos en la parte inferior derecha.

Ahora debemos hacer clic en el botón “Agregar” y seleccionar “Agregar ensamblado existente…”.

Sólo queda seleccionar el ensamblado de interés y el destino de la implementación.

C# a fondo: Las clases System.Net.FtpWebRequest y System.Net.FtpWebResponse

El espacio de nombres System.Net del .NET Framework proporciona una interfaz de programación sencilla para muchos de los protocolos que se utilizan en las redes.

La clase FtpWebRequest implementa un cliente FTP.

La clase FtpWebResponse encapsula la respuesta de un servidor de FTP a una solicitud.

La clase WebRequestMethods.Ftp representa los tipos de métodos del protocolo FTP que se pueden utilizar con una solicitud FTP.

Veamos un ejemplo de trabajo con un servidor FTP usando estas clases que nos brinda el .NET Framework, en concreto las operaciones de subida, listado, descarga y borrado de ficheros.

Espacios de nombres

using System.IO;
using System.Net;

Código común a las distintas operaciones

const string server = "ftp://localhost/";
static NetworkCredential credentials = new NetworkCredential("ftpuser", "ftppw");

const string document = "Documento.pdf";
static string uploads = Environment.CurrentDirectory + @"Subidas";
static string downloads = Environment.CurrentDirectory + @"Descargas";

static void Main(string[] args)
{
    UploadFile();
    ListDirectory();
    DownloadFile();
    DeleteFile();
}

Subida de archivo

public static void UploadFile()
{
    FtpWebRequest request = (FtpWebRequest)WebRequest.Create(server + document);
    request.Credentials = credentials;
    request.Method = WebRequestMethods.Ftp.UploadFile;

    byte[] content = File.ReadAllBytes(uploads + document);

    request.ContentLength = content.Length;

    Stream stream = request.GetRequestStream();
    stream.Write(content, 0, content.Length);
    stream.Close();
}

Listado de contenido de directorio

public static void ListDirectory()
{
    FtpWebRequest request = (FtpWebRequest)WebRequest.Create(server);
    request.Credentials = credentials;
    request.Method = WebRequestMethods.Ftp.ListDirectory;

    using (StreamReader reader = new StreamReader(((FtpWebResponse)request.GetResponse()).GetResponseStream()))
    {
        Console.WriteLine(reader.ReadToEnd());
        reader.Close();
    }

    Console.Read();
}

Descarga de archivo

public static void DownloadFile()
{
    FtpWebRequest request = (FtpWebRequest)WebRequest.Create(server + document);
    request.Credentials = credentials;
    request.Method = WebRequestMethods.Ftp.DownloadFile;

    using (MemoryStream stream = new MemoryStream())
    {
        ((FtpWebResponse)request.GetResponse()).GetResponseStream().CopyTo(stream);
        File.WriteAllBytes(downloads + document, stream.ToArray());
    }
}

Borrado de archivo

public static void DeleteFile()
{
    FtpWebRequest request = (FtpWebRequest)WebRequest.Create(server + document);
    request.Credentials = credentials;
    request.Method = WebRequestMethods.Ftp.DeleteFile;

    request.GetResponse();
}

Para finalizar, el código en un proyecto de ejemplo de Visual Studio 2010 como fichero adjunto.

Referencia:

System.Net (Espacio de nombres)

FtpWebRequest (Clase)

FtpWebResponse (Clase)

WebRequestMethods.Ftp (Clase)

Cómo usar la plantilla SketchFlow para Windows Phone con Expression Blend en español

El proyecto SketchFlow Template for Windows Phone añade a Expression Blend + SketchFlow un nuevo tipo de proyecto para realizar prototipos de aplicaciones Windows Phone de manera rápida y sencilla.

La sorpresa desagradable que nos encontramos tras su instalación en la versión en español de Expression Blend es que no aparece ningún tipo nuevo de proyecto en el entorno:

El problema es que el instalador de la plantilla ubica el contenido en la carpeta en inglés y no tiene en consideración la posible existencia de otros idiomas. La solución pasa por copiar el archivo csWindowsPhonePrototype.zip desde su ubicación en la subcarpeta ProjectTemplatesenCSharpWindowsPhone de la ruta de instalación de Expression Blend (por defecto C:Program Files (x86)Microsoft ExpressionBlend 4) a ProjectTemplatesesCSharpWindowsPhone.

Por ahora no he detectado ningún funcionamiento anómalo tras este “apaño”, pero si detectáis cualquier tipo de problema al trabajar con esta plantilla no dudéis en comentarlo.

Borrado eficiente de documentos y elementos en SharePoint 2010

El modelo de objetos de SharePoint 2010, en concreto la clase SPWeb, nos brinda la posibilidad de ejecutar múltiples operaciones en el servidor de manera transaccional gracias al método ProcessBatchData.

Veamos un ejemplo de borrado completo del contenido de una biblioteca y una lista que nos sirva como toma de contacto con la sintaxis y modo de empleo.

Escenario

Partimos de una biblioteca de documentos y de una lista de contactos estándar de SharePoint con los siguientes datos:

Código

Creamos una aplicación de consola en el servidor de SharePoint estableciendo en las propiedades del proyecto que la plataforma de destino es x64.

Agregamos el espacio de nombres necesario:

using Microsoft.SharePoint;

A continuación el código inicial del ejemplo. La línea comentada realizaría el vaciado de la papelera posterior a la eliminación de todo el contenido de la lista y la biblioteca.

static void Main(string[] args)
{
    using (SPWeb web = new SPSite("http://intranet").OpenWeb())
    {
        web.ProcessBatchData(GenerarLoteBorradoElementos(web.Lists["Contactos"]).ToString());
        web.ProcessBatchData(GenerarLoteBorradoDocumentos((SPDocumentLibrary)web.Lists["Documentos"]).ToString());

        // web.RecycleBin.DeleteAll();
    }
}

Borrado de elementos de una lista

private static StringBuilder GenerarLoteBorradoElementos(SPList lista)
{
    StringBuilder SB = new StringBuilder();
    SB.Append("<?xml version="1.0" encoding="UTF-8"?><Batch>");

    string comando = "<Method><SetList>" + lista.ID + "</SetList><SetVar Name="ID">{0}</SetVar><SetVar Name="Cmd">Delete</SetVar></Method>";

    foreach (SPListItem item in lista.Items)
        SB.Append(string.Format(comando, item.ID.ToString()));

    SB.Append("</Batch>");

    return SB;
}

Borrado de documentos de una biblioteca

La diferencia respecto al código de borrado de la lista es que para cada item de la biblioteca hay que especificar la URL relativa del documento.

private static StringBuilder GenerarLoteBorradoDocumentos(SPDocumentLibrary biblioteca)
{
    StringBuilder SB = new StringBuilder();
    SB.Append("<?xml version="1.0" encoding="UTF-8"?><Batch>");

    string comando = "<Method><SetList>" + biblioteca.ID + "</SetList><SetVar Name="ID">{0}</SetVar><SetVar Name="Cmd">Delete</SetVar><SetVar Name="owsfileref">{1}</SetVar></Method>";

    foreach (SPListItem item in biblioteca.Items)
        SB.Append(string.Format(comando, item.ID.ToString(), item.File.ServerRelativeUrl));

    SB.Append("</Batch>");

    return SB;
}

Para finalizar, el código en un proyecto de ejemplo de Visual Studio 2010 como fichero adjunto.

Accediendo a ASP.NET Page Methods y servicios WCF desde JavaScript con jQuery

Un Page Method es un método declarado como public y static definido en el codebehind de una página aspx y “decorado” con el atributo WebMethod usado para los métodos de los servicios web.

Windows Communication Foundation (WCF) es el modelo de programación de Microsoft para aplicaciones orientadas a servicios.

Veamos un ejemplo de cómo acceder a un Page Method y un servicio WCF con jQuery, utilizando para ello la versión 1.7.1. Comenzamos por una captura de la solución para tener clara la estructura:

A continuación, el código html común a ambas operaciones para ver posteriormente el código y configuración del lado servidor y la función JavaScript necesaria en cada caso.

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Page methods y servicios WCF desde jQuery</title>
    <script type="text/javascript" src="Scripts/jquery-1.7.1.min.js"></script>
</head>
<body>
    <form id="form1" runat="server">
        <div>
            <input type="button" value="Recibir mensaje desde page method" onclick="RecibirMensajeDesdePageMethod()" />
            <input type="button" value="Recibir mensaje desde WCF" onclick="RecibirMensajeDesdeWCF()" />
            <p id="Mensaje"></p>
        </div>
    </form>
</body>
</html>

Accediendo a un Page Method con jQuery

El código de servidor para acceder a un Page Method es muy sencillo y no necesita ninguna configuración especial. Como se ha comentado al principio sólo necesitamos declarar un método de nuestra página aspx como público y estático y decorarlo con el atributo WebMethod para poder utilizarlo directamente desde cliente:

[WebMethod]
public static string EnviarMensajeDesdePageMethod()
{
    return "Llamada a un page method desde jQuery.";
}

Sólo nos queda desarrollar la función JavaScript encargada de la llamada y procesamiento del resultado con la ayuda de jQuery:

<script type="text/javascript">
    function RecibirMensajeDesdePageMethod() {
        $.ajax({
            type: "POST",
            url: "Default.aspx/EnviarMensajeDesdePageMethod",
            data: "{}",
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            success: function (mensaje) {
                $("#Mensaje").text(mensaje.d);
            } 
        });
    }
</script>

Para finalizar, una captura del resultado:

Consumiendo un servicio WCF con jQuery

La creación y consumo desde JavaScript de un servicio WCF es un poco más complejo. Para empezar, necesitamos agregar una referencia a System.ServiceModel.Web en nuestro proyecto ya que definiremos en el contrato de operación el método de llamada y formato de respuesta entre otras cosas. Vamos a ver el contrato del servicio:

using System.ServiceModel.Web;

namespace jQueryDemo.Services
{
    [ServiceContract]
    public interface IMensajes
    {
        [OperationContract]
        [WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.Wrapped, ResponseFormat = WebMessageFormat.Json)]
        string EnviarMensajeDesdeWCF();
    }
}

A continuación, la implementación del contrato:

namespace jQueryDemo.Services
{
    public class Mensajes : IMensajes
    {
        public string EnviarMensajeDesdeWCF()
        {
            return "Llamada a un servicio WCF desde jQuery.";
        }
    }
}

Ahora la configuración del servicio en su correspondiente sección del Web.config:

<system.serviceModel>
  <behaviors>
    <serviceBehaviors>
      <behavior name="ServiceBehavior">
        <serviceMetadata httpGetEnabled="true"/>
        <serviceDebug includeExceptionDetailInFaults="true"/>
      </behavior>
    </serviceBehaviors>
    <endpointBehaviors>
      <behavior name="EndpointBehavior">
        <webHttp />
      </behavior>
    </endpointBehaviors>
  </behaviors>
  <services>
    <service behaviorConfiguration="ServiceBehavior" name="jQueryDemo.Services.Mensajes">
      <endpoint address="" binding="webHttpBinding" contract="jQueryDemo.Services.IMensajes" behaviorConfiguration="EndpointBehavior"/>
    </service>
  </services>
</system.serviceModel>

Y por último la función JavaScript encargada de la llamada y procesamiento del resultado con la ayuda de jQuery:

<script type="text/javascript">
    function RecibirMensajeDesdeWCF() {
        $.ajax({
            type: "POST",
            url: "Services/Mensajes.svc/EnviarMensajeDesdeWCF",
            data: "{}",
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            success: function (mensaje) {
                $("#Mensaje").text(mensaje.EnviarMensajeDesdeWCFResult);
            }
        });
    }
</script>

Para finalizar, una captura del resultado y el código en un proyecto de ejemplo de Visual Studio 2010 como fichero adjunto:

C# a fondo: La clase System.Diagnostics.Trace

El espacio de nombres System.Diagnostics del .NET Framework proporciona clases que nos permiten interactuar con los procesos del sistema, registros de eventos, contadores de rendimiento y depurar la aplicación y hacer seguimiento de la ejecución del código.

La clase Trace nos proporciona un conjunto de métodos y propiedades que nos facilitan el seguimiento del código.

Es tarea común en determinados procesos tener que mostrar por línea de comandos información acerca de la ejecución para mantener informado al usuario y a su vez almacenar esa información en un archivo de log para su tratamiento posterior. Veamos como la clase Trace nos facilita el desarrollo en escenarios como el planteado, en el que necesitamos escribir en varios agentes de escucha de seguimiento:

Espacio de nombres

using System.Diagnostics;


Código

string Log = @"C:WindowsTempRegistro.log";

Trace.Listeners.Clear();
Trace.Listeners.Add(new TextWriterTraceListener(Log));
Trace.Listeners.Add(new ConsoleTraceListener(false));
Trace.AutoFlush = true;

Trace.WriteLine("Información a mostrar al usuario y registrar en el log.");

La funcionalidad proporcionada por la clase Trace va mucho más allá de lo mostrado en este ejemplo. Para profundizar en el tema, tenemos a nuestra disposición la siguiente documentación:

Referencia:

System.Diagnostics (Espacio de nombres)

Trace (Clase)

TraceListener (Clase)

Creación de un procedimiento almacenado de SQL Server con C#

SQL Server incorpora integrado el componente CLR del .NET Framework desde su versión 2005, lo que nos permite aprovechar “toda” la potencia de .NET para realizar nuestros desarrollos de bases de datos. Realmente los espacios de nombres que se admiten en esta integración son limitados. Tenemos un listado completo en Bibliotecas de .NET Framework admitidas.

Los tipos de elementos que podemos desarrollar gracias a esta integración con el CLR son: procedimientos almacenados, desencadenadores, agregados, funciones definidas por el usuario y tipos definidos por el usuario.

Para habilitar la integración CLR en SQL Server debemos ejecutar el siguiente script de configuración:

sp_configure 'show advanced options', 1;
go
reconfigure;
go
sp_configure 'clr enabled', 1;
go
reconfigure;

Veamos ahora un ejemplo de cómo desarrollar, implementar y ejecutar un procedimiento almacenado usando SQL CLR. Para ello vamos a utilizar Visual Studio 2010 y SQL Server 2008.

Desarrollo

Creamos un nuevo proyecto en Visual Studio utilizando la plantilla “Proyecto de base de datos SQL CLR de Visual C#”.

A continuación, elegimos la conexión de base de datos que queremos utilizar empleando el asistente y, una vez creado el proyecto, agregamos un nuevo elemento “Procedimiento almacenado”.

Desarrollamos la funcionalidad del procedimiento almacenado, en este ejemplo recibe como parámetros el nombre y apellidos de un cliente y lo inserta en la tabla correspondiente:

[SqlProcedure]
public static void InsertarCliente(string nombre, string apellidos)
{
    using (SqlConnection conexion = new SqlConnection("context connection=true"))
    {
        SqlCommand comando = new SqlCommand("insert into Cliente (Nombre, Apellidos) values (@Nombre, @Apellidos)", conexion);
        comando.Parameters.Add(new SqlParameter("@Nombre", SqlDbType.NVarChar) { Value = nombre });
        comando.Parameters.Add(new SqlParameter("@Apellidos", SqlDbType.NVarChar) { Value = apellidos });

        conexion.Open();
        comando.ExecuteNonQuery();
        conexion.Close();
    }
}

Destacar en este código la cadena de conexión utilizada, que nos permite emplear el contexto de conexión en el que se ejecuta el procedimiento almacenado evitándonos tener que especificar servidor y credenciales.

Ejecución y depuración

Una vez implementado mediante Visual Studio, podemos ejecutar y depurar el procedimiento almacenado de manera sencilla mediante el “Explorador de servidores”:

Implementación

La implementación del procedimiento almacenado sin utilizar Visual Studio, en un entorno de producción por ejemplo, la llevamos a cabo mediante el siguiente código:

create assembly DemoSQLCLR from 'c:DemoSQLCLR.dll' with permission_set = safe
go
create procedure InsertarCliente
(
	@Nombre nvarchar(50),
	@Apellidos nvarchar(150)
)
as external name DemoSQLCLR.StoredProcedures.InsertarCliente

Y para retirar la implementación debemos ejecutar lo siguiente:

drop procedure InsertarCliente
go
drop assembly DemoSQLCLR

Jornada técnica sobre Cloud Computing y Azure: jueves 19 de abril, CNTG en Santiago de Compostela

Jornada técnica sobre Cloud Computing y Azure: Desarrollo de una aplicación y casos prácticos reales

Jueves día 19 de abril de 2012 de 10:00 a 13:30 horas

El jueves de la semana que viene tendrá lugar en el Centro de Novas Tecnoloxías de Galicia una jornada técnica sobre Cloud Computing y Azure.

Participarán en esta jornada José Manuel Alarcón, Antonio Gómez Pavón y, aunque en la noticia figura Rodrigo Corral, Plain Concepts confirmó el día de ayer a través de su twitter que será Luis Guerrero el encargado de la parte práctica.

Aun estáis a tiempo de asistir ya que el plazo de inscripción finaliza el día 18 de abril. Nos vemos allí.

Más información

Registro

C# a fondo: La instrucción yield

La instrucción yield se emplea en un bloque iterador. Un bloque iterador es una sección de código que devuelve una secuencia de valores del mismo tipo.

La instrucción yield se emplea con la instrucción return para proporcionar un valor al objeto enumerador y con la instrucción break para indicar el final de la iteración.

Veamos un ejemplo sencillo que nos aclare el concepto y nos muestre las ventajas de su uso. El siguiente método devuelve la lista de números pares comprendidos entre dos números:

public static IEnumerable<int> ObtenerPares(int inicio, int fin)
{
    List<int> pares = new List<int>();

    for (int i = inicio; i <= fin; i++)
        if ((i % 2) == 0)
            pares.Add(i);

    return pares;
}

La instrucción yield nos da la oportunidad de llevar a cabo este tipo de operaciones sin tener que declarar una variable en la que ir almacenando los distintos valores antes de proceder a su devolución como resultado:

public static IEnumerable<int> ObtenerPares(int inicio, int fin)
{
    for (int i = inicio; i <= fin; i++)
        if ((i % 2) == 0)
            yield return i;
}

Referencia:

yield (Referencia de C#)

Iteradores (Guía de programación de C#)

Fuentes personalizadas en nuestros proyectos web: Google Web Fonts y CSS3

A la hora de trabajar en el diseño y desarrollo de nuestra web nos encontramos con el problema del limitado número de fuentes que podemos utilizar por la falta de seguridad de que nuestros usuarios las tengan instaladas en sus equipos. Vamos a ver a continuación dos soluciones que nos permiten usar fuentes personalizadas en nuestros proyectos.

Google Web Fonts

Google Web Fonts es un repositorio de fuentes open source optimizadas para web. El modo de empleo es muy sencillo, sólo hay que seleccionar las fuentes que nos interesan y añadir a nuestra web el código que nos genera. Veamos un ejemplo:

<html>
    <head>
        <title>Google Web Fonts</title>
        <link href='http://fonts.googleapis.com/css?family=Sevillana' rel='stylesheet' type='text/css'>
    </head>
    <body>
        <p style="font-family: 'Sevillana'; font-size: large">Ejemplo de uso de Google Web Fonts.</p>
    </body>
</html>

CSS3

CSS3 nos permite utilizar cualquier fuente mediante la declaración @font-face. Debemos declarar el nombre de la fuente y su ubicación en el servidor y ya podremos utilizarla como cualquier otra fuente en nuestro proyecto web:

<html>
    <head>
        <style type="text/css">
            @font-face {
                font-family: myFont;
                src: url(fonts/TunaAndHotDogsOnRye.eot);
                src: url(fonts/TunaAndHotDogsOnRye.ttf) format("truetype");
            }
        </style>
    </head>
    <body>
        <p style="font-family: 'myFont'; font-size: large">Ejemplo de carga de fuentes con CSS3.</p>
    </body>
</html>

En las pruebas realizadas, con las últimas versiones de Mozilla Firefox y Google Chrome se ha cargado correctamente la fuente con formato ttf. Sin embargo, en Internet Explorer 9 no ha funcionado. En este momento, la línea 6 es obligatoria para la compatibilidad con IE, que sólo soporta el formato eot (Embedded Open Type) propietario de Microsoft.

Para ayudarnos con la conversión del formato ttf a eot, tenemos a nuestra disposición la herramienta open source ttf2eot.