Could not load type ‘System.ServiceModel.Activation.HttpModule’ from assembly ‘System.ServiceModel, Version=3.0.0.0 [...]

Uno de los incidentes que nos podemos encontrar al tener instalado el nuevo Framework 4.0 es que nuestras aplicaciones con WCF anteriores dejen de funcionar por el siguiente motivo:

Para solucionarlo, debemos acceder al servidor de aplicaciones (IIS), nos posicionamos en el sitio que nos está fallando y accedemos al apartado de Módulos:

Una vez allí, quitamos la versión 3.0 de ServiceModel, quedando únicamente la versión 4.0.

Espero que sea de utilidad.

¡Saludos!

Utilizando el API de TinyUrl

Todos aquellos que frecuentamos Twitter estamos más que acostumbrados a ver direcciones cortas generadas por distintos proveedores. En este post voy a mostrar un pequeño ejemplo de cómo podríamos utilizar uno de estos proveedores, TinyUrl, para conseguir esas direcciones cortas desde nuestra aplicación.

En primer lugar he creado un pequeño formulario Ajax con ASP.NET MVC:


<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Tiny Url Maker
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <p>
        <% using (Ajax.BeginForm("MakeTinyUrl", new AjaxOptions { HttpMethod = "Post", UpdateTargetId = "tinyUrl" }))
           {%>
        <%=Html.TextBox("url") %>
        <input id="btnMakeUrl" type="submit" value="Make tinyUrl" />
        <%
            }%>
    </p>
    <div id="tinyUrl">
        <%Html.RenderPartial("TinyUrl"); %>
    </div>
</asp:Content>

Al pulsar en el botón, de manera asíncrona, llamaremos a la acción MakeTinyUrl donde llamaremos a la API disponible.


using System.IO;
using System.Net;
using System.Web.Mvc;

namespace TinyURLAPI.Controllers
{
    [HandleError]
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            ViewData["Message"] = "Welcome to ASP.NET MVC!";

            return View();
        }

        [HttpPost]
        public ActionResult MakeTinyUrl(string url)
        {
            var tinyUrl = WebRequest.Create("http://tinyurl.com/api-create.php?url=" + url);
            var shortUrl = tinyUrl.GetResponse();

            using (var reader = new StreamReader(shortUrl.GetResponseStream()))
            {
                ViewData["tinyUrl"] = reader.ReadToEnd();
            }
            return PartialView("TinyUrl");

        }
    }
}

Si arrancamos la aplicación e introducimos cualquier url, el resultado sería el siguiente:

¡Saludos!

Crear un documento Excel con Open XML

¿Cuántas veces hemos necesitado exportar los datos desde una aplicación a un documento Excel? Este es uno de los retos de ayer y de hoy :)
Si bien las opciones son varias y, como no, algunas mejores que otras, he optado por trabajar esta vez con Open XML. Este estándar está basado en archivos XML comprimidos en un ZIP (docx, xlsx, pptx) y es el elegido por Microsoft como formato por defecto a partir de la versión 2007 de Microsoft Office (En la versión 2010 te pregunta cuál es el que quieres utilizar de forma predeterminada). En este post me centraré en las hojas de cálculo aunque también es posible la creación de documentos Word, presentaciones y charts.

DESCARGAS NECESARIAS

Para poder trabajar con Open XML y Visual Studio, necesitamos descargar  Open XML SDK 2.0 for Microsoft Office.
Con este SDK tendremos la capacidad de definir una serie de esquemas XML para la representación de hojas de cálculo, charts, presentaciones y documentos word bajo el estándar ECMA 376 y la aprobación ISO/IEC 29500. Microsoft Office Word 2007/2010, Excel 2007/2010 y PowerPoint 2007/2010 usan Open XML como formato por defecto para sus archivos.

CREACIÓN DE UNA HOJA DE CÁLCULO

Lo primero que necesitamos para comenzar a trabajar con Open XML en Visual Studio es agregar las referencias a DocumentFormat.OpenXML y Windows.Base.

Una hoja de cálculo consta fundamentalmente de tres partes: Un workbook, las hojas y los datos almacenados en cada hoja. La estructura más simple de un documento excel sería algo parecido a esto:

<x:workbook xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:x="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
  <x:sheets>
    <x:sheet xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" name="ReturnGisSheet" sheetId="1" r:id="rId1" />
  </x:sheets>
</x:workbook>

El primer objetivo es crear el documento con los elementos mínimos para que el mismo sea un documento válido.


using System.IO;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;

namespace OpenXMLExcel.Models
{
    public class OpenXml
    {
        public static byte[] CreateExcel()
        {
            var memoryStream = new MemoryStream();
            using (var excel = SpreadsheetDocument.Create(memoryStream, SpreadsheetDocumentType.Workbook, true))
            {
                CreateParts(excel);
            }

            return memoryStream.ToArray();
        }

        public static void CreateParts(SpreadsheetDocument excel)
        {
            //workbook area
            var workbookPart = excel.AddWorkbookPart();
            //workbook content
            CreateWorkbookPartContent(workbookPart);

            //worksheet area
            var worksheetPart = workbookPart.AddNewPart<WorksheetPart>("rId1");
            GenerateWorksheetContent(worksheetPart);
        }

        private static void CreateWorkbookPartContent(WorkbookPart workbookPart)
        {
            var workbook = new Workbook();

            //Workbook sheets
            var sheets = new Sheets();
            var sheet = new Sheet { Name = "ReturnGisSheet", SheetId = 1, Id = "rId1" };

            sheets.Append(sheet);
            workbook.Append(sheets);

            workbookPart.Workbook = workbook;
        }

        private static void GenerateWorksheetContent(WorksheetPart worksheetPart)
        {
            var worksheet = new Worksheet();

            var sheetData = new SheetData();

            worksheet.Append(sheetData);

            worksheetPart.Worksheet = worksheet;
        }
    }
}

Cada elemento de un archivo Open XML está alojado en un contenedor específicamente diseñado para ese tipo de elemento. De la misma manera, todos ellos deben estar enlazados entre sí ya que, de lo contrario, el documento no se abrirá (En el caso de Office 2003/2007) o bien nos solicitará su reparación, lo cual no es muy elegante.
Si arrancamos el proyecto web y solicitamos el archivo veremos que el nombre de la hoja es ReturnGis y, en el caso de Office 2010, esta aparecerá protegida.

AÑADIR DATOS A LA HOJA DE CÁLCULOS

La parte del documento encargada de contener los datos de una hoja se llama SheetData, la cual está alojada dentro de un WorkSheet y, a su vez, dentro del WorkSheetPart del documento. En este caso alimentaré la hoja de cálculo a través de la base de datos de ejemplo de Adventure Works 2008 R2.


using System;
using System.IO;
using System.Linq;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;

namespace OpenXMLExcel.Models
{
    public class OpenXml
    {
        public static byte[] CreateExcel()
        {
            var memoryStream = new MemoryStream();
            using (var excel = SpreadsheetDocument.Create(memoryStream, SpreadsheetDocumentType.Workbook, true))
            {
                CreateParts(excel);
            }

            return memoryStream.ToArray();
        }

        public static void CreateParts(SpreadsheetDocument excel)
        {
            //workbook area
            var workbookPart = excel.AddWorkbookPart();
            //workbook content
            CreateWorkbookPartContent(workbookPart);

            //worksheet area
            var worksheetPart = workbookPart.AddNewPart<WorksheetPart>("rId1");
            GenerateWorksheetContent(worksheetPart);
        }

        private static void CreateWorkbookPartContent(WorkbookPart workbookPart)
        {
            var workbook = new Workbook();

            //Workbook sheets
            var sheets = new Sheets();
            var sheet = new Sheet { Name = "ReturnGisSheet", SheetId = 1, Id = "rId1" };

            sheets.Append(sheet);
            workbook.Append(sheets);

            workbookPart.Workbook = workbook;
        }

        private static void GenerateWorksheetContent(WorksheetPart worksheetPart)
        {
            var worksheet = new Worksheet();

            var sheetData = new SheetData();

            var adventureWorksEntities = new AdventureWorksEntities();
            var creditCardData = adventureWorksEntities.CreditCard.ToList();

            for (var i = 0; i < creditCardData.Count; i++)
            {
                var rowNumber = (i + 1).ToString();
                var row = new Row { RowIndex = UInt32.Parse(rowNumber) };
                var creditCardId = new Cell { CellReference = "A" + rowNumber, CellValue = new CellValue(creditCardData[i].CreditCardID.ToString()) ,DataType = CellValues.Number};
                row.Append(creditCardId);
                var cardType = new Cell { CellReference = "B" + rowNumber, CellValue = new CellValue(creditCardData[i].CardType), DataType = CellValues.String };
                row.Append(cardType);
                var cardNumber = new Cell { CellReference = "C" + rowNumber, CellValue = new CellValue(creditCardData[i].CardNumber), DataType = CellValues.Number };
                row.Append(cardNumber);
                var expMonth = new Cell { CellReference = "D" + rowNumber, CellValue = new CellValue(creditCardData[i].ExpMonth.ToString()), DataType = CellValues.Number };
                row.Append(expMonth);
                var expYear = new Cell { CellReference = "E" + rowNumber, CellValue = new CellValue(creditCardData[i].ExpYear.ToString()), DataType = CellValues.Number };
                row.Append(expYear);
                sheetData.Append(row);
            }
            worksheet.Append(sheetData);

            worksheetPart.Worksheet = worksheet;
        }
    }
}

El resultado sería el siguiente:

OPEN XML SDK 2.0 PRODUCTIVITY TOOL FOR MICROSOFT OFFICE

Existen infinidad de combinaciones a la hora de crear un documento, por lo que es bastante complicado aprender y localizar cada una de las propiedades y la forma de asociación entre los elementos que componen un Open XML. Por ello, cuando instalamos el SDK vimos que en la página de descarga aparecían dos elementos disponibles:

El primero de ellos se trata de una herramienta que nos permite ver de una forma jerárquica documentos ya creados, así como la posibilidad de obtener el código necesario para implementarlo en C#. Puede ser recomendable utilizar esta herramienta como guía pero resulta algo complejo de comprender ya que el código no está optimizado para su uso.

También es posible validar nuestros documentos para comprobar que la estructura es la correcta aunque no siempre es efectivo. El motivo es que dependiendo del error cometido en la creación del mismo la herramienta nos permitirá abrir el documento o no.

La gran ventaja de trabajar con este formato es que no existe la necesidad de tener instalado Office en el servidor y creamos nuestros documentos a través de una programación orientada a objetos.

Facilito el proyecto por si fuera de utilidad.

¡Saludos!