EPPlus, o como tratar en C# con Excel en OpenXML y no morir con Interop. Capitulo II

Uno de los proyectos más dolorosos que nos podemos encontrar en .NET, con permiso de todo lo relacionado con SharePoint, es aquel que tenga que manipular ficheros de Office. Siendo el más común el construir hojas de cálculo Excel que muestren informes.

En el inicio de esta miniserie, he instalado los prerrequisitos necesarios para utilizar Interop y EPPlus, he creado un proyecto y un documento Excel. En este segundo, voy a manipular los datos de las celdas y de las hojas realizando las operaciones más normales.

Hacer sitio antes guardar

Aunque yo me he dado cuenta refactorizando, voy a iniciar picando un método que me borre el fichero Excel físico para que EEPlus no se me queje (si encuentra el fichero lo abre, no lo sustituye, y casca por querer meter elementos que ya existen), y para que Interop no me saque la ventanita de “¿Desea sobreescribir?”.

Así creo una clase nueva, la famosa Utilidades, y le escribo un método estático para borrar el fichero.

using System.IO;

namespace GenbetaDevExcel
{
    public class Utilidades
    {
        public static void BorraFichero(string path)
        {
            if (! string.IsNullOrWhiteSpace(path))
            {
                if (File.Exists(path))
                { File.Delete(path); }
            }
        }
    }
}

Lo siguiente que hago es desacoplar el método de guardar, para que sea el test quien decida cuando lanzarlo y dar por acabado el flujo de operaciones. Fíjate que en Interop, invoco el BorraFichero() justo antes de salvar, en cambio en EPPlus no.

INTEROP

public bool GuardaExcel(Excel.Application excel)
        {
            try
            {
                var fichero = @"C:\Users\Juan\Desktop\ExcelInterop.xlsx";
                Utilidades.BorraFichero(fichero);

                string workbookPath = fichero;

                excel.Workbooks[1].SaveAs(workbookPath);
                excel.Workbooks[1].Close();

                return true;
            }
            catch (System.Exception)
            { return false; }
        }

EPPlus

        public bool GuardaExcel(ExcelPackage excel)
        {
            try
            {
                excel.Save();
                return true;
            }
            catch (System.Exception)
            { return false; }
        }

Habiendo sacado el proceso de guardar, y añadiendo el borrado al inicio del código de EPPlus, los métodos de Abrir la Excel han quedado así.

INTEROP

 public Excel.Application CreaNuevaExcelConInterop()
        {
            Excel.Application excel = new Excel.Application();
            excel.Visible = false;
            //var workbook = excel.Workbooks.Open(@"C:\Users\Juan\Desktop\Plantilla_Informes_Indicadores.xlsx");

            //** Añade las hojas al principio y les pone el nombre HojaX automáticamente

            var workbook = excel.Workbooks.Add();
            workbook.Sheets.Add();
            
            return excel;
        }

EPPlus

public ExcelPackage CreaNuevaExcelConEPPlus()
        {
            string fichero = @"C:\Users\Juan\Desktop\ExcelEPPlus.xlsx";
            Utilidades.BorraFichero(fichero);

            ExcelPackage excel = new ExcelPackage(new FileInfo(fichero));
            excel.Workbook.Worksheets.Add("hoja1");
            excel.Workbook.Worksheets.Add("hoja2");

            return excel;
        }

Una curiosidad es el orden en que se insertan las nuevas hojas en el libro de trabajo. En EPPlus las va añadiendo a la última posición con el nombre que le he introducido, en cambio en Interop se añaden siempre en la segunda posición a partir del inicio, y le pone el nombre (en español) hojaXXXX, donde XXXX es un número autoincremental.

Selección de celdas y rangos de celdas

El rudimento mínimo básico de las operaciones contra una Excel es el seleccionar una celda o un rango de celdas, y escribir en ellas.

INTEROP

        public Excel.Application EscribeEnRangoDeCeldas(Excel.Application excel)
        {
            Excel.Worksheet hojaActual = excel.Workbooks[1].Worksheets[1];
            hojaActual.Cells.get_Range("A1").Value2 = "Esto funciona!";
            
            var rangoDeCeldas = hojaActual.Cells.get_Range("$C$1:$C$3");
            rangoDeCeldas = hojaActual.Cells.get_Range("C1:C3");

            return excel;
        }

EPPlus

        public ExcelPackage EscribeEnRangoDeCeldas(ExcelPackage excel)
        {
            var hojaActual = excel.Workbook.Worksheets["hoja1"];
            hojaActual = excel.Workbook.Worksheets[1];

            hojaActual.Cells["A1"].Value = "Esto funciona!";
            var rangoDeCeldas = hojaActual.Cells["C1:C3"];
            rangoDeCeldas = hojaActual.Cells["$C$1:$C$3"];

            return excel;
        }

Para comprobar que el funcionamiento es correcto, he realizado dos test que lanzan la creación de la Excel, realizan el método de escribir y salvan.

INTEROP

[TestMethod]
        public void EscribeEnRangoDeCeldas_con_Interop()
        {
            var constructor = new ExcelConInterop();
            var excel = constructor.CreaNuevaExcelConInterop();

            constructor.EscribeEnRangoDeCeldas(excel);
            constructor.GuardaExcel(excel);

            Assert.IsNotNull(excel);
        }

EPPlus

[TestMethod]
        public void EscribeEnRangoDeCeldas_con_EPPlus()
        {
            var constructor = new ExcelConEPPlus();
            var excel = constructor.CreaNuevaExcelConEPPlus();

            constructor.EscribeEnRangoDeCeldas(excel);
            constructor.GuardaExcel(excel);

            Assert.IsNotNull(excel);
        }

Para no darle complejidad al código la forma de comprobar que ha escrito correctamente es abrir la Excel y revisar que sea así.

Introduciendo fórmulas

¿Qué sería de Excel si no fuera por las fórmulas? Sin duda no tendría sentido una hoja de cálculo sin poder introducir definiciones de operaciones. Así voy a introducir dos valores en dos celdas, y en una tercera inserto la formula que suma ambos valores.

Aquí ambas sintaxis son muy parecidas, siempre un poco más fea la de Interop. En cambio la obligación de tener un Excel completo instalado en la máquina donde se ejecuta el código – el mayor inconveniente de esta forma de trabajar, y más con servidores cloud en PaaS – es que permite hacer cosas que EPPlus no.

INTEROP

        public Excel.Application SumaDosCeldas(Excel.Application excel)
        {
            Excel.Worksheet hojaActual = (Excel.Worksheet) excel.Workbooks[1].Worksheets[1];

            hojaActual.Cells.get_Range("A5").Value2 = "150";
            hojaActual.Cells.get_Range("B5").Value2 = "100";
            hojaActual.Cells.get_Range("A10").Formula = "=SUM(A5:B5)";

            return excel;
        }

EPPlus

public ExcelPackage SumaDosCeldas(ExcelPackage excel)
        {
            var hojaActual = excel.Workbook.Worksheets["hoja1"];
            hojaActual = excel.Workbook.Worksheets[1];

            hojaActual.Cells["A5"].Value = 150;
            hojaActual.Cells["B5"].Value = 100;
            hojaActual.Cells["A10"].Formula = "=SUM(A5:B5)";

            return excel;
        }

La principal, para mi gusto, es que como EPPlus no incorpora un motor de cálculo, no puedo recuperar el valor del resultado de una celda que contiene una fórmula. Cosa que con Interop sí que puedo.

También me he encontrado con un problema irresoluble con hojas de datos grandes o muy grandes ya que EPPlus, simplemente, casca. Mientras Interop aguanta mucho más.

Por ello no he hecho test recuperando el resultado de la suma, si no meros lanzadores de los métodos.

INTEROP

 [TestMethod]
        public void SumaDosCeldas_con_Interop()
        {
            //** Interop deja recuperar el valor del resultado de la formula
            //** EEPlus, no.
            var constructor = new ExcelConInterop();
            var excel = constructor.CreaNuevaExcelConInterop();
            constructor.SumaDosCeldas(excel);
            constructor.GuardaExcel(excel);

            Assert.IsNotNull(excel);
        }

EPPlus

        [TestMethod]
        public void SumaDosCeldas_con_EPPlus()
        {
            var constructor = new ExcelConEPPlus();
            var excel = constructor.CreaNuevaExcelConEPPlus();
            constructor.SumaDosCeldas(excel);
            constructor.GuardaExcel(excel);

            Assert.IsNotNull(excel);
        }

Y para comprobar que la suma está bien y que resulta en 250, lo que hago es abrir los ficheros.

Ahora dejo para el siguiente capítulo, y posiblemente final, algo un poco más avanzado y que es el introducir una imagen y una gráfica en la hoja.

Espero que te sea de utilidad.

En GenbetaDev | EPPlus, o como tratar en C# con Excel en OpenXML y no morir con Interop.

Ver todos los comentarios en https://www.genbeta.com

VER 0 Comentario

Portada de Genbeta