lunes, 21 de junio de 2010

[N-Tier] – Desarrollo en capas - Ejemplo Facturación – Parte 1

 

Introducción

Este articulo es el primero de una serie que tratara temas relacionados con el desarrollo en capas

La idea original es empezar desde el principio e ir paulatinamente agregando funcionalidad mientras se analiza las mejoras involucradas en el diseño

Es por eso que este primer articulo no mostrara nada en la separación en capas, sino que se desarrollara íntegramente en la presentación, todo el código estará en los eventos o método privados de los formularios

Esto será útil para poder comparar la evolución en la codificación empleada cuando no se tienen capas, y cuando se define una arquitectura que si la tiene.

Ejemplo de código

Como se comento al principio del articulo este ejemplo no reflejara una arquitectura en capas, sino que estará toda la lógica en los formularios.

Además se notara que en el ejemplo se hace uso de datatable dinámicos, o sea no se usan objetos tipados.

Sera al momento de armar una arquitectura con capas en donde esto se modificara para dar lugar a una correcta comunicación.

En la siguiente imagen se puede apreciar el diseño del formulario:

Este permitirá seleccionar un cliente, mediante el botón de Búsqueda, mostrando un dialogo con una grilla con el listado de cliente.

Al costado derecho de la grilla se encuentra el botón con un signo de “+”, este permitirá agregar un nueva ítem de compra.

En la parte inferior un botón de confirmar procesara el pedido, previa validación la información ingresada.

Carga de los datos de inicialización del formulario

Durante la carga del formulario de compra, se tiene lugar varias acciones.

La primer tiene que ver con una consulta a la Base de  Datos en donde se recuperan el listado de “temas” que serán cargados en el combo del DataGridView que representa las líneas de compra.

Además por defecto se crea una línea de compra, usando el método NuevaLinea()

private void frmCompra_Load(object sender, EventArgs e)
{
    //
    // Obtengo la lista de Tracks
    //
    DataTable dt = new DataTable();

    using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["default"].ToString()))
    {
        conn.Open();

        string sql = @"SELECT TrackId, Name FROM Track ORDER BY Name";
        SqlCommand cmd = new SqlCommand(sql, conn);

        SqlDataAdapter da = new SqlDataAdapter(cmd);
        da.Fill(dt);

    }

    //
    // Cargo los items del combo
    //
    DataGridViewComboBoxColumn comboCol = dgvLineaCompra.Columns["Track"] as DataGridViewComboBoxColumn;
    comboCol.ValueMember = "TrackId";
    comboCol.DisplayMember = "Name";
    comboCol.DataSource = dt;

    //
    // 
    //
    NuevaLinea();
}
/// <summary>
/// Crea un item de compra en la grilla
/// </summary>
private void NuevaLinea()
{
    DataTable dt = null;

    if (dgvLineaCompra.DataSource == null)
    {
        dt = new DataTable();
        dt.Columns.Add("Track", typeof(int));
        dt.Columns.Add("PrecioUnitario");
    }
    else
        dt = dgvLineaCompra.DataSource as DataTable;

    DataRow row = dt.NewRow();

    dt.Rows.Add(row);

    dgvLineaCompra.DataSource = dt;
}

Este método valida si la grilla posee un origen da datos previamente asignado, en caso de contar con uno lo recupera y procede a agregar un ítem nuevo.

Sino hay datos presentes en la grilla, crea una nueva instancia de datos y procede de igual forma.

Al final concluye con la asignación del datatable a la grilla, para desplegar la información.

Obtener el precio desde un combo en el DataGridView

Para poder operar correctamente con el ComboBox contenido en una celda de la grilla, es necesario contar con mas de un evento.

La explicación del uso de estos eventos, esta detallada en este otro artículo:

[DataGridView] - Parte 6 - ComboBox y evento SelectedIndexChanged

Lo interesante es ver como se recuperar un valor especifico de la consulta haciendo uso del ExecuteScalar(), el cual devuelve contenido del primer registro y la primer columna de la consulta, en este caso por ser solo un valor cierra perfecto su uso.

DataGridViewComboBoxEditingControl dgvCombo;

private void dgvLineaCompra_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
    dgvCombo = e.Control as DataGridViewComboBoxEditingControl;

    if (dgvCombo != null)
    {
        dgvCombo.SelectedIndexChanged += new EventHandler(dgvLineaCompra_SelectedIndexChanged);
    }

}

private void dgvLineaCompra_SelectedIndexChanged(object sender, EventArgs e)
{

    ComboBox combo = sender as ComboBox;

    //
    // Se obtiene el precio
    // en base a la seleccion de combo en la celda de la grilla
    //
    decimal precio = 0;
    using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["default"].ToString()))
    {
        conn.Open();

        string sql = @"SELECT UnitPrice FROM Track WHERE TrackId = @idTrack";
        SqlCommand cmd = new SqlCommand(sql, conn);
        cmd.Parameters.AddWithValue("@idTrack", Convert.ToInt32(combo.SelectedValue));

        precio = Convert.ToDecimal(cmd.ExecuteScalar());

    }

    DataGridViewRow row = dgvLineaCompra.CurrentRow;

    row.Cells["PrecioUnitario"].Value = precio;
}

private void dgvLineaCompra_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{

    if (dgvCombo != null)
        dgvCombo.SelectedIndexChanged -= new EventHandler(dgvLineaCompra_SelectedIndexChanged);

}

Validación del formulario

La validación de los campos del formulario es un punto importante, en el método Validaciones(), no solo se verifican los campos individuales, sino que también se itera por las líneas de la grilla verificando que las celdas estén completas.

Se puede remarcar que al usuario se le informa de los puntos detectados durante la validación mediante el uso del control ErrorProvider, desplegándose así un icono al costado de cada control o línea de la grilla donde se detecta el problema.

/// <summary>
/// Valida que los controles contengan con informacion correcta 
/// antes de procesar la transaccion
/// </summary>
/// <returns></returns>
private bool Validaciones()
{
    bool result = true;

    //
    // inicializo los mensajes de validaciones que pudiera haber
    //
    errorProvider.Clear();

    //
    // verifico los campos del cliente
    //
    if (string.IsNullOrEmpty(txtNombre.Text))
    {
        errorProvider.SetError(txtNombre, "El nombre es obligatorio"); 
        result = false;
        
    }

    if (string.IsNullOrEmpty(txtApellido.Text))
    {
        errorProvider.SetError(txtApellido, "El apellido es obligatorio"); 
        result = false;
    }

    if (string.IsNullOrEmpty(txtEmail.Text))
    {
        errorProvider.SetError(txtEmail, "El Mail es obligatorio"); 
        result = false;
    }

    //
    // valido las lineas de compra
    //
    foreach (DataGridViewRow row in dgvLineaCompra.Rows)
    {
        //
        // inicializo las linea de error
        // en caso de tener un mensaje de error previo
        //
        row.ErrorText = "";

        //
        // se validan los campos de la fila
        //
        if (string.IsNullOrEmpty(Convert.ToString(row.Cells["Track"].Value)))
        {
            row.ErrorText = "Debe seleccionar in item de compra.";
            result = false;
        }

        if (string.IsNullOrEmpty(Convert.ToString(row.Cells["Cantidad"].Value)))
        {
            row.ErrorText = "Debe ingresar una cantidad";
            result = false;
        }
        else
        {
            int cantidad = 0;
            if (!Int32.TryParse(Convert.ToString(row.Cells["Cantidad"].Value), out cantidad))
            {
                row.ErrorText = "La cantidad ingresada debe ser un valor numerico";
                result = false;
            }
        }

    }

    return result;
}

 

Proceso de Confirmación de Compra

Este es el código mas extenso de todo el formulario, ya que allí se tomara la información y se procederá toda la transacción de compra realizando los INSERT o UPDATE que hagan falta para lleva a cabo la operación.

 

private void btnConfirmar_Click(object sender, EventArgs e)
{
    if (!Validaciones())
        return;

    //
    // inicializo la transacciones
    //
    using (TransactionScope scope = new TransactionScope())
    {
        using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["default"].ToString()))
        {
            conn.Open();

            #region Creo\Actualizo la informacion del cliente
            int idCliente = 0;

            //
            // si el cliente se ha seleccionado lo actualizo, sino se crea uno nuevo
            //
            if (cliente == null)
            {
                string sql = @"INSERT INTO Customer (FirstName, LastName, Company, Address, Email) VALUES (@firstName, @lastName, @company, @address, @email)
                       SELECT SCOPE_IDENTITY()";

                SqlCommand cmd = new SqlCommand(sql, conn);

                cmd.Parameters.AddWithValue("@firstName", txtNombre.Text);
                cmd.Parameters.AddWithValue("@lastName", txtApellido.Text);
                cmd.Parameters.AddWithValue("@company", txtDireccion.Text);
                cmd.Parameters.AddWithValue("@address", txtCompañia.Text);
                cmd.Parameters.AddWithValue("@email", txtEmail.Text);


                idCliente = Convert.ToInt32(cmd.ExecuteScalar());

            }
            else
            {
                string sql = @"UPDATE Customer SET  
                                    FirstName = @firstName, 
                                    LastName = @lastName, Company = @company, 
                                    Address = @address,
                                    Email = @email
                            WHERE CustomerId = @customerid";

                SqlCommand cmd = new SqlCommand(sql, conn);

                cmd.Parameters.AddWithValue("@firstName", txtNombre.Text);
                cmd.Parameters.AddWithValue("@lastName", txtApellido.Text);
                cmd.Parameters.AddWithValue("@company", txtDireccion.Text);
                cmd.Parameters.AddWithValue("@address", txtCompañia.Text);
                cmd.Parameters.AddWithValue("@email", txtEmail.Text);
                cmd.Parameters.AddWithValue("@customerid", Convert.ToInt32(cliente["CustomerId"]));
                
                cmd.ExecuteNonQuery();
                idCliente = Convert.ToInt32(cliente["CustomerId"]);

            }
            #endregion

            #region Creo el Encabezado de la Factura
            
            int idEncabezadoFactura = 0;

            string sqlFactura = @"INSERT INTO Invoice (CustomerId, InvoiceDate, BillingAddress, Total) VALUES (@customerId, @date, @address, @total)
                       SELECT SCOPE_IDENTITY()";

            using (SqlCommand cmd = new SqlCommand(sqlFactura, conn))
            {

                cmd.Parameters.AddWithValue("@customerId", idCliente);
                cmd.Parameters.AddWithValue("@date", DateTime.Now.Date);
                cmd.Parameters.AddWithValue("@address", txtDireccion.Text);
                cmd.Parameters.AddWithValue("@total", 0);

                idEncabezadoFactura = Convert.ToInt32(cmd.ExecuteScalar());
            }

            #endregion
            
            #region Creo cada linea de la facturacion

            string sqlLineaFactura = @"INSERT INTO InvoiceLine (InvoiceId, TrackId, UnitPrice, Quantity) 
                                        VALUES (@invoiceid, @trackid, @unitprice, @quantity)
                                        SELECT SCOPE_IDENTITY()";

            using(SqlCommand cmd = new SqlCommand(sqlLineaFactura, conn))
            {
                foreach (DataGridViewRow row in dgvLineaCompra.Rows)
                {
                    //
                    // como se reutiliza el mismo objeto SqlCommand es necesario limpiar los parametros
                    // de la oepracion previa, sino estos se iran agregando en la coleccion, generando un fallo
                    //
                    cmd.Parameters.Clear();

                    cmd.Parameters.AddWithValue("@invoiceid", idEncabezadoFactura);
                    cmd.Parameters.AddWithValue("@trackid", Convert.ToInt32(row.Cells["Track"].Value));
                    cmd.Parameters.AddWithValue("@unitprice", Convert.ToDecimal(row.Cells["PrecioUnitario"].Value));
                    cmd.Parameters.AddWithValue("@quantity", Convert.ToInt32(row.Cells["Cantidad"].Value));

                    //
                    // Si bien obtenermos el id de linea de factura, este no es usado
                    // en la aplicacion
                    //
                    int idLineaFactura = Convert.ToInt32(cmd.ExecuteScalar());
                    
                }
            }
            #endregion

        }

        scope.Complete();

    }

    InicializarControles();

}

Los bloques de código principales incluyen:

- Creación o actualización de los datos del cliente

- Creación de la cabecera de la compra

- creación de las líneas de compra, asociadas a la cabecera.

En este punto sea interesante contar con la estructura de tablas con las cuales se trabajara:

Si se analiza el código se vera que los id de las tablas son auto numéricos, por lo tanto en la misma operación de INSERT se adiciona la operación que permite recuperar el id generado por la tabla, haciendo uso del SCOPE_IDENTITY()

Para mas información sobre el uso de los campos auto numéricos revisar este otro articulo:

[ADO.NET] – Parte 6 - Ejemplos simples – Campos Auto numéricos (Identity)

Algo a destacar es el uso de transacciones, las cuales son muy simples de implementar al contar con la clase TransactionScope, la cual engloba toda la transacción, y por medio del método Complete() se confirma la operación, cualquier error que se produjera anularía las operaciones realizadas dejando las tablas con datos consistentes.

Conclusiones

Si bien el ejemplo se puede considerar prolijo y ordenado en el código, esto es solo porque se trata de una aplicación pequeña que forma parte de un ejemplos, aplicar esta misma técnica en aplicaciones de varios formulario y operaciones que interactúan puede resultar verdaderamente complejas.

Las principales desventajas de esta forma de codificar serian:

- No fomentan la reutilización, si se analiza el código cada consulta o conexión a la base de datos se realiza en los eventos, si fuera necesario en algún otro punto del código obtener la misma información abría que replicar el código.

Este punto se podría solucionar si se crean métodos que encapsulen esta funcionalidad, pero al estas usando formulario estos estarían local al mismo, no podrías accederse desde otros puntos de la aplicación, es necesario contar con otro sitio que permita acceder sin generar acoplamiento.

- El procesamiento de las transacciones generan código muy extenso, este punto es claramente visible en el botón de confirmación de la compra, esta es una operación simple en comparación con las que podrían crearse, sin embargo creo una buena cantidad de líneas de código.

- No hay separación de las responsabilidades, en un solo punto se crean cliente, y se graban la facturación, esto debería poder separarse para dividir la complejidad y aumentar la mantenibilidad de la aplicación.

Tener código que hace muchas operaciones distintas no es bueno, mas que nada para su mantenimiento futuro.

 

Consideración para el código

Los ejemplos fue desarrollado usando VS 2008 y SQL Server 2008 Express.

Es necesario tener el servicio de Sql Express iniciado localmente para poder ejecutar la solución.

 

[C#]
[VB.NET]

51 comentarios:

Isidro dijo...

Hola, Gracias segire estos post muy cerca ya que ahora mismo estoy en el desarrollo de una aplicacion de facturacion, solo dos cosas favor de incluir el script de la base de datos algunos no tenemos sql express, por ejemplo yo solo tengo el sql server, favor de comentar mas el codigo esto nos ayuda a entender mejor, gracias

Nelson Giraldo dijo...

Hola Leandro, gracias por estos ejemplos, seguire de serca estos post sobre desarrollo en capas ya que me interesa mucho el tema y simplemente gracias por tomarte el tiempo y realizar este tipo de temas.

Augusto dijo...

¡Muchas gracias por el aporte!

Enrique dijo...

Magnifico POST

Te agradezco que lo publiques en C# y en VB.NET

He tenido que reconectar la BD no se por que razon. Pensaba que me salia errorprovider porque algo estaba mal y resulta que te lo proporciona windows para control de errores, curioso.

Voy a la segunda parte a ver con que me sorprendes

Mauro dijo...

Hola Leandro gracias por el post, me ha sido de mucha utilidad. Tengo una duda, hace algun tiempo tuve una conversacion con un profesor de desarrollo de software en la universidad, respecto a las conexiones a las BDs y donde definir los archivos de conexion a dichas BD. El aseguraba que la mejor practica era definir las propiedades de conexion en un archivo XML aparte. Yo personalmente considero que lo mejor es definir todo en el web.config. ¿Usted que considera?
Muchas gracias

Andres Mauricio

Leandro Tuttini dijo...

hola Mauro

Particularmente siempre dejo la cadena de conexion en el .config que se define en la aplicacion, es mas si lo ves bien el app.config es un xml.

La verdad no me pondria a agregar toda la logica que se necesita en un xml extra solo por el connection string, ademas el .config ya tiene hasta una seccion para esto de nombre, si la idea es no ponerlo alli para que creo .net esa seccion.

Storing and Retrieving Connection Strings

Imagino que el planteo lo hizo para agregar algo de seguridad, al no estar alli mismo el xml y poder sacarlo a otro lugar se evitaria una visualizacion rapida y posible inseguridad, pero creo que si es por esto de ultima se podria encriptar una seccion del .config, o sino usar seguridad integrada de windows para autenticar al conexion contra la db.

saludos

Fernando Leandro dijo...

Hola Leandro, gracias por el POST, como haces para incrustar el codigo en blogspot, me gusta como queda tabulado con la numeración, hay que utilizar alguna herramienta especial para que se maquete así en HTML.
Gracias

Leandro Tuttini dijo...

hola Fernando

Lo que uso para publicar codigo es una tools que me permita poner syntax highlighting.

Para escribir en los articulos uso Windows Live Writer, con este plugin

Code Snippet With Syntaxhighlighter Support for Windows Live Writer

Para que esto funcione debe modificar con algunas referencas el template del blog.

saludos

fredy orantes dijo...

hola leandro
es un exelente material
solo que e tenido unos imcobenientes
con algo que estoy realizando
dengo un grid con las columnas
codigo - cantidad - punitario - total

estoy trabajando con texbox el grid
megustaria saber com hacer la consulta de punitario cuando digite el codigo y muestre el punitario de ese producto, cuando ponga la cantida lo multiplique por punitario y muestre el resultado en total
, y el boton para quitar articulos de la lista del grid

Ing. Prof. Jesus Eguiarte Hernandez dijo...

Tengo un pequeño problema con una palabra que usas frecuentemente que es la de grilla, si me pudieras definir el significado ya que en mi pais tiene otro significado.

Gracias!

Leandro Tuttini dijo...

hola Jesus

luego de algunas preguntas sobre el mismo tema estoy cambiando el termino grilla, por grid

hago referencia a este cuando me refiero a controles como ser el DataGridView, o GridView

la grilla es el grid, o sea un representacion de campos y registros que forman una tabla

basicamente una cuadricula

saludos

Juan Manuel dijo...

Juan
Hola, Leandro quisiera saber como haces para que las celda de Precio unitario no se pued editar en el diseño de Grid esta False y en ningua parte del codigo veo q le cambien a true,
yo necesito aser algo parecido pero mi grido si deja editar todo. Saludos y que estes bien..

Leandro Tuttini dijo...

hola Juan Manue

cuando defines la columna asigna la propiedad ReadOnly en true

esto hara que esa columna no se pueda editar

saludos

ffdv dijo...

Amigo Leandro, perdona usar esta vía, pero no encontré como enviar una pregunta sobre un tema distinto (Leer Excel desde ASP VB), agradecería me indicaras por donde y como hacerlo. gracias de antemano.

Jenyna ramos dijo...

Hola Leandro una consulta respecto a C# en N capas entorno web en la datagridview al momento de realizar tus propios metodos se utiliza OnCommand="Editar_Command" y CommandName= <%# Container.DataItem("CODIGO")%> o como se utiliza dichos metodos con respecto al Diseño=?
(
Editar )

Jenyna ramos dijo...

Y en la parte programtiva se realiza de la siguiente forma o como se realiza=?
[public

void Editar_Command(object sender, CommandEventArgs e)

{
string str_modo = "E";
string str_prueba = e.CommandName;
Response.Redirect(
"frmPrueba.aspx?modo=" + str_modo + "&prueba=" + str_prueba);}]

Leandro Tuttini dijo...

hola Jenyna ramos

pero tu desarrollo es web o winforms, porque mencionas el DataGridView, y este es para winforms

en el CommandName colo debes poner el nombre del campo
CommandName="CODIGO"

despues puede hacer

[ASP.NET] GridView – Edición Empleados

saludos

henry.iq dijo...

Hola, muchas gracias por los ejemplos
me sirven de mucho.

Tengo un problema, como podría actualizar algunas celdas(Ejemplo:precio,cantidad) del gridview al ingresar por caja de texto(TextBox).

el proceso en resumen:Cargo los productos en un dropdownlist como también precio en un label, ingreso la cantidad en el textbox y lo muestro en el gridview al cliquear el boton agregar.

agradezco de antemano su respuesta.

Leandro Tuttini dijo...

hola henry.iq

veo que se trata de un desarrollo web

me pregunto y si tomas la info del combo y del textbox y simplemente haces un INSERT en la tabla ?

por supuesto luego de insertar podrias recargar los registros del grid para visualizar el ultimo insert

saludos

Rosa Elena Santos Ferrer dijo...

Muy bueno el tutorial. me da un error en el uso de la transaccion. me podria explicar como hace referencia a ella.

Rosa Elena Santos Ferrer dijo...

Hola leandro tengo un problemita me da un error porque no se como hacer referencia a la transaccion.

Leandro Tuttini dijo...

hola Rosa

cuals eria el mesnaje de error que recibes ? porque puede ser un problema con alguina query

recuerda que las transacciones evitan que se ejecuten en forme las queries, sino que lo hacen al momento del complete

deberias atrapar el exception y ver el mensajes del error

saludos

Rosa Elena Santos Ferrer dijo...


Este es el mensaje:

Error 1 The type or namespace name 'Transactions' does not exist in the namespace 'System' (are you missing an assembly reference?)

Rosa Elena Santos Ferrer dijo...

Estoy trabajando la aplicacion en C#.

Leandro Tuttini dijo...

hola Rosa

has agregado la referencia a System.Transaction ?

porque recuerda que sino agregas esa referncia en el proyecto la clase de transaccion no puedes usarla

saludos

Rosa Elena Santos Ferrer dijo...

Lo hice agregando esa referencia al proyecto. pero me aparecen mas errores. lo revisare bien. gracias

Rosa Elena Santos Ferrer dijo...

Me pregunto si puedo usar una base de dato local.Database1.sdf

porque estoy haciendo una aplicacion, y esa es la que estoy usando.

Rosa Elena Santos Ferrer dijo...

necesito buscar dato de una tabla en la base de dato a traves de un DataGridView.

y no me funciona muy bien.

Leandro Tuttini dijo...

hola Rosa

pero el problema de la transaccion continua, porque el lo ultimo que comentas para realizar un busqueda no hace falta ninguna transacion

puede usar un .sdf si la db sera local a la pc del usuario

saludos

Rosa Elena Santos Ferrer dijo...

Hola el error de la tranasacion me aparece en que esto haciendo pero con la otra base de dato. osea con la aplicacion sin capa, que estoy aprendiendo hacerla.

y el uso de la base de dato local es otra aplicacion que estoy haciendo, que me pidieron en la universidad.

Rosa Elena Santos Ferrer dijo...

Ok. entendi la usare. gracias por la sugerencias.

Rosa Elena Santos Ferrer dijo...

Hola leandro me funciono.

Alexander Rodriguez dijo...

Hola Leandro
Tengo una inquietud.
Como seria el codigo de c# para cuando elimine un ultimo registro de mi BD, y vuelva a presionar mi boton no provoque un error por falta de registros, sino dado el caso arrojar un mensaje que ya no hay registros en la base de datos.
Gracias

Leandro Tuttini dijo...

hola Alexander

que operacion es la que falla cuando no hay registros en la db?

porque en principio no veria que algo tenga que fallar ente esta operacion

saludos

germanRive dijo...

hola leandro tengo una duda---

quiero crear una funcion global en una clase .. esa funcion va a exportar un gridview a excel (es en asp.net(vb)...
---pongo esto
'-----------------
pagina.RenderControl(htw)
response.Clear()
response.Buffer = True
response.ContentType =
-----------------------------
me marka error que dice (la variable se utiliza antes de asignar un valor

Leandro Tuttini dijo...

hola germanRive

esta clase donde la defines ? esta dentro del propio proyecto web

ese response que se ve alli donde lo declaras ? lo pregunto porque quzias sea el

HttpContext.Current.Response

o sea vien de alli?

saludos

germanRive dijo...

mira leandro... lo clase(archivo) esta en el mismo proyecto web..

porque lo que puse anterirmente lo hace en la misma pagina donde tengo la grilla y funciona(y yo lo quiero hacer global o sea una funcion y no tener ke poner todo el codigo(19 lineas) en cada pagina que exporte a excel...

germanRive dijo...

gracias leaandro ya kedo mi funcion ..muchas gracias., y kedo de la siguiente manera (exporta gridview a excel)por si alguien le sirve y de manera simplificada ...

Imports System.IO
Public Class Class1

Public Shared Function exporToExcel(ByVal Grilla As GridView) As Boolean

Dim sb As StringBuilder = New StringBuilder()
Dim sw As StringWriter = New StringWriter(sb)
Dim htw As HtmlTextWriter = New HtmlTextWriter(sw)

Grilla.RenderControl(htw)
With HttpContext.Current.Response
.Clear()
.Buffer = True
.ContentType = "application/vnd.ms-excel" 'vnd.ms-word'exporta a word
.AddHeader("Content-Disposition", "attachment;filename=ArchivoExcel.xls")
.Charset = "UTF-8"
.ContentEncoding = Encoding.Default
.Write(sb.ToString())
.End()
End With
End Function

End Class

germanRive dijo...

leandro otra vez aki preguntado....

como saber o si hay alguna forma el equivalente a form closing en asp.net....
lo que pasa que cargo unas imagenes temporales y quiero que alsalir de la pagina o en este casi ir a otra pagina estas imagenes se eliminen para que no se keden el servidor...alguna idea leandro??

Leandro Tuttini dijo...

hola

lo mas parecido es usar

window.onbeforeunload

desde javascript, con ese evento podrias controlar cuando se cierra el browser o se sale de una pagina

saludos

germanRive dijo...

deja lo pruebo y te digo como me fue...gracias

germanRive dijo...

leandro hay alguna de forma de apuntar a una imgen en una base de datos.. ejemplo... botonimagen.imageurl="/direccion_foto" sin tener que grabarlo en disco....por ahy lei que si se puede.. pero no dice de ke manera...hay alguna forma leandro??

Leandro Tuttini dijo...

hola

podrias crear un handler

[ASP.NET] - Guardar Imagen base de datos

como veras poner el array de byte directo en el Response, sin necesidad de un archivo

saludos

germanRive dijo...

ya quedo leandro muchas gracias.. lo probe de las dos maneras y me resulto util...gracias

germanRive dijo...

hola lenadro tengo el sigueinte problema al imprimir en un aimpresora termica CUSTOM TG02.. al imprimir : CrearLinea(e.Graphics, 9, "FOLIO:", x, y, ancho - 34, 6, StringAlignment.Far) .. me sale el texto reciortado si le dejo mas de 1 espacio entre linea y linea... que podra ser (uso vb.net)

Leandro Tuttini dijo...

hola german

no se que relacion tiene esta pregunta con desarrollo en capas, pero se me ocurre que seria mejor y mas controlado si creras un reporte para imprimir informacion

con un repote podrias controlar mejor temas de fuentes, espacios y ubicacion del contenido

saludos

germanRive dijo...

ok..gracias leandro,,

Eder Santiago dijo...

hola Leandro.
una consulta e desarrollado una sistema con una tabla que guarda imagenes de diseños creados, todo bien pero solo me permitio guardar hasta 20 diseños y me da error: el grupo primary de la tabla esta lleno estoy usando sql server 2008 y v.net 2005 te mandare el codigo para guardarlo y el error que muestra

Leandro Tuttini dijo...

hola Eder

este es el mensaje del error que obtienes: "el grupo primary de la tabla esta lleno"

la verdad es la primera vez que veo un mensaje como este, aunque revisando encontre

http://social.msdn.microsoft.com/Forums/es/netfxwebes/thread/a3f78879-2ecb-4445-93b0-467c3df6b429

sino pregunta en el foro de sql server (del mismo link que proporciono) para ver si saben a que se debe este mensaje en sql server


slaudos

Chocobomix dijo...

Hola Leandro la verdad agradezco tus aportes son de gran ayuda para iniciar con el aprendizaje. Esta vez me gustaría saber si puedes instruirnos con un tuto que trate de como trabajar con funciones almacenas en otra base de datos como Oracle o MySql y que uno desde C# le pase los paramentos y luego reciba los resultados. No se si me explique bien. Un Saludo des Rep Dom.

Leandro Tuttini dijo...

hola Chocobomix

pero suar mysql u Oracle es muy parecido a sql server porque ado.net que es la base es identico

solo cambian los objetos y como defines los parametros

por ejemplo en lugar de usar SqlConnection, usarias MySqlConnection u OracleConnection

http://social.msdn.microsoft.com/Forums/en-US/netfxwebes/thread/48867ed7-bad2-4c14-835f-3366d0ac2395

para le parametro en lugar de usar @, seria ? para mysql y : para Oracle
pero el resto es igual

saludos