sábado, febrero 26, 2011

Encriptar texto en vb.net con SHA512

SHA512 en un encriptador de cifrado irreversible que se encuentra en la libreria System.Security.Cryptography del .net.

Al ser de cifrado irreversible ya no se puede desencriptar. puede tener diferentes usos por ejemplo si queremos validar la contraseña de un usuario poder comparar la contraseña encriptada en la base de datos y la cadena encriptada de una caja de texto.

para usar SHA512 debemos importar las siguientes librerias


Imports System.Security.Cryptography
Imports System.Text


la siguiente funcion recibe la cabena y devuelve la cadena encriptada


Public Function EncriptarPwd(ByVal pwd As String) As String
Dim cifrador As New SHA512Managed
Dim contraseñaOriginal As Byte() = Encoding.ASCII.GetBytes(pwd)
Dim contraseñaCifrada As Byte() = cifrador.ComputeHash(contraseñaOriginal)
Dim textoContraseñaCifrada As String = Convert.ToBase64String(contraseñaCifrada)
Return textoContraseñaCifrada
End Function

Uso de SET NOCOUNT en SQLServer

En SQLServer se usa SET NOCOUNT para devolver o no la cantidad de filas afectadas por alguna sentencia o transacción ejecutada. SET NOCOUNT tiene dos estados:

SET NOCOUNT ON : Inhabilita la cuenta del número de filas afectadas por una instrucción Transact-SQL

SET NOCOUNT OFF: Habilita la cuenta del número de filas afectadas por una instrucción Transact-SQL





Cuando se usa SET NOCOUNT ON se mejora el rendimiento del servidor porque omite la devolución del numero de filas afectadas al cliente o terminal que ha realizado la consulta, pero hay casos en que es necesario obtener el número de registros afectados segun nuestra necesidad, por ello se recomienda analizar muy bien cuando usar SET NOCOUNT ON.

El siguiente ejemplo hace una consulta a la tabla customers de la base de datos Northwind usando los dos estados de SET NOCOUNT, se puede apreciar la diferencia.

Usando SET NOCOUNT ON



----- 1
GO
SET NOCOUNT ON

select * from dbo.customers
where country = 'France'
GO

Resultado:

Usando SET NOCOUNT OFF


---- 2
GO
SET NOCOUNT OFF

select * from dbo.customers
where country = 'France'
GO


Resultado:

viernes, febrero 25, 2011

Reporte con Crystal Report y Linq to Xml

En el post anterior hice un pequeño ejemplo de como guardar los datos de una tabla a un archivo Xml y luego leerlos y mostrarlos en un DataGridView.

Ahora voy a realizar un ejemplo de como mostrar un reporte de los datos contenidos en un archivo XML usando Linq. Para este ejemplo tengo un proyecto de formularios de windows llamado XmlExample, en el cual tengo un formulario diseñado como muestra la imagen.


En el proyecto XmlExample también debo crear la clase Categories.vb el cual se usará para diseñar el reporte.
Public Class Categories
Private _CategoryID As Integer = String.Empty
Private _CategoryName As String = String.Empty
Private _Description As String = String.Empty

Public Property CategoryID() As Integer
Get
Return _CategoryID
End Get
Set(ByVal value As Integer)
_CategoryID = value
End Set
End Property

Public Property CategoryName() As String
Get
Return _CategoryName
End Get
Set(ByVal value As String)
_CategoryName = value
End Set
End Property

Public Property Description() As String
Get
Return _Description
End Get
Set(ByVal value As String)
_Description = value
End Set
End Property
End Class

Agregamos un nuevo proyecto de CrystalReport




El Explorador de soluciones debe quedar asi:


En el Diseñador del reporte Seleccionamos el origen de datos.

Nos Situamos como muestra la imagen (nueva conexión de ADO.NET) y presionamos doble click.

Aparecerá una ventana donde debemos indicar que objeto deseamos usar, en nuestro caso debemos seleccionar la clase categories.vb que esta en el proyecto XmlExample y presionamos finalizar.

En el arbol de la derecha nos aparecera el orgien de datos con el cual procederemos a diseñar nuestro reporte.

En el proyecto XmlExample donde se encuentra el formulario de consulta agregamos la referencia del proyecto de CrystalReport que hemos agregado


En el formulario agregamos un boton para generar el reporte y un crystalreportView. En el boton ingresamos el siguiente código:
Dim lCategoriaXml As XDocument = XDocument.Load(txtUrl.Text)
Dim lCategorias = From lcategoria In lCategoriaXml.Descendants("Categoria") _
Select CategoryID = lcategoria.Element("CategoryID").Value, _
CategoryName = lcategoria.Element("CategoryName").Value, _
Description = lcategoria.Element("Description").Value

Dim crp As New XMLReport.RptCategories
crp.SetDataSource(lCategorias.ToList)
CrystalReportViewer1.ReportSource = crp
TabControl1.SelectedIndex = 1

Grabar Datos de una tabla a Xml y leer con Linq to XML

En algunos casos es necesario guardar los datos de una table en un archivos XML. El siguiente código muestra como grabar los datos de la tabla categories de la base de datos Northwind hacia un archivo XML en la unidad D:\




Using cn As New SqlClient.SqlConnection
cn.ConnectionString = "Data Source=xxxxx;Initial Catalog=northwind;user ID=sa;password=xxxxxxxx"
cn.Open()
Using cmd As New SqlClient.SqlCommand
cmd.CommandType = CommandType.Text
cmd.CommandText = RichTextBox1.Text
cmd.Connection = cn
Using dr As SqlClient.SqlDataReader = cmd.ExecuteReader
Dim lColumn01 As Integer = dr.GetOrdinal("CategoryID")
Dim lColumn02 As Integer = dr.GetOrdinal("CategoryName")
Dim lColumn03 As Integer = dr.GetOrdinal("Description")
Dim lCount As Integer = dr.FieldCount - 1
Dim Values(lCount) As Object
Dim lRead As Boolean = False
Dim lElement As XElement

While dr.Read
dr.GetValues(Values)
If lRead = False Then
lElement = New XElement("Categorias", _
New XElement("Categoria", New XElement("CategoryID", Values(lColumn01)), _
New XElement("CategoryName", Values(lColumn02)), _
New XElement("Description", Values(lColumn03))))
Else
lElement.Add(New XElement("Categorias", _
New XElement("Categoria", New XElement("CategoryID", Values(lColumn01)), _
New XElement("CategoryName", Values(lColumn02)), _
New XElement("Description", Values(lColumn03)))))
End If
lRead = True
End While
lElement.Save(txtUrl.Text)
MsgBox("Datos Grabados en " & txtUrl.Text)
End Using
End Using
End Using


para leer los datos del archivo XML he usado Linq to XML para mostrar los datos en el datagridview


Dim lCategoriaXml As XDocument = XDocument.Load(txtUrl.Text)
Dim lCategorias = From lcategoria In lCategoriaXml.Descendants("Categoria") _
Select CategoryID = lcategoria.Element("CategoryID").Value, _
CategoryName = lcategoria.Element("CategoryName").Value, _
Description = lcategoria.Element("Description").Value

DataGridView1.DataSource = lCategorias.ToList




puedes descargar el código del ejemplo aquí.

jueves, febrero 24, 2011

Extraer esquemas de tablas con datatable


Tal vez en alguna oportunidad se habran topado con la necesidad de extraer el esquema de una tabla (sin importar la base de datos), es decir obtener el nombre de todas sus columnas, tipo de dato, si acepto nulos, etc. . Net ofrece la posibilidad de extraer el esquema de una tabla a travez de Datatables.

Un Datatable es un objeto Ado.Net, el cual te permite representar cualquier tabla de una base de datos en modo local, donde tu puedes manipularlo a tu gusto sin que la tabla real de la base de datos se vea afectado, almenos que tu indiques que deas grabar los cambios realizados.

Este ejemplo lo voy a realizar con la tabla categories de la base de datos Northwind el cual tiene la siguiente estructura:


Ahora para obtener la estructura de la tabla categories procedemos a ejecutar la siguiente sentencia:

El resultado es:

sábado, febrero 19, 2011

Snippely, util organizador de códigos

Snippely es un útil herramienta libre que te permite organizar tus códigos de manera sencilla, ahora ya no tendrás que buscar en tus archivos aquel código que no recuerdas.

Esta herramienta es util para los desarrolladores de software, y lo puedes descargar aqui




viernes, febrero 11, 2011

XMind: Herramienta libre para diseñar diagramas

Hace tiempo vengo utilizando esta util herramienta con la cual se pueden crear diagramas, mapas conceptuales, etc.

Esta muy bueno y lo recomiendo porque a mi me ha servido.


Lo pueden descargar de la siguiente dirección: http://www.xmind.net/

jueves, febrero 10, 2011

Tablas temporales en Sql Server 2000: Optimizar su uso

Siguiendo con el post sobre las tablas temporales en SQL Server 2000 voy a dar una serie de posibles mejoras que se pueden aplicar para conseguir un mejor desempeño.

  1. Es mejor crear la tabla temporal con comandos DDL y luego insertar los datos, por ejemplo:


CREATE TABLE #TABLA1
( CODIGO INT,
NOMBRE VARCHAR(20)
)

insert into #TABLA1(CODIGO,NOMBRE) values('1','LACTEOS')
insert into #TABLA1(CODIGO,NOMBRE) values('2','BEBIDAS')


y evitar crearlo en una instrucción SELECT INTO:




SELECT categoryID,CategoryName
INTO #TABLA1
FROM CATEGORIES


Debido a que en la ultima opción los bloqueos hacia los objetos del sistema duran mas tiempo.

2. Evitar el uso de SELECT * FROM, en su lugar se debe extraer las columnas que se necesitan.

3. Insertar solo la información necesaria.

4. Borrar la tabla temporal al finalizar el conjunto de sentencias o el procedimiento almacenado. Las tablas temporales se destruyen cuando se termina la conexión pero no tendría sentido dejarlas ocupar recursos del servidor si ya no se la va a usar.

5. Cuando la cantidad de datos que maneja una tabla temporal es grande se recomienda crearle un indice para que ayude a recuperar la información.

6. Colocar la base de datos tempdb en un disco dedicado solo para esta función aumentará el rendimiento global del sistema si se hace un uso intensivo de tablas temporales.

7. No crear tablas temporales dentro de transacciones y trigger porque la concurrencia a la base de datos se verá afectada.

Diagrama de Joins en SQL Server

Aqui dejo un útil diagrama que ayuda a comprender el funcionamiento de los Joins en Sql Server.


martes, febrero 08, 2011

Tablas temporales en Sql Server 2000: alternativas recomendadas

En ocasiones el resultado que deseamos obtener de una tabla o un conjunto de tablas a travez de solo una sentencia de select resulta complicado e inviable porque los datos tienen que ser tratados y procesados a fin de mostrar la información de acuerdo a nuestra necesidad, por lo cual es inevitable el uso de tablas temporales dentro de los procedimientos almacenados.

Pero la solución en base a tablas temporales resulta perjudicial para el desempeño del servidor de base de datos por eso se recomienda no usarlas.

¿Y porque no usar tablas temporales? Las razones las menciono a continuación:

  1. las tablas temporales se crean en el tempdb y al crearlas se producen bloqueos sobre esta base de datos como por ejemplo en las tablas sysobjects y sysindexes. Los bloqueos sobre el la tempdb afectan a todo el servidor.
  2. Es necesario que se escriba sobre el disco.
  3. Al insertar datos de nuevo se produce escritura al disco (Los accesos al disco suelen ser los cuellos de botella del sistema).
  4. Al leer datos de nuevo hay que rrecurrir al disco
  5. Al borrar la tabla de nuevo hay que adquirir bloqueos sobre la base de datos tempdb y realizar operaciones en disco.
  6. Si la consulta dentro de un procedimiento almacenado tiene una tabla temporal difícilmente se reutilizará el plan de ejecución.
Siempre la mejor solución es obtener los datos a travez de una sola consulta.

Siempre es bueno saber en que momento usar tablas temporales, para ello debemos conocer bien su funcionamiento y limitaciones, tambien es bueno conocer las posibles alternativas y revisar el plan de ejecución a fin de obtener la consulta mas óptima.

Con SQL Server 2000 podemos usar variables de tablas. Las variables de tablas tienen una serie de ventajas frente a tablas temporales:

  1. Solo existe en el ámbito en que fue definido.
  2. Producen menos recompilación de procedimientos almacenados.
  3. No necesitan de bloqueos ni de tantos recursos.
  4. se crean en memoria lo cual produce menos overhead que la tabla temporal
Pero también tiene una serie de inconvenientes:

  1. Una vez declara no se puede modificar la estructura de la tabla. Por ejemplo si dentro de procedimiento almacenado hemos definido una variable de tabla con los campos código y nombre y luego de declararla queremos alterar la tabla para agregar la columna importe no nos va a permitir.
  2. Los indices tienen que ser agrupados
  3. No se pueden usar en una sola sentencia Insert into o select into. Primero se deve declarar la variable de tabla y luego insertar los datos correspondientes.
  4. No se puede usar funciones en las restricciones.

Para finalizar, viendo las ventajas y desventajas es mejor usar variables de tablas frente a tablas temporales, pero en el caso en que se procesen grandes cantidades de datos y se necesite la creación de indices es mejor una tabla temporal.

Se debe evaluar que opción es la mas recomendable para nuestro procedimiento almacenado, en el siguiente post sobre tablas temporales colocare algunas formas de optimar su uso.


domingo, febrero 06, 2011

Vb.Net: copiar el contenido de una carpeta a otra

Cuando recien estaba iniciando en el mundo de .net un realice un sencillo programa que fuese capaz de copiar el contenido de una carpeta hacia otra cada 20 segundos.

El programa lo hice en visual studio 2003, pero he realizado algunos cambios como por ejemplo la posibilidad de elegir el tiempo en que se copiaran los archivos y ademas lo he migrado a visual studio 2010.
Aquí muestro una imagen del programa.




Para descargar el ejemplo presione aqui

viernes, febrero 04, 2011

DatagridviewComboBoxColumn: celdas con items irrepetibles por fila

Ahora voy a presentar un datagridview con una columna de tipo DatagridViewComboboxColumn donde cada item seleccionado en la columna tipo combobox no se vuelva a repetir en ninguna otra fila.

Habeces es necesario hacer validaciones de este tipo, por ejemplo si en un datagridview mostramos el nombre de un trabajador donde se le debe de asignar los conceptos de pago ya sea Sueldo Básico, Horas Extras, Movilidad, etc, pero donde cada concepto sea único por fila, es decir que un trabajador no podrá tener 2 importes por concepto de sueldos basicos o 3 importes por concepto de horas extras.

Para empezar creamos la clase personal.vb


Public Class Personal
Private lcod_personal As String
Private lnom_personal As String
Private lcod_concepto As String
Private limp_concepto As Decimal


Public Property cod_personal() As String
Get
Return lcod_personal
End Get
Set(ByVal value As String)
lcod_personal = value
End Set
End Property
Public Property nom_personal() As String
Get
Return lnom_personal
End Get
Set(ByVal value As String)
lnom_personal = value
End Set
End Property
Public Property cod_concepto() As String
Get
Return lcod_concepto
End Get
Set(ByVal value As String)
lcod_concepto = value
End Set
End Property

Public Property imp_concepto() As Decimal
Get
Return limp_concepto
End Get
Set(ByVal value As Decimal)
limp_concepto = value
End Set
End Property


End Class



luego creamos una clase ListPersonal.vb que herede de una lista de personal

Public Class ListPersonal
Inherits List(Of Personal)
Sub New()

End Sub

Sub AddPersonal(ByVal lcod_personal As String, _
ByVal lnom_personal As String, _
ByVal lcod_concepto As String, _
ByVal limp_concepto As Decimal)

Dim lPersonal As New Personal
lPersonal.cod_personal = lcod_personal
lPersonal.nom_personal = lnom_personal
lPersonal.cod_concepto = lcod_concepto
lPersonal.imp_concepto = limp_concepto
Me.Add(lPersonal)
End Sub

End Class


Ahora debemos crear la clase Conceptos.vb

Public Class Conceptos
Private lcod_concepto As String
Private lnom_concepto As String

Public Property cod_concepto() As String
Get
Return lcod_concepto
End Get
Set(ByVal value As String)
lcod_concepto = value
End Set
End Property

Public Property nom_concepto() As String
Get
Return lnom_concepto
End Get
Set(ByVal value As String)
lnom_concepto = value
End Set
End Property
End Class


ademas como el caso anterior creamos la clase ListConcepto.vb que herede de una lista de Conceptos

Public Class ListConcepto
Inherits List(Of Conceptos)

Sub New()
End Sub

Sub AddConcepto(ByVal Codigo As String, ByVal Nombre As String)
Dim lconcepto As New Conceptos
lconcepto.cod_concepto = Codigo
lconcepto.nom_concepto = Nombre
Me.Add(lconcepto)
End Sub

End Class



por ultimo un formulario con un datagridview y dos botones (un boton para eliminar filas y el otro para agregar una nueva fila)


Public Class Form1
Dim lListPersonal As New ListPersonal
Dim lListConcepto As New ListConcepto
Dim lBinding As New BindingSource

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

'Creamos la Estructura del datagridview
Dim ColumnCodigo As New DataGridViewTextBoxColumn
ColumnCodigo.Name = "Código"
ColumnCodigo.HeaderText = "Codigo"
ColumnCodigo.DataPropertyName = "cod_personal"
ColumnCodigo.Width = 80
Me.DataGridView1.Columns.Add(ColumnCodigo)

Dim ColumnNombre As New DataGridViewTextBoxColumn
ColumnNombre.Name = "Nombre"
ColumnNombre.HeaderText = "Apellidos y nombre"
ColumnNombre.DataPropertyName = "nom_personal"
ColumnNombre.Width = 200
Me.DataGridView1.Columns.Add(ColumnNombre)

Dim ColumnConcepto As New DataGridViewComboBoxColumn
ColumnConcepto.Name = "Concepto"
ColumnConcepto.HeaderText = "Concepto pago"
ColumnConcepto.DataPropertyName = "cod_concepto"
ColumnConcepto.Width = 140
Me.DataGridView1.Columns.Add(ColumnConcepto)

Dim ColumnImporte As New DataGridViewTextBoxColumn
ColumnImporte.Name = "Concepto"
ColumnImporte.HeaderText = "Importe S/."
ColumnImporte.DataPropertyName = "imp_concepto"
Me.DataGridView1.Columns.Add(ColumnImporte)

Me.DataGridView1.AutoGenerateColumns = False

'Agregamos los conceptos de pago y los asignamos a la columna tipo combobox
lListConcepto.AddConcepto("", "(SELECCIONE)")
lListConcepto.AddConcepto("01", "SUELDO BÁSICO")
lListConcepto.AddConcepto("02", "ASIGNACION FAMILIAR")
lListConcepto.AddConcepto("03", "HORAS EXTRAS")
lListConcepto.AddConcepto("04", "GRATIFICACION")
lListConcepto.AddConcepto("05", "MOVILIDAD")
lListConcepto.AddConcepto("06", "REFRIGERIO")

ColumnConcepto.DataSource = lListConcepto
ColumnConcepto.DisplayMember = "nom_concepto"
ColumnConcepto.ValueMember = "cod_concepto"

'Cargamos algunos trabajadores


lListPersonal.AddPersonal("XY001", "PEREZ RODRIGUEZ, JUAN", "", 0)
lBinding.DataSource = lListPersonal
Me.DataGridView1.DataSource = lBinding

End Sub


Private Sub btnAgregar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAgregar.Click
Dim lInfo As New Personal
lInfo.cod_personal = "XY001"
lInfo.nom_personal = "PEREZ RODRIGUEZ, JUAN"
lInfo.cod_concepto = ""
lInfo.imp_concepto = 0
lBinding.Add(lInfo)

End Sub


Private Sub DataGridView1_CurrentCellChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles DataGridView1.CurrentCellChanged
If DataGridView1.CurrentRow Is Nothing Then Exit Sub
If DataGridView1.CurrentRow.Cells("Concepto").Value = String.Empty Then
Me.DataGridView1.CurrentRow.ErrorText = "Debe seleccionar un concepto válido"
Else
Me.DataGridView1.CurrentRow.ErrorText = String.Empty
End If
End Sub

Private Sub DataGridView1_CellBeginEdit(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellCancelEventArgs) Handles DataGridView1.CellBeginEdit
Dim lStrColumn As String = Me.DataGridView1.Columns(e.ColumnIndex).Name

'Solo es váido cuando la columna del datagridview corresponde a conceptos
If lStrColumn = "Concepto" Then


Dim lConceptosAsignados As New List(Of String)
Dim lConceptosNoAsignados As New ListConcepto

'PASO 1
'Obtenemos los conceptos que ya fueron asignados al personal

'Recorremos las filas del datagridview
For Each lInfo As DataGridViewRow In Me.DataGridView1.Rows
'Entraran a la condicion todas las filas excepto la fila que esta
'en edicion
If e.RowIndex <> lInfo.Index Then
'Obtenemos la entidad por fila
Dim lBoundItem As Personal = lInfo.DataBoundItem
If lBoundItem Is Nothing Then
Continue For
End If
'Agregamos a la lista de conceptos asignados
lConceptosAsignados.Add(lBoundItem.cod_concepto)
End If
Next

'PASO 2
'En la Celda del DataGridViewComboboxcolum que esta en edicion
'mostramos aquellos conceptos que aun no han sido asignados

Dim lBoolAsignado As Boolean = False
For Each info As Conceptos In lListConcepto
lBoolAsignado = False
'Verificamos si el concepto ha sido asignado en alguna fila
For Each Value As String In lConceptosAsignados
If info.cod_concepto = Value Then
lBoolAsignado = True
Exit For
End If
Next
'Si el concepto aun no ha sido asignado
If lBoolAsignado = False Then
lConceptosNoAsignados.Add(info)
End If
Next

'A la celda del DatagridViewComboBoxcolumn en edicion indicamos
'que solo se muestren los items que aun no han sido asignados
Dim dgvColumn As New DataGridViewComboBoxCell
dgvColumn.DataSource = lConceptosNoAsignados
dgvColumn.ValueMember = "cod_concepto"
dgvColumn.DisplayMember = "nom_concepto"
Me.DataGridView1.Item(lStrColumn, e.RowIndex) = dgvColumn
End If
End Sub

Private Sub btnEliminar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnEliminar.Click
If Not (DataGridView1.CurrentRow Is Nothing) Then
lBinding.RemoveCurrent()
End If
End Sub
End Class


En el evento CellBeginEdit del datagridview es donde se van a filtrar solo aquellos conceptos que aun no han sido seleccionados y se mostraran el la celda en edición.

Puedes descargar el código del ejemplo en la siguiente dirección:
http://cid-ad089621e4982823.office.live.com/self.aspx/Ejemplos%20.Net/DataGridViewComboBoxColumnItems.rar

jueves, febrero 03, 2011

Consultas dinámicas en Sqlserver 2000

Hace algún tiempo atrás tuve que implementar en la aplicación que estuve desarrollando, consultas configurables para que el usuario puede filtrar la información de acuerdo a su necesidad, pongamos como ejemplo a la base de datos Northwind y la tabla Products, la aplicación debía consultar por cualquier columna que el usuario seleccione y filtrar la información según las coincidencias encontradas.

A mi no me pareció complejo hacer algo asi, porque solo debía armar la consulta en la aplicación y ejecutarla, pero luego me di cuenta que hacerlo de esta manera tenia dos desventajas:

- Al armar las consultas en la aplicación, esta se convertía en poco escalable y muy estática porque cualquier cambio o actualización que quisiese hacer tenia que modificar el código del programa y crear otro instalador o en todo caso reemplazar la dll donde se ha realizado el cambio.

- Las consultas que se envían desde la aplicación consumen mas recursos del servidor porque sql server tiene que cargar en memoria la sentencia a ejecutar, luego analizarla, compilarla y por ultimo ejecutarla, ocasionando que el servidor consuma mas recursos.

Los procedimientos almacenados están pre compilados en el servidor, por lo cual ejecutarlos tiene un menor costo para el servidor.

Para no armar y concatenar la sentencia en la aplicación debía ser capaz de armar la consulta en el servidor a travez de un procedimiento almacenado.

Para mi satisfacción encontré que desde la versión Sql Server 2000 existe el comando sp_executesql, capaz de ejecutar cualquier tipo de consulta que se le especifique.

El siguiente ejemplo usa sp_executesql y consiste en crear un procedimiento almacenado que consulte a la tabla Products sujeto a la condición que se envia al procedimiento.



GO
drop function [fGetCondicion]
GO
create function [dbo].[fGetCondicion]
(
@Condicion varchar(20),
@value varchar(100)
)
RETURNS VARCHAR(20)
AS
-- =============================================
-- Author: Milton Baltazar Valenzuela
-- Description: Obtiene el tipo de condicion y el valor
-- de la condicion para devolver la condicion
-- en el formato requerido
-- =============================================
BEGIN
RETURN (
SELECT
CASE @Condicion
WHEN 'like' THEN @Condicion + '''%' + @value + '%'''
WHEN '=' THEN @Condicion + ' ' + @value
WHEN '<>' THEN @Condicion + ' ' + @value
WHEN '>' THEN @Condicion + ' ' + @value
WHEN '<' THEN @Condicion + ' ' + @value ELSE null END) END GO GO drop procedure usp_get_products GO create procedure usp_get_products ( @productID int ,@ProductName varchar(40) ,@CategoryID int ,@UnitPrice money ,@Condicion varchar(100) ) AS BEGIN Declare @ls_query nvarchar(4000) Declare @li_error int Declare @ls_error varchar(1000) set @ls_query='select productID, ProductName, CategoryID, UnitPrice from dbo.Products' if @productID is not null begin set @ls_query = @ls_query + ' where productID ' + dbo.fGetCondicion(@Condicion,@productID) end else if @ProductName is not null begin set @ls_query = @ls_query + ' where ProductName ' + dbo.fGetCondicion(@Condicion,@ProductName) end else if @CategoryID is not null begin set @ls_query = @ls_query + ' where CategoryID ' + dbo.fGetCondicion(@Condicion,@CategoryID) end exec @li_error = sp_executesql @ls_query,N' @productID int ,@ProductName varchar(40) ,@CategoryID int ,@Condicion varchar(100)' ,@productID ,@ProductName ,@CategoryID ,@Condicion ; if @li_error <> 0
begin
set @ls_error = 'Error en la consulta. '
RaisError(@ls_error,16,1)
end
END
GO


----------------------------------------------------
--ejecutamos el procedimiento almacenado
----------------------------------------------------

exec usp_get_products 10,null,null,null,'='

exec usp_get_products null,'',null,null,'like'

exec usp_get_products null,'Chai',null,null,'Like'


GO


En resumen he creado un procedimiento almacenado que me permita consultar
cualquier campo de acuerdo a la condición que le envío, es cierto que al código no esta al 100% perfeccionado pero solo quiero dar la idea de que se puede hacer con sp_executesql.

DatagridView con origen de datos Listas Genericas y columnas Ordenables

Cuando se usa un datatable con origen de datos en un DatagridView por defecto queda la posibilidad de ordenar de manera ascendente o descendente los datos haciendo click en los encabezados de la columnas.

Pero cuando se usa listas genéricas como origen de datos para un Datagridview la posibilidad de ordenación queda deshabilitado.

para solucionar este problema se debe crear una clase SortableBindingList.vb que herede de BindingList


Imports System
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Diagnostics
Imports System.Linq
Imports System.Linq.Expressions
Imports System.Text
Imports System.Reflection

Public Class SortableBindingList(Of T)
Inherits BindingList(Of T)
Private _isSorted As Boolean
Private _sortDirection As ListSortDirection
Private _sortProperty As PropertyDescriptor

'This override shows the binded object, that our list supports sorting
Protected Overrides ReadOnly Property SupportsSortingCore() As Boolean
Get
Return True
End Get
End Property

'And that it can sort bi-directional
Protected Overrides ReadOnly Property SortDirectionCore() As ListSortDirection
Get
Return _sortDirection
End Get
End Property

'And that it can sort by T typed object's properties
Protected Overloads Overrides ReadOnly Property SortPropertyCore() As PropertyDescriptor

Get
Return _sortProperty
End Get
End Property

'This is the method, what gets called when the sort event occurs in the bound object
Protected Overloads Overrides Sub ApplySortCore(ByVal prop As PropertyDescriptor, ByVal direction As ListSortDirection)
Dim items As List(Of T) = TryCast(Me.Items, List(Of T))

If items IsNot Nothing Then
Dim pc As New PropertyComparer(Of T)(prop.Name, direction)
items.Sort(pc)
_isSorted = True
_sortDirection = direction
_sortProperty = prop
Else
_isSorted = False
End If
OnListChanged(New ListChangedEventArgs(ListChangedType.Reset, -1))
End Sub

'This shows if our list is already sorted or not
Protected Overloads Overrides ReadOnly Property IsSortedCore() As Boolean
Get
Return _isSorted
End Get
End Property

'Removing the sort
Protected Overrides Sub RemoveSortCore()
_isSorted = False
End Sub

'Sub New(ByVal list As ICollection(Of T))
' MyBase.New(list)
'End Sub
End Class

Public Class PropertyComparer(Of T)
Implements IComparer(Of T)
Private _property As PropertyInfo
Private _sortDirection As ListSortDirection

Public Sub New(ByVal sortProperty As String, ByVal sortDirection As ListSortDirection)
_property = GetType(T).GetProperty(sortProperty)
Me._sortDirection = sortDirection
End Sub

Public Function Compare(ByVal x As T, ByVal y As T) As Integer Implements IComparer(Of T).Compare
Dim valueX As Object = _property.GetValue(x, Nothing)
Dim valueY As Object = _property.GetValue(y, Nothing)

If _sortDirection = ListSortDirection.Ascending Then Return Comparer.[Default].Compare(valueX, valueY)

Return Comparer.[Default].Compare(valueY, valueX)
End Function
End Class



SortableBindingList reemplazará a List(Of ) o a cualquier tipo de coleccion que use. Como este ejemplo lo hago de la base de datos Northwind y la tabla Categories tengo que crear una clase con las columnas que deseo consultar.

procedemos a crear la clase Categories.vb


Public Class Categories
Private lCategoryId As String = String.Empty
Private lCategoryName As String = String.Empty
Private lDescription As String = String.Empty

Public Property CategoryId() As String
Get
Return lCategoryId
End Get
Set(ByVal value As String)
lCategoryId = value
End Set
End Property

Public Property CategoriName() As String
Get
Return lCategoryName
End Get
Set(ByVal value As String)
lCategoryName = value
End Set
End Property

Public Property Description() As String
Get
Return lDescription
End Get
Set(ByVal value As String)
lDescription = value
End Set
End Property

End Class



ahora voy a crear una clase ListCategories.vb que herede de SortableBindingList


Public Class ListCategories
Inherits SortableBindingList(Of Categories)

Sub New()
End Sub

End Class



ahora cuando quiera usar una colección de la clase categories.vb solo tengo que instanciar de la clase ListCategories y ya no de List(Of)

ejmplo:

Dim List as New ListCategories

o

Dim List as New SortableBindingList(Of categories)

lo cual reemplazaria a:

Dim List as New List(Of categories)

Ahora creamos un formulario con un datagridview y consultamos de la base de datos Northwind y la tabla Categories y colocamos el siguiente código en el load del formulario.


Dim lList As New ListCategories

Using cn As New SqlConnection("Data Source=XXXX;Initial Catalog=BD;User ID=sa;Password=xyzxyz")
Using cmd As New SqlCommand
cmd.CommandType = CommandType.Text
cmd.CommandText = "select categoryId, categoryName, Description from Categories"
cmd.Connection = cn
cn.Open()
Using dr As SqlDataReader = cmd.ExecuteReader
If dr.HasRows = True Then
Dim lColumn0 As Integer = dr.GetOrdinal("categoryId")
Dim lColumn1 As Integer = dr.GetOrdinal("categoryName")
Dim lColumn2 As Integer = dr.GetOrdinal("Description")
Dim lCount As Integer = dr.FieldCount - 1
Dim Values(lCount) As Object
Dim lEntidad As Categories
While dr.Read
dr.GetValues(Values)
lEntidad = New Categories
lEntidad.CategoryId = Values(lColumn0)
lEntidad.CategoriName = Values(lColumn1)
lEntidad.Description = Values(lColumn2)
lList.Add(lEntidad)
End While
End If
End Using
cn.Close()
End Using
End Using

DataGridView1.DataSource = lList


ahora ejecutamos y podemos visualizar que el datagridview se carga teniendo asociado como origen de datos una lista generica de tipo SortableBindingList y tambien podemos apreciar que se puede ordenar las columnas haciendo click en ellas.

espero haya sido de utilidad, hasta la próxima.