3ENGINE

Programación y otros cachivaches

Archivo

Página 7/102

Tecnologia

Cómo generar archivos Excel en C# con Open XML


Office Open XML es un formato de archivo abierto y estándar cuyas extensiones más comunes son .docx, .xlsx y .pptx. Principalmente contiene datos en XML comprimidos en un .zip. .NET dispone de librerias capaces de trabajar con este formato y de esta manera prescindir de instalar Office y sus temidas Microsoft.Office.Interop.

Generar archivos Excel en C# con Open XML

Para la demo es necesario instalar el paquete DocumentFormat.OpenXml:

generar archivos Excel en C# con Open XML

O desde la consola de Nuget:

Install-Package DocumentFormat.OpenXml 

El código es el siguiente:

 
namespace SaveExcelApp
{
    public static class SaveExcel
    {
        
        public static void BuildExcel(DataTable dataTable, string ExcelPath)        
        {
            using (SpreadsheetDocument myWorkbook = 
                SpreadsheetDocument.Create(ExcelPath, 
                SpreadsheetDocumentType.Workbook))
            {
                // workbook Part
                WorkbookPart workbookPart = myWorkbook.AddWorkbookPart();
                var worksheetPart = workbookPart.AddNewPart();
                string relId = workbookPart.GetIdOfPart(worksheetPart);

                // file Version
                var fileVersion = new FileVersion { ApplicationName = "Microsoft Office Excel" };

                // sheets               
                var sheets = new Sheets();
                var sheet = new Sheet { Name = dataTable.TableName, SheetId = 1, Id = relId };
                sheets.Append(sheet);

                // data
                SheetData sheetData = new SheetData(CreateSheetData(dataTable));

                // add the parts to the workbook and save
                var workbook = new Workbook();
                workbook.Append(fileVersion);
                workbook.Append(sheets);
                var worksheet = new Worksheet();
                worksheet.Append(sheetData);
                worksheetPart.Worksheet = worksheet;
                worksheetPart.Worksheet.Save();
                myWorkbook.WorkbookPart.Workbook = workbook;
                myWorkbook.WorkbookPart.Workbook.Save();
                myWorkbook.Close();
            }
        }

        private static List CreateSheetData(DataTable dataTable)
        {
            List elements = new List();

            // row header
            var rowHeader = new Row();
            Cell[] cellsHeader = new Cell[dataTable.Columns.Count];
            for (int i = 0; i < dataTable.Columns.Count; i++)
            {
                cellsHeader[i] = new Cell();
                cellsHeader[i].DataType = CellValues.String;
                cellsHeader[i].CellValue = new CellValue(dataTable.Columns[i].ColumnName);
            }
            rowHeader.Append(cellsHeader);
            elements.Add(rowHeader); 
            
           // rows data
           foreach (DataRow rowDataTable in dataTable.Rows)
           {     
                var row = new Row();
                Cell[] cells = new Cell[dataTable.Columns.Count];

                for (int i = 0; i < dataTable.Columns.Count; i++)
                {
                    cells[i] = new Cell();
                    cells[i].DataType = CellValues.String;
                    cells[i].CellValue = new CellValue((string)rowDataTable[i]);
                }
                row.Append(cells); 
                elements.Add(row);            
            }                      
            return elements;
        }
    }
}

Para mi demo los datos que deseo guardar en el archivo Excel los tengo en un DataTable. Al método BuildExcel se le pasa el DataTable con los datos y la ruta completa del archivo Excel a generar. El método CreateSheetData se encarga de leer los datos del DataTable y generar las celdas. El nombre de la hoja Excel se obtiene del nombre de la tabla.

Se trata de un código sencillo pero que puede servir como punto de partida.

Y ahora una pequeña demo:

var demoTable = new DataTable("mi demo");
demoTable.Columns.Add("name");
demoTable.Columns.Add("surname");
demoTable.Columns.Add("favorite color");
demoTable.Rows.Add(new Object[] { "David", "Miro", "blue" });
demoTable.Rows.Add(new Object[] { "Pablo", "Iglesias", "yellow" });
demoTable.Rows.Add(new Object[] { "Ratoncito", "Perez", "green" });
SaveExcel.BuildExcel(demoTable, "demo.xlsx");

Y el archivo generado:

generar archivos Excel en C# con Open XML

Enlace a la demo: SaveExcel.zip




Tecnologia

ORA-00932: inconsistent datatypes: expected DATE got NUMBER


Si estas lanzando una query con Oracle Data Access y te da un error ORA-00932: inconsistent datatypes: expected DATE got NUMBER:

using (OracleConnection connection = new OracleConnection(connectionString))
{
    connection.Open();
    try
    {
        var sql = "UPDATE FILE SET CREATIONDATE = :CREATIONDATE WHERE ID = :ID";
        var cmd = new OracleCommand(sql, connection);
        cmd.Parameters.Add("ID", 5);
        cmd.Parameters.Add(new OracleParameter("IMPORTDATE", DateTime.Now));
        cmd.ExecuteNonQuery();
    }
    finally
    {
        connection.Close();
    }
}

Aunque parezca increible :-D, la razón mas probable es que el orden de los parámetros en la query no coincide con el orden en que se agregaron a la colección Parameters. Por lo tanto la solución al ejemplo anterior es cambiar el orden de inserción de los parámetros:

cmd.Parameters.Add(new OracleParameter("IMPORTDATE", DateTime.Now));
cmd.Parameters.Add("ID", 5);



Tecnologia

Campo autonumerico en Oracle


Oracle no dispone de un campo autonumérico. Para conseguir emular un campo autonumérico necesitaremos una secuencia y un trigger. A continuación los pasos.

1. Crear la tabla

CREATE TABLE NAMES
(
ID       NUMBER (10) NOT NULL,
NAME     NVARCHAR2 (200) NOT NULL
CONSTRAINT PK_NAMES PRIMARY KEY (ID)
)

2. Crear la secuencia

CREATE SEQUENCE NAMES_SEQ
START WITH 1
INCREMENT BY 1
CACHE 20;

3. Crear el trigger

CREATE OR REPLACE TRIGGER TRG_NAMES_SEQ BEFORE INSERT OR UPDATE ON NAMES
FOR EACH ROW
DECLARE
v_newVal NUMBER(12) := 0;
BEGIN
  IF INSERTING THEN
    SELECT CAFILELOG_SEQ.NEXTVAL INTO v_newVal FROM DUAL;
    :NEW.ID := v_newVal;
  END IF;
END;

Hasta aqui perfecto. Ahora puedes insertar un registro en la tabla NAMES sin preocuparte de asignarle un identificador. Ahora bien, si necesitas averiguar el identificar asignado utiliza NAMES_SEQ.CURRVAL:

INSERT INTO NAMES (NAME) VALUES ("My name");
SELECT NAMES_SEQ.CURRVAL FROM DUAL;

Con PL/SQL puedes averiguar el identificador mediante RETURNING INTO. Por ejemplo con .NET queda así:

static decimal InsertName(string name)
{
using (OracleConnection connection = new OracleConnection(connectionString))
{
decimal id = 0;
connection.Open();
OracleTransaction transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted);
try
{
OracleCommand cmd = new OracleCommand("INSERT INTO NAMES (NAME) VALUES (:name) RETURNING ID INTO :myId", connection);
cmd.Parameters.Add(new OracleParameter("name", name));
cmd.Parameters.Add(new OracleParameter("myId", OracleDbType.Decimal, ParameterDirection.Output));
cmd.ExecuteNonQuery();                            
id = ((OracleDecimal)(cmd.Parameters["myId"].Value)).Value;             
transaction.Commit();
}
catch (Exception e)
{
transaction.Rollback();
}
finally
{
connection.Close();
}
return id;
}
}

Con Entity Framework con Code First, esto se hace con la opción DatabaseGeneratedOption.Identity de la data annotation DatabaseGenerated:

[Table("FILE", Schema = "MI")]
public class File
{
    [Key]
    [Column("ID")]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public decimal Id { get; set; }

    [Column("FILENAME")]
    public string Filename {get; set;}

    public virtual ICollection Lines { get; set; }
}



Tecnologia

Cómo crear un servicio Windows .NET autoinstalable


Cuando llega la hora de instalar un servicio Windows en .NET, en mucho sitios recomiendan instalar el servicio mediante una utilidad de la línea de comandos llamada installutil.exe

Comandos básicos para instalar y desinstalar un servicio con installutil.exe:

installutil.exe miservicio.exe
installutil.exe /u miservicio.exe

Una vez instalado podéis arrancar y parar el servicio mediante la utilidad net.exe:

net start miservicio.exe
net stop miservicio.exe

Crear un servicio Windows .NET autoinstalable

El problema de installutil.exe es que sólo la encontraras si instalas Visual Studio en el ordenador donde necesitas instalar el servicio. Una solución es crear un proyecto de InstallShield para crear un autoinstalable. Pero yo os cuento una manera mas sencilla de conseguir esto y ahorraros el sufrimiento de utilizar InstallShield 🙂

Antes de nada, el código incluye una directiva DEBUG que permite debugar el servicio como si se tratara de una aplicación de consola. Esto lo expliqué en Debug desde VS.NET de un servicio Windows.

Por último, recomiendo añadir un ProjectInstaller.cs a vuestro servicio tal como explica este enlace en el paso 5. Añadirlo nos permitirá entre otras cosas especificar el tipo de cuenta, el nombre y la descripción del servicio, el modo de arranque, etc.

Código de ejemplo:

using System.ServiceProcess;
using System.Configuration.Install;
using System.Reflection;
using System;
using System.Threading;

namespace MyService
{
    static class Program
    {
        /// 
        /// Punto de entrada principal para la aplicación.
        /// 
        static void Main(string[] args)
        {
            #if (!DEBUG)    
                if (System.Environment.UserInteractive)
                {
                    string parameter = string.Concat(args);
                    switch (parameter)
                    {
                        case "-i":
                        case "-install":
                            try
                            {
                                ManagedInstallerClass.InstallHelper(new string[] { Assembly.GetExecutingAssembly().Location });
                            }
                            catch
                            {
                            }
                            break;
                        case "-u":
                        case "-uninstall":
                            try
                            {
                                ManagedInstallerClass.InstallHelper(new string[] { "/u", Assembly.GetExecutingAssembly().Location });
                            }
                            catch
                            {
                            }
                            break;
                    }
                }
                else
                {
                    ServiceBase[] ServicesToRun;
                    ServicesToRun = new ServiceBase[] 
                    { 
                        new ServiceWatcher()
                    };
                    ServiceBase.Run(ServicesToRun);
                }

            #else
                // debug mode
                ServiceWatcher service = new ServiceWatcher();
                service.Init();
                Thread.Sleep(Timeout.Infinite);
            #endif
        }
    }
}

Básicamente la idea es que se puede tener un servicio autocontenido para instalar y desinstalar utilizando ManagedInstallerClass como se muestra el ejemplo.

De modo que si compilas el servicio en modo RELEASE para instalar el servicio:

miservicio.exe -install

y para desisntalarlo:

miservicio.exe -uninstall