domingo, 25 de agosto de 2013

Obtener el primer día del mes en curso con una función first_day() con PL/SQL en Oracle.

En PL/SQL existe la función LAST_DAY() con la que se obtiene el último día del mes en curso, entonces si tenemos que cumplir un requerimiento en donde necesitamos un campo o una variable con el último día del mes en curso simplemente ejecutamos esta función:


Ahora bien, si el requerimiento a cumplir se trata de obtener el primer día del mes en curso, no existe en PL/SQL Oracle una función predeterminada, por lo que para llegar a ese resultado basta con restarle un mes a la fecha en curso con la función ADD_MONTHS(), le aplicamos la función LAST_DAY() para obtener el último día del mes anterior y sumarle un día.


Aquí otra forma alterna de llegar al mismo resultado.



Descarga el código PL/SQL

lunes, 19 de agosto de 2013

ADO.NET Helper para Oracle (Update)

Todos los programas que utilizan las clases e interfaces del proveedor predeterminado de ADO.NET para Oracle que se encuentran en el ensamblado System.Data.Oraclient deben actualizarse por las clases e interfaces que se encuentran en el ensamblado Oracle.DataAccess.Client, que es el proveedor nativo de Oracle para ADO.NET, este ensamblado se descarga del siguiente enlace Oracle Data Provider for .NET


A continuación el código actualizado de las clases Helper de ADO.NET que publique en este post.

El código de la clase OracleDataBase


El código de la clase OracleDataBaseCommand

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using Oracle.DataAccess.Client;

namespace OracleHelper
{
    internal sealed class OracleDataBaseCommand
    {

internal static int Insert(string commandText, Dictionary parameters, 
System.Data.CommandType cmdtype)
{
            
int resp = 0;
try
{
    using (OracleConnection conn = OracleDataBase.GetInstance().GetConnection())
    {
        using (OracleCommand cmd = new OracleCommand(commandText, conn))
        {
            cmd.CommandType = cmdtype;
            if (parameters != null)
            {
                foreach (KeyValuePair pair in parameters)
                {
                    cmd.Parameters.Add(new OracleParameter(pair.Key, pair.Value));
                }
            }
            resp = cmd.ExecuteNonQuery();
        }
    }
}
catch (OracleException ex)
{
    Logger.LogWriteError(ex.Message);
    throw ex;
}
return resp;
}
internal static int Update(string commandText, Dictionary parameters, 
System.Data.CommandType cmdtype)
{
            
try
{
    return Insert(commandText, parameters, cmdtype);
}
catch (OracleException ex)
{
    Logger.LogWriteError(ex.Message);
    throw ex;
}
}
internal static OracleDataReader GetReader(string commandText, Dictionary parameters,
System.Data.CommandType cmdtype)
{
OracleDataReader reader = null;
try
{
    OracleConnection conn = OracleDataBase.GetInstance().GetConnection();
    using (OracleCommand cmd = new OracleCommand(commandText, conn)) {
        if (parameters != null)
        {
            foreach (KeyValuePair pair in parameters)
            {
                cmd.Parameters.Add(new OracleParameter(pair.Key, pair.Value));
            }
        }
        reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
    }
}
catch (Exception ex) {
    Logger.LogWriteError(ex.Message);
    throw ex;
}
return reader;
}

internal OracleDataReader GetParameterizedReader(string commandText, OracleParameter[] parameters, 
System.Data.CommandType cmdtype)
{
OracleDataReader reader = null;
OracleConnection conn = OracleDataBase.GetInstance().GetConnection();
using (OracleCommand cmd = new OracleCommand(commandText, conn))
{
    cmd.CommandType = cmdtype;
    if (parameters != null)
        cmd.Parameters.AddRange(parameters);
    reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
}
return reader;
}
}
}

Como ejemplo del uso del helper, tenemos el código de la clase CustomerDAC cuya funcionalidad es tener los métodos CRUD para una entidad Customer.


El código de la clase Customer


Para finalizar el código de una aplicación de consola que muestra el uso de la clase CustomerDAC.


Download el código fuente para Xamarin Studio o Visual Studio

miércoles, 14 de agosto de 2013

ADO.NET Helper para Oracle

Hace bastante tiempo que hice unas clases de ADO.NET para Oracle (un tipo helper de manera elemental). quizá no es la manera más optima de acceder a una base de datos, pero al menos en ambientes restringidos en donde no es posible instalar frameworks u otro proveedor de ADO.NET para Oracle que no sea el que viene predeterminado por .NET.

Aquí esta la clase para manejar la conexión, se llama OracleDataBase

using System;
using System.Data;
using System.Data.OracleClient;
using System.Configuration;

namespace Util.Data
{
internal sealed class OracleDataBase
    {
        static OracleConnection _conn = null;
        static OracleDataBase Instance = null;
        string ConnectionString = null;
        
        
        OracleDataBase()
        {
            
            try
            {
    //ConnectionString = "data source=MYSERVER;user id=MYUSER;password=MYPASSWORD";
    ConnectionString =ConfigurationManager.ConnectionStrings["My Oracle connstring"].ConnectionString;
            }
            catch (Exception e)
            {
                //Log the error
            }
        }
        static void CreateInstance()
        {
            if (Instance == null)
                Instance = new OracleDataBase();
        }
        internal static OracleDataBase GetInstance()
        {
            if (Instance == null)
                CreateInstance();
            return Instance;
        }

        internal OracleConnection GetConnection()
        {
            try
            {
                _conn = ConnectionString;
                _conn.Open();
            }
            catch (OracleException ex)
            {
                //Log the error
                throw ex;
            }
            return _conn;
        }
    }
}
Utilizo otra clase llamada OracleDataBaseCommand para auxiliarme con los comandos.
using System;
using System.Data;
using System.Data.OracleClient;

namespace Util.Data{

internal sealed class OracleDataBaseCommand
    {
        
internal static int Insert(string commandText, Dictionary parameters, 
System.Data.CommandType cmdtype)
        {
            
            int resp = 0;
            try
            {
                using (OracleConnection conn = OracleDataBase.GetInstance().GetConnection())
                {
                    using (OracleCommand cmd = new OracleCommand(commandText, conn))
                    {
                        cmd.CommandType = cmdtype;
                        if (parameters != null)
                        {
                            foreach (KeyValuePair pair in parameters)
                            {
                                cmd.Parameters.Add(new OracleParameter(pair.Key, pair.Value));
                            }
                        }
                        resp = cmd.ExecuteNonQuery();
                    }
                }
            }
            catch (OracleException ex)
            {
                //Log the error
                throw ex;
            }
            return resp;
        }
        internal static int Update(string commandText, Dictionary parameters, 
System.Data.CommandType cmdtype)
        {
            
            try
            {
                return Insert(commandText, parameters, cmdtype);
            }
            catch (OracleException ex)
            {
                //Log the error
                throw ex;
            }
        }
        internal static OracleDataReader GetReader(string commandText, 
                Dictionary parameters, System.Data.CommandType cmdtype)
        {
            OracleDataReader reader = null;
           
            try
            {
                OracleConnection conn = OracleDataBase.GetInstance().GetConnection();
                using (OracleCommand cmd = new OracleCommand(commandText, conn)) {
                    if (parameters != null)
                    {
                        foreach (KeyValuePair pair in parameters)
                        {
                            cmd.Parameters.Add(new OracleParameter(pair.Key, pair.Value));
                        }
                    }
                    reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
                }
            }
            catch (Exception ex) {
                //Log the error
            }
            return reader;
        }

        internal OracleDataReader GetParameterizedReader(string commandText, 
               OracleParameter[] parameters,System.Data.CommandType cmdtype)
        {
            OracleDataReader reader = null;
            OracleConnection conn = OracleDataBase.GetInstance().GetConnection();
            using (OracleCommand cmd = new OracleCommand(commandText, conn))
            {
                cmd.CommandType = cmdtype;
                if (parameters != null)
                    cmd.Parameters.AddRange(parameters);
                reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
            }
            return reader;
        }
    }
}

Más o menos su utilización dentro de una clase que sirva para persistir o extraer datos sería de la siguiente manera.

namespace Project.Data{

public class CustomerManager{

public bool Create(Customer c)
        {
            string commandText = "My store or my SQL command";
            bool resp = false;
            Dictionary parameters = new Dictionary();
            //my parameters, if my commandText is a store procedure
            parameters.Add("CustomerID", c.ID);
            resp = (OracleDataBaseCommand.Insert(commandText, parameters, 
                         CommandType.StoredProcedure) > 0 ? true : false);
            return resp;
        }

        public bool Update(Customer c)
        {
            string commandText = "My store or my SQL command";
            bool resp = false;
            Dictionary parameters = new Dictionary();
            //my parameters, if my commandText is a store procedure
            parameters.Add("CustomerID", c.ID);
            resp = (OracleDataBaseCommand.Update(commandText, parameters, 
                                CommandType.StoredProcedure) > 0 ? true : false);
            return resp;
        }

public Customer Retrieve(int id)
        {
            Customer resp = null;
            string commandText = "My store or my SQL command";
            Dictionary parameters = new Dictionary();
            //my parameters, if my commandText is a store procedure
            parameters.Add("CustomerID", id);
                    using (OracleDataReader reader = OracleDataBaseCommand.GetReader(commandText,
                      parameters,CommandType.Text))
                    {
                        while (reader.Read())
                        {
                            resp = new Customer();
                            resp.ID = reader.GetInt32(0);
                            resp.NAME= reader["Name"].ToString();
                        }
                    }
            return resp;
        }

}
}

viernes, 2 de agosto de 2013

Entendiendo Unboxing en C# con GTK#

Es la operación en donde un tipo por referencia se convierte a un tipo simple se denomina Unboxing, esta conversión al igual que el Boxing puede ser explícita o implícita según se requiera.

A diferencia del Boxing el Unboxing requiere muchos menos recursos ya que no requiere la reserva de memoria adicional porque solamente se obtiene un apuntador hacia el tipo simple contenido dentro del objeto.

Internamente el proceso de Unboxing lleva los siguientes pasos:

  1. Se comprueba si existe una referencia hacia un objeto creado mediante Boxing.
  2. Si esto es verdadero se obtiene un apuntador para el tipo simple contenido dentro del objeto.
  3. Se le copia el valor del objeto al tipo simple también se copian los campos del objeto del managed heap al stack.
  4. Si no existe una referencia al objeto se lanza una excepción del tipo NullReferenceException.
  5. Si el tipo simple a la que se le asigna el valor del objeto no tiene la suficiente longitud para almacenar el valor del objeto se lanza una excepción del tipo InvalidCastException.

Como ejemplo del uso de esta técnica el siguiente programa GTK# solicita dos números de doble precisión y determina cuál es el mayor de ellos.


using System;
using Gtk;

namespace TestBoxing
{
class GtkUnBoxingSample : Window
{
Entry txtNumber1 = new Entry();
Entry txtNumber2 = new Entry();
Button btnOk = new Button("Ok");
Label lblMsg = new Label(string.Empty);
Label lblError = new Label(string.Empty);

public GtkUnBoxingSample() : base("Unboxing sample")
{
 DeleteEvent += new DeleteEventHandler(ClosedWindowEvent);
 btnOk.Clicked += new EventHandler(HandleBtnOkClicked);
 this.BorderWidth = 6;
 var f = new Frame("Enter two numbers");
 this.Add(f);
 var vbox = new VBox(false,8);
 Table t = new Table(3,2,false);
 t.Attach(new Label("First number "),0,1,0,1);
 t.Attach(txtNumber1,1,2,0,1);
 t.Attach(new Label("Second number"),0,1,1,2);
 t.Attach(txtNumber2,1,2,1,2);
 vbox.PackStart(t,false,false,0);
 HButtonBox bb = new HButtonBox();
 bb.Add(btnOk);
 vbox.Add(bb);
 vbox.Add(lblMsg);
 vbox.Add(lblError);
 f.Add(vbox);
 ShowAll();
}

void HandleBtnOkClicked (object sender, EventArgs e)
{
 lblError.Text = string.Empty;
 try
 {
double x,y,temp;
x = Double.Parse (txtNumber1.Text);//Unboxing string to double
y = Convert.ToDouble(txtNumber2.Text);//Unboxing 
if(y > x)
{
 temp = x;
 x = y;
 y = temp;
}
lblMsg.Text = x.ToString() + " greater than " + y.ToString();//Boxing
}catch(FormatException ex){
lblMsg.Text = string.Empty;
lblError.Text = ex.Message;    
}
}

void ClosedWindowEvent(object o, DeleteEventArgs args){
 Application.Quit();
 
}
public static void Main(string[] args)
{
 Application.Init();
 new GtkUnBoxingSample();
 Application.Run();
}
}
}

Al ejecutar el programa se muestra la siguiente ventana:


Al ingresar ambos valores y pulsar el botón "Ok" el programa comparará ambos números.



En el metódo HandleBtnOkClicked se encuentra toda la funcionalidad, es aquí donde utilizamos la técnica de Unboxing

void HandleBtnOkClicked (object sender, EventArgs e)
{
 lblError.Text = string.Empty;
 try
 {
 double x,y,temp;
 x = Double.Parse (txtNumber1.Text);//Unboxing string to double
 y = Convert.ToDouble(txtNumber2.Text);//Unboxing 
 if(y > x)
 {
  temp = x;
  x = y;
  y = temp;
 }
 lblMsg.Text = x.ToString() + " greater than " + y.ToString();//Boxing
}catch(FormatException ex){
 lblMsg.Text = string.Empty;
 lblError.Text = ex.Message;    
}
}

En la siguientes líneas, de un tipo por referencia como la propiedad Text del Widget Entry, lo convertimos a un tipo por valor:

 double x,y,temp;
 x = Double.Parse (txtNumber1.Text);//Unboxing string to double
 y = Convert.ToDouble(txtNumber2.Text);//Unboxing 

Es una buena práctica utilizar el código que realiza el Unboxing dentro de un bloque try/catch, esto para manejar la excepción en caso de que el valor del tipo por referencia (objeto) no sea del mismo tipo que el tipo simple que lo almacenará.


Download el código fuente para Xamarin Studio o Visual Studio

jueves, 1 de agosto de 2013

Entendiendo Boxing en C# con GTK#

Todos los tipos de datos en C# heredan de la clase base Object, tanto los tipos de datos por valor (ejem: primitivos) como los tipos por referencia (ejem:clases), esta herencia permite convertir un tipo de dato por valor a uno por referencia y a la inversa, a estas operaciones se les conoce como Boxing y Unboxing.

Estas operaciones son importantes dentro del sistema de tipos de C# ya que unifican la forma de trabajar con ellos, puesto que será posible invocar a métodos comunes en cada uno de ellos sin importar su tipo.

El siguiente programa GTK# muestra estos conceptos, el programa emula a un sencillo administrador de cuenta bancaria solicita una cantidad para deposito o retiro y según el tipo de movimiento pone un signo de `+’ si es deposito y un signo de ‘-‘si es retiro.

El programa consta de una estructura MovementItem y de la clase GtkBoxingSample.

El código fuente de la estructura MovementItem.

using System;

namespace TestBoxing
{
public struct MovementItem
{
DateTime _date;
char _sign;
double _amount;
public DateTime Date {
 set{ _date = value;}
 get{ return _date;}
}
public char Sign {
 set{ _sign = value;}
 get{ return _sign;}
}
public double Amount {
 set{ _amount = value;}
 get{ return _amount;}
} 
public MovementItem (DateTime date, 
                     char sign, 
                     double amount)
{
 this._date = date;
 this._sign = sign;
 this._amount = amount;
}
}
}

El código fuente de la clase GtkBoxingSample.

using System;
using Gtk;
using System.Collections;

namespace TestBoxing
{
class GtkBoxingSample : Window
{
Entry txtAmount = new Entry();
Entry txtBalance = new Entry("0");
Button btnDeposit = new Button("Deposit");
Button btnWithDraw = new Button("WithDraw");
TreeView grid = null;
ListStore _store = null;

public GtkBoxingSample () : base("Boxing Sample")
{
 DeleteEvent += new DeleteEventHandler(ClosedWindowEvent);
 btnDeposit.Clicked += new EventHandler(HandleDeposit);
 btnWithDraw.Clicked += new EventHandler(HandleWithDraw);
 this.BorderWidth = 6;
 var vbox = new VBox(false,8);
 var hbox = new HBox(false,6);
 hbox.Add(new Label("Amount: "));
 hbox.Add(txtAmount);
 HButtonBox hbb = new HButtonBox();
 hbb.Add(btnDeposit);
 hbb.Add(btnWithDraw);
 vbox.Add(hbox);
 vbox.Add(hbb);
 _store = new ListStore (typeof(string),typeof(string),typeof(string));
 ChangeBalance(DateTime.Now,'+',1000);
 grid = new TreeView(_store);
 string[] colnames = {"Date","Type","Amount"};
 for(int i = 0;i < colnames.Length;i++)
  grid.AppendColumn(new TreeViewColumn(colnames[i],new CellRendererText(),"text",i));
 HBox gridBox = new HBox();
 gridBox.Add(grid);
 HBox balanceBox = new HBox();
 balanceBox.Add(new Label("Balance"));
 txtBalance.IsEditable = false;
 balanceBox.Add(txtBalance);
 vbox.Add(gridBox);
 vbox.Add(balanceBox);
 this.Add(vbox);
 ShowAll();
}

void HandleDeposit (object sender,EventArgs e)
{
 double amount = Double.Parse (txtAmount.Text);
 ChangeBalance(DateTime.Now,'+',amount);
}

void HandleWithDraw (object sender,EventArgs e)
{
 double amount = Double.Parse(txtAmount.Text);
 ChangeBalance(DateTime.Now,'-',amount);
}



void ChangeBalance (DateTime date,char sign, double amount)
{
 double balance = 0.0;
 MovementItem item = new MovementItem (date, sign, amount);
 if(sign.Equals('+')) balance = Convert.ToDouble(txtBalance.Text) + amount; //unboxing
 if(sign.Equals('-')) balance = Convert.ToDouble(txtBalance.Text) - amount; //unboxing
 txtBalance.Text = Convert.ToString(balance); //boxing
   object objAmount = amount;
  _store.AppendValues (item.Date.ToString("dd/MM/yyyy"), //boxing date to string
                Convert.ToString(item.Sign), //boxing char to string
                   objAmount.ToString()); //boxing double to string
}

void ClosedWindowEvent(object o, DeleteEventArgs args){
 Application.Quit();
}

public static void Main (string[] args)
{
 Application.Init();
 new GtkBoxingSample();
 Application.Run();
}
}
}

Al ejecutar el programa se verá la siguiente ventana con un saldo inicial de 1000


Al ingresar un cantidad y dar click en el botón "Deposit", se mostrará el movimiento con un signo "+" sumando el monto al saldo.


Lo mismo sucederá con el botón "WithDraw", solo que se muestra el movimiento con un signo "-" restando el monto al saldo.


Se usan las técnicas de Boxing/Unboxing en el método ChangeBalance

void ChangeBalance (DateTime date,char sign, double amount)
{
double balance = 0.0;
MovementItem item = new MovementItem (date, sign, amount);
if(sign.Equals('+')) balance = Convert.ToDouble(txtBalance.Text) + amount; //unboxing
if(sign.Equals('-')) balance = Convert.ToDouble(txtBalance.Text) - amount; //unboxing
txtBalance.Text = Convert.ToString(balance); //boxing

_store.AppendValues (item.Date.ToString("dd/MM/yyyy"), //boxing date to string
                Convert.ToString(item.Sign), //boxing char to string
                item.Amount.ToString()); //boxing double to string
}

En .NET toda estructura o tipo simple es un tipo por valor y como la propiedad Text del Widget Entry admite únicamente el tipo String que es un tipo por referencia, por lo que para mostrar la cantidad en el campo de texto Balance, utilizamos el Boxing como en la siguiente línea:

txtBalance.Text = Convert.ToString(balance); //boxing

Después para agregar un item en el ListStore utilizamos el método AppendValues(), el cuál recibe como argumento un arreglo de objetos, nuevamente tenemos que utilizar el Boxing para cada que cada tipo simple de la estructura se convierta en un tipo por referencia, esto se hace con la siguiente línea:

_store.AppendValues (item.Date.ToString("dd/MM/yyyy"), //boxing date to string
                Convert.ToString(item.Sign), //boxing char to string
                item.Amount.ToString()); //boxing double to string

Internamente el proceso de Boxing lleva los siguientes pasos:

  1. Se reserva una cantidad de memoria adicional al tipo de variable para las operaciones adicionales que diferencian a aun objeto de un tipo simple.
  2. Se crea una variable de objeto temporal en donde se almacenará el valor de la variable.
  3. Se copia el valor de la variable al valor del objeto creado.
  4. Se regresa la dirección del objeto ya que en este momento el tipo por valor es un tipo por referencia.

El Boxing puede ser llevado a cabo de forma implícita por ejemplo:

item.Date.ToString("dd/MM/yyyy");
O de manera explícita, por ejemplo:
object objAmount = amount;
objAmount.ToString();

El proceso de Boxing es costoso ya que requiere de una memoria adicional que afecta al performance de la aplicación, además ocasiona que el código IL producido sea mucho mayor.

Download el código fuente para Xamarin Studio o Visual Studio