domingo, 29 de noviembre de 2009

C# – ADO.NET – Parte 4 – Actualización Información Ms Access

 

Introducción

Este articulo intenta brindar un ejemplo practico de como hacer uso de los objetos de ADO.NET para poder manipular la información de la base de datos, en este caso será de Ms Access.

Gran parte de la explicación se ha realizado en artículos previos

C# – ADO.NET – Parte 3 – Actualización Información Sql Server

que si bien tratan otra base de datos, se vera que salvo algunos pequeños detalles es prácticamente idéntico.

Es por eso que a medida que se vaya analizando el código del articulo es recomendable darle un vistazo al link de la Parte 3 del articulo, este contendrá detalles mas precisos sobre algunos aspectos utilizados.

 

1 – Creación nueva entidad (Insert)

En este código encontraran también el método Save() el cual será el encargado de determinar si es necesario insertar el registro o simplemente actualizarlos

public static ContactoEntity Save(ContactoEntity contacto)
{

    if (string.IsNullOrEmpty(contacto.Nombre))
        throw new BusinessException(Messages.MSG1002);

    if (string.IsNullOrEmpty(contacto.Apellido))
        throw new BusinessException(Messages.MSG1003);

    if (Exists(contacto))
        return Update(contacto);
    else
        return Insert(contacto);
}

Si se analiza el código veras que la funcionalidad que determina si existe la entidad es idéntica a como se programaría cuando se hace uso de Sql Server, el único cambio radica en que se utiliza los objeto de OleDb

private static bool Exists(ContactoEntity contacto)
{
    if (contacto == null)
        throw new BusinessException(Messages.ERR1001); 

    return Exists(contacto.IdContacto);
}

private static bool Exists(int Id)
{
    string sql = @"SELECT COUNT(*)
                      FROM Contacto
                      WHERE IdContacto = @Id";


    using (OleDbConnection conn = new OleDbConnection(ConfigurationManager.ConnectionStrings["default"].ToString()))
    {

        OleDbCommand command = new OleDbCommand(sql, conn);
        command.Parameters.AddWithValue("Id", Id);

        conn.Open();

        int count = Convert.ToInt32(command.ExecuteScalar());

        if (count == 0)
            return false;
        else
            return true;
        
    }
}

al igual que el ejemplo del link relacionado, se hace uso de ExecuteScalar, el cual devolverá simplemente la primer columna de la primer fila de la consulta.

LA funcionalidad que inserta tiene un pequeño detalles que hay que aclararlo ya que puede presentarse problemas en otras circunstancias.

Este se presenta al declarar el parámetro de fecha de nacimiento (líneas 28-30), se vera que se crea una variable del tipo OleDbParameter en donde se define el tipo de dato puntualmente.

Esto debe ser así ya que el método AddWithValue() no puede determinar para este tipo de dato cual es el correcto, cuando se le pasarle un objeto del tipo DataTime de .net, es por eso que hay que definirlo OleDbType.Date, y de esta forma funciona perfectamente.

 

private static ContactoEntity Insert(ContactoEntity contacto)
{
    string sql = @"INSERT INTO Contacto (IdContacto
                          ,Nombre
                          ,Apellido
                          ,FechaNacimiento
                          ,Localidad
                          ,Calle
                          ,Numero)
                      VALUES (@Id, 
                            @Nombre, 
                            @Apellido, 
                            @FechaNacimiento, 
                            @Localidad, 
                            @Calle, 
                            @Numero)";


    using (OleDbConnection conn = new OleDbConnection(ConfigurationManager.ConnectionStrings["default"].ToString()))
    {
        int NextId = MaxId() + 1;

        OleDbCommand command = new OleDbCommand(sql, conn);
        command.Parameters.AddWithValue("Id", NextId);
        command.Parameters.AddWithValue("Nombre", contacto.Nombre);
        command.Parameters.AddWithValue("Apellido", contacto.Apellido);
        
        OleDbParameter param = new OleDbParameter("FechaNacimiento", OleDbType.Date);
        param.Value = contacto.FechaNacimiento;
        command.Parameters.Add(param);
        
        command.Parameters.AddWithValue("Localidad", string.IsNullOrEmpty(contacto.Localidad) ? (object)DBNull.Value : contacto.Localidad);
        command.Parameters.AddWithValue("Calle", string.IsNullOrEmpty(contacto.Calle) ? (object)DBNull.Value : contacto.Calle);
        command.Parameters.AddWithValue("Numero", contacto.Numero.HasValue ? contacto.Numero : (object)DBNull.Value );

        conn.Open();

        command.ExecuteNonQuery();

        contacto.IdContacto = NextId;

        return contacto;

    }
}

También se veras que la función MaxId() que permite recuperar el ultimo id ingresado no sufre cambio alguno con respecto a utilizado en una base de datos Sql Server

private static int MaxId()
{
    string sql = @"SELECT MAX(IdContacto)
                      FROM Contacto";


    using (OleDbConnection conn = new OleDbConnection(ConfigurationManager.ConnectionStrings["default"].ToString()))
    {

        OleDbCommand command = new OleDbCommand(sql, conn);

        conn.Open();

        return Convert.ToInt32(command.ExecuteScalar());

    }
}

2 – Actualización de la entidad (Update)

En la operación de actualización hay que remarcar dos puntos que son importantes y generan cambio con respecto a su equivalente en Sql Server

Uno al igual que como sucedió en la funcionalidad de Insert, es necesario definir el tipo de dato explicito para la fecha de nacimiento.

El segundo aspecto se refiere a que si bien hasta ahora pensábamos que los nombres de los parámetros guardaban una conexión, bajo la operación del actualización nos damos cuenta que no es tan así.

Es por ello que se notara que la creación de parámetros en este caso el “Id” ha sido declarado al final del resto, mientras que en si equivalente para Sql Server podría haber sido declarado en cualquier posición.

Esto marca un punto importante durante la actualización y es que el nombre sirve para tener una referencia a que parámetro estamos asignando el valor, pero para el provider de base de datos OleDb, lo que importa es la posición del parámetro en colección de Parameters.

Si en este código se declarar el parámetro “Id” en primer lugar la actualización no se realizaría adecuadamente.

private static ContactoEntity Update(ContactoEntity contacto)
{

    string sql = @"UPDATE Contacto SET 
                          Nombre = @Nombre
                          ,Apellido = @Apellido
                          ,FechaNacimiento = @FechaNacimiento
                          ,Localidad =  @Localidad
                          ,Calle =  @Calle
                          ,Numero = @Numero
                    WHERE IdContacto = @Id";


    using (OleDbConnection conn = new OleDbConnection(ConfigurationManager.ConnectionStrings["default"].ToString()))
    {

        OleDbCommand command = new OleDbCommand(sql, conn);
        
        command.Parameters.AddWithValue("Nombre", contacto.Nombre);
        command.Parameters.AddWithValue("Apellido", contacto.Apellido);

        OleDbParameter param = new OleDbParameter("FechaNacimiento", OleDbType.Date);
        param.Value = contacto.FechaNacimiento;
        command.Parameters.Add(param);

        command.Parameters.AddWithValue("Localidad", string.IsNullOrEmpty(contacto.Localidad) ? (object)DBNull.Value : contacto.Localidad);
        command.Parameters.AddWithValue("Calle", string.IsNullOrEmpty(contacto.Calle) ? (object)DBNull.Value : contacto.Calle);
        command.Parameters.AddWithValue("Numero", contacto.Numero.HasValue ? contacto.Numero : (object)DBNull.Value);
        
        command.Parameters.AddWithValue("Id", contacto.IdContacto);

        conn.Open();

        int rows = command.ExecuteNonQuery();

        return contacto;

    }
}

3 – Eliminación de la entidad (Delete)

Esta operación no sufre cambio alguno con respecto a su equivalente en Sql Server, salvo los objetos de ado.net utilizados.

 

public static void Delete(int Id)
{
    string sql = @"DELETE FROM Contacto 
                   WHERE [IdContacto] = @Id";


    using (OleDbConnection conn = new OleDbConnection(ConfigurationManager.ConnectionStrings["default"].ToString()))
    {

        OleDbCommand command = new OleDbCommand(sql, conn);
        command.Parameters.AddWithValue("Id", Id);

        conn.Open();

        command.ExecuteNonQuery();

    }
}

 

[C#]
[VB.NET]

27 comentarios:

  1. Hola!, la verdad es que estos artículos están bastante bien expuesto, ójala todo en internet estuviera tan bien expuesto.

    Ya que estás tratando temas relativos a ADO.NET me gustaría plantearte unas dudas que tengo al respecto, por si me puedes echar una mano en alguna de ellas.

    Las dudas son las siguientes:

    1. La utilización de transacciones puede hacer que un tipo de bloqueo pesimista se convierta en uno optimista?

    2. ¿Bajo que circunstancias no se debería emplear un Dataset con tipo?

    3. ¿Puede ser reemplazado un Dataset sin tipo por uno con tipo?, ¿bajo que circunstancias? ..¿por ejemplo?

    Forman parte de la preparación de un examen sobre tecnologías .NET. Se las preguntas, pero aún no tengo nada claras las respuestas.

    La verdad creo que son preguntas bastante buscaditas, sobre todo la 1ª. Estaré encantado con cualquier ayuda al respecto.

    Saludos y ciertamente un buen blog el tuyo!

    ResponderEliminar
  2. Hola Leandro!, acabo de ver que tu mismo has respondido dos de las preguntas que te formulaba en mi mensaje anterior, en los foros de msdn.

    Muchas gracias por tu ayuda!

    Saludos!

    -Alex_TS-

    ResponderEliminar
  3. Hola necesito ayuda sobre como actualizar la cantidad en bodega cuando digito la cantidad recibida de determinados materiales en mi tabla recepcion_pedido

    Espero me pueda ayudar!!!

    ResponderEliminar
  4. Hola Karen

    Te comento, no se si has analizado la seccion de actualizacion pero alli esta al clave, simplemente arma una consulta del tipo

    UPDATE recepcion_pedido SET bodega = @cant WHERE Idbodega = @Idbodega

    algo que no has detallado es como diferencias a una bodega de otra, imagino que tendras algun id, es por eso que lo agregue a la query en el WHERE, para que solo actualzice esa bodega en particular

    lo unico que queda es agregar esta como veras en el articulo del post, parandole los parametros de @cant y @idbodega al objeto SqlCommand

    espero te sea de utilidad este comentarios
    saludos

    ResponderEliminar
  5. Buena sugerencia pero necesito que la cantidad en bodega incremente cuando digito la cantidad_recibida

    es decir cantBodega= cantBodega+ Cantidad_recibida

    ResponderEliminar
  6. Hola

    en ese caso podrias hacer

    UPDATE recepcion_pedido SET cantBodega = cantBodega + @cant WHERE Idbodega = @Idbodega

    como veras tomas el campo con su valor actual y le sumas el del parametro.

    saludos

    ResponderEliminar
  7. Buenas Noches
    Sr. Leandro Tuttini

    Descarque el zip VB.NET
    hizo la extracion y abri el proyecto, pero cuando ya ejecutado, cuando guardo el registro, me da un mensaje de "El proveedor 'Microsoft.Jet.OLEDB.4.0' no está registrado en el equipo"

    ¿donde tengo que registrar el proveedor?

    Saludos...

    ResponderEliminar
  8. hola Roger

    tienes en la pc instalado office, concretamente Ms Access? sino esta instalsdo no va a funcionar

    ademas estas usando una pc de 64 bits ? porque si es asi deberias cambiar el target del proyecto para que compile en 32, ya que Jet no soporta la arquitectura 64

    saludos

    ResponderEliminar
  9. me gustaria saber como puedo hacer una clase generica que haga las operaciones Create retrive update delete, que me sirva para cualquier aplicacion. que sea reutilizable.

    ResponderEliminar
  10. hola marcelo

    creo que no entendi la pregunta

    podrias programar en capas, por lo que crearias uan capa de datos con als operaciones de insert, actualizacion, etc para operar con las entidades de la db

    [N-Tier] – Desarrollo en capas - Ejemplo Facturacion - parte 3

    de esta forma creas las operaciones que cometas y puedes reutilizarlas en varias aplicaciones

    saludos

    ResponderEliminar
  11. hola leandro saludos

    como puedo incorporar la base de datos al proyecto de vb me refiero tengo que crear un .exe para cualquier pc y el programa tiene conecion a la base de datos como se realiza ese procedimiento
    gracias de antemano por sus aportes

    ResponderEliminar
  12. hola Manuel

    por base de datos te refieres a usar Ms Access ?
    el articulo utiliza esta base de datos

    si es Acces con que solo este el archivo de la db junto al .exe, o en el connection string le indiques la ruta correcta alcanzaria

    despues la db es solo un archivo

    saludos

    ResponderEliminar
  13. no leandro perdon por no explicar

    me refiero a sql
    gracias nuevamente

    ResponderEliminar
  14. hola Manuel

    por sql te refieres a sql server ? porque sql es la notacion para realizar consultass a las base de datos, Ms Access usa sql, MySql usa sql, sql server usa sql, todas las dbs usan sql

    imagino se trata de sql server, en ese caso usas la db con attach automatico o sea tienes el .mdf dentro de la estructura de la solucion? si es asi aplica igual que con Ms Access solo incluyes este en la distribucion, pero ojo en la pc del usuario tambien deberias instalar el sql server express para que inicie el servicio

    saludos

    ResponderEliminar
  15. Disculpa Leandro si deseó trabajar en Sql server utilizando la versión gratuita la express 2008 esta me serviría para poder tener una aplicación clientes servidor. usando esta versión express para una base de datos algo más robusta que Access. Y estos códigos que. Estas colocando serviría de maravilla para Sql server..? Disculpa sí esté no era el foro para preguntarte esto pero de alguna manera necesitaba contactarte. Saludos de Venezuela

    ResponderEliminar
  16. hola Enyelber

    usas sql server es una buena idea para contar con una db robusta

    pero no entendi la pregunta que planteas, o cual seria la duda


    saludos

    ResponderEliminar
  17. Hola! Muchas gracias por toda la info!

    Ahora hay algo que estoy tratando de hacer y no encuentro la forma. Estoy trabajando con visual studio 2005 y una BD Access.
    Quiero hacer un INSERT masivo a una de as tablas, con datos que guarde dentro de un Dictionary<>.

    Podrías ayudarme ya que no encontré otra forma más que poner el insert dentro del foreach que recorre el Dictionary.

    Muchas gracias!!

    ResponderEliminar
  18. Como estas Leandro;

    Estoy haciendo una aplicación sencilla donde uso tu codigo Login – Usando Password con Hash. En access me corre de maravilla pero quise hacerlo con una BD SQL Server y hago lo siguiente:

    1.- Cree un formulario de Inicio de Sesión y en su codigo tiene lo siguiente:

    Imports System.Collections.Generic
    Imports System.ComponentModel
    Imports System.Data
    Imports System.Drawing
    Imports System.Linq
    Imports System.Text
    Imports System.Windows.Forms

    Public Class LoginForm1
    Private Sub OK_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles OK.Click
    Dim nombre As String = UsernameTextBox.Text
    Dim password As String = PasswordTextBox.Text

    If LoginService.Autenticar(nombre, password) Then
    Me.DialogResult = DialogResult.OK
    Else
    Me.DialogResult = DialogResult.Abort
    End If
    End Sub

    Private Sub Cancel_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Cancel.Click
    Me.Close()
    End Sub

    End Class

    En la Clase Login.VB tengo lo siguiente:

    Imports System.Collections.Generic
    Imports System.Linq
    Imports System.Text
    Imports System.Data.SqlClient
    Imports System.Configuration

    Public NotInheritable Class LoginService
    Public Shared Function Autenticar(ByVal usuario As String, ByVal password As String) As Boolean

    Dim sql As String = "SELECT COUNT(*) " & _
    "FROM Usuarios " & _
    "WHERE NombreLogin = @nombre AND Password = @password"


    Using conn As New SqlConnection(ConfigurationManager.ConnectionStrings("default").ToString())
    conn.Open()

    Dim command As New SqlCommand(sql, conn)
    command.Parameters.AddWithValue("@nombre", usuario)

    Dim hash As String = Helper.EncodePassword(String.Concat(usuario, password))
    command.Parameters.AddWithValue("@password", hash)

    Dim count As Integer = Convert.ToInt32(command.ExecuteScalar())

    If count = 0 Then
    Return False
    Else
    Return True
    End If

    End Using

    End Function
    End Class

    inmediatamente me da un error en ConfigurationManager me indica Error 1 'ConfigurationManager' no está declarado. Puede que esté inaccesible debido a su nivel de protección.
    yo copie la Base de Datos que tenias en el ejemplo de Hast y agregue esa al proyecto, indicandole conectar una Base de datos y le indique que era un Archivo de base de datos de Microsoft SQL Server (SqlClient) y le indique la ruta donde esta la BD. pero no se porque me da el error del ConfigurationManager. estoy trabajando con vs2012 y sqlserver que viene integrado en visual... gracias por la ayuda de antemano

    ResponderEliminar
  19. hola Jéssica

    pero ese diccionario solo tiene dos valores
    podrias recorrerlo usando

    foreach(KeyValuePair item in diccionario){

    }

    asi podrias armar el insert

    http://social.msdn.microsoft.com/Forums/es/vcses/thread/3998fceb-9b98-4f05-8588-d59373781d2b

    como en la pregunta del link analiza como se arma el command para ir pasando los parametros y el cmd.Parameters.Clear(); en cada loop

    saludos

    ResponderEliminar
  20. hola Enyelber

    recuerda que debes agregar al referencia a System.Configuration
    ademas de definir el

    using System.Configuration

    para que el ConfigurationManager se pueda usar

    saludos

    ResponderEliminar
  21. Leandro ahora tengo este problema
    en este codigo de la clase
    Public NotInheritable Class LoginService
    Public Shared Function Autenticar(ByVal usuario As String, ByVal password As String) As Boolean

    Dim sql As String = "SELECT COUNT(*) " & _
    "FROM Usuarios " & _
    "WHERE NombreLogin = @nombre AND Password = @password"


    Using conn As New SqlConnection(ConfigurationManager.ConnectionStrings("default").ToString())
    conn.Open()

    Dim command As New SqlCommand(sql, conn)
    command.Parameters.AddWithValue("@nombre", usuario)

    Dim hash As String = Helper.EncodePassword(String.Concat(usuario, password))
    command.Parameters.AddWithValue("@password", hash)

    Dim count As Integer = Convert.ToInt32(command.ExecuteScalar())

    If count = 0 Then
    Return False
    Else
    Return True
    End If

    End Using

    End Function

    la sentencia, Using conn As New SqlConnection(ConfigurationManager.ConnectionStrings("default").ToString()) me indica que No se controló System.NullReferenceException... lo estraño que en el ejemplo que tu tienes de Hash funcionaba pero ahora me sale el mismo error

    ResponderEliminar
  22. hola Enyelber


    has validado que en el app.config este presente el tag que define la configuracion del connection string

    porque puede dar ese error cuando no encuantra el "default" el el archivo de configuracion

    saludos

    ResponderEliminar
  23. hola leandro,

    modifique mi App.config











    pero ahora me indica el siguiente detalle:

    No se puede abrir la base de datos en la versión 706. Este servidor es compatible con la versión 662 y anteriores. No se admite esta ruta de actualización.

    voy a revisar que pasa. pero ya me estoy acercando jejeje

    ResponderEliminar
  24. hola Enyelber

    ese error se suele generar cuando tienes una db que fue creada con un sql server 2008 R2 en una instalacion que no es R2

    valida que sql server tienes en la pc, si es la que instala visual studio, entonces quizas deberias quitar ese sql server y descargar la version sql server 2008 R2 express

    saludos

    ResponderEliminar
  25. efectivamente tengo la versión 2012. ya pude conectar la BD con esta configuración.










    estubo bueno el dolor de cabeza pero se aprende. saludos y gracias

    ResponderEliminar
  26. Buenas...

    Estoy muy interesado en este codigo...

    Estoy desarrollando una aplicacion en Visual Studio 2013 con base de datos Acces 2013...

    Necesito lo siguiente:

    El programa va a ser usado por varias personas en distintas computadoras de manera independiente, al no tener acceso a internet estas computadoras no puedo usar una base de datos en la nube para que todas se conecten a ella, asi que cada laptop tendra su propia base de datos.

    En la Oficina principal tendran tambien el programa, pero alli necesitaran todos los datos de las bases de datos de todos los usuarios por cuestiones de reportes generales.

    Como una base de datos acces a la larga pesa mucho para ser enviada por email, se me ocurrio la idea de generar un archivo excel que contiene los datos de las bases de datos.

    Cree un form en donde cargo ese Excel en un DataGridView, hasta los momentos me ha salido todo bien... el archivo carga perfecto y me muestra todos los valores del excel en el DataGridView.

    Lo que necesito ayuda es para tomar todos esos datos mostrados en el DataGridView y Actualizar la base de datos asosiada a la aplicacion.

    Necesito que los datos existentes se actualicen y los que no estan se generen... esto para evitar duplicados...

    La tabla uno la filtro por numero de Cedula, en la base de datos tengo como valor unico, que no pueda repetirse.

    La tabla dos los campos que no pueden repetirse simultaneamente son Cedula y Fecha de consulta medica.

    Como podria hacer eso? como podria actualizar mi base de datos tomando en cuenta lo que comento?
    Agradeceria muuuuuchisimo su ayuda ya que llevo 3 dias dando vueltas en la cabeza e investigando en internet y no he dado con una solucion.

    Saludos...

    ResponderEliminar