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] |
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
ResponderEliminarHola 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.
ResponderEliminar¡Muchas gracias por el aporte!
ResponderEliminarMagnifico POST
ResponderEliminarTe 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
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?
ResponderEliminarMuchas gracias
Andres Mauricio
hola Mauro
ResponderEliminarParticularmente 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
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.
ResponderEliminarGracias
hola Fernando
ResponderEliminarLo 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
hola leandro
ResponderEliminares 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
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.
ResponderEliminarGracias!
Grilla le llamamos tambien al Datagridview
Eliminarhola Jesus
ResponderEliminarluego 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
ResponderEliminarHola, 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..
hola Juan Manue
ResponderEliminarcuando defines la columna asigna la propiedad ReadOnly en true
esto hara que esa columna no se pueda editar
saludos
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.
ResponderEliminarHola 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=?
ResponderEliminar(
Editar )
Y en la parte programtiva se realiza de la siguiente forma o como se realiza=?
ResponderEliminar[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);}]
hola Jenyna ramos
ResponderEliminarpero 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
Hola, muchas gracias por los ejemplos
ResponderEliminarme 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.
hola henry.iq
ResponderEliminarveo 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
Muy bueno el tutorial. me da un error en el uso de la transaccion. me podria explicar como hace referencia a ella.
ResponderEliminarHola leandro tengo un problemita me da un error porque no se como hacer referencia a la transaccion.
ResponderEliminarhola Rosa
ResponderEliminarcuals 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
ResponderEliminarEste es el mensaje:
Error 1 The type or namespace name 'Transactions' does not exist in the namespace 'System' (are you missing an assembly reference?)
Estoy trabajando la aplicacion en C#.
ResponderEliminarhola Rosa
ResponderEliminarhas agregado la referencia a System.Transaction ?
porque recuerda que sino agregas esa referncia en el proyecto la clase de transaccion no puedes usarla
saludos
Lo hice agregando esa referencia al proyecto. pero me aparecen mas errores. lo revisare bien. gracias
ResponderEliminarMe pregunto si puedo usar una base de dato local.Database1.sdf
ResponderEliminarporque estoy haciendo una aplicacion, y esa es la que estoy usando.
necesito buscar dato de una tabla en la base de dato a traves de un DataGridView.
ResponderEliminary no me funciona muy bien.
hola Rosa
ResponderEliminarpero 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
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.
ResponderEliminary el uso de la base de dato local es otra aplicacion que estoy haciendo, que me pidieron en la universidad.
Ok. entendi la usare. gracias por la sugerencias.
ResponderEliminarHola leandro me funciono.
ResponderEliminarHola Leandro
ResponderEliminarTengo 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
hola Alexander
ResponderEliminarque 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
hola leandro tengo una duda---
ResponderEliminarquiero 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
hola germanRive
ResponderEliminaresta 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
mira leandro... lo clase(archivo) esta en el mismo proyecto web..
ResponderEliminarporque 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...
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 ...
ResponderEliminarImports 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
leandro otra vez aki preguntado....
ResponderEliminarcomo 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??
hola
ResponderEliminarlo 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
deja lo pruebo y te digo como me fue...gracias
ResponderEliminarleandro 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??
ResponderEliminarhola
ResponderEliminarpodrias 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
ya quedo leandro muchas gracias.. lo probe de las dos maneras y me resulto util...gracias
ResponderEliminarhola 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)
ResponderEliminarhola german
ResponderEliminarno 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
ok..gracias leandro,,
ResponderEliminarhola Leandro.
ResponderEliminaruna 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
hola Eder
ResponderEliminareste 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
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.
ResponderEliminarhola Chocobomix
ResponderEliminarpero 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
Hola leandro una pregunta para tener claro el concepto respecto de ntier con base datos chinook, si yo quisiera hacer un calculo algo complejo como descuentos para ciertos productos, o por cierto monto de compra, estos cálculos yo los debería poner en la capa de negocios o business layer?
ResponderEliminarhola Unknown
ResponderEliminarexacto, asi es
los calculos o todo lo que tenga que ver con logica que pueda o no trabajar con una determinada entidad del dominio se realiza en el negocio
ahora bien, recuerda que si este es un simple calculo para mostrar al usuario un dato informativo como se la suma de los montos de compra, esto se pdoria realizar en la presentacion
pero el calculo de iva de este se realiza en la capa de negocio, porque calcular el iva requiere de un proceso que puede ahcer variar los porcentajes segun el negocio
saludos
Hola leandro, teniendo en cuenta la solucion conlabase de datos chinook, y quisiera exponer un proyecto de esta solucion como WebService o quizas como WCF, cual seria?
ResponderEliminarhola Unknown
ResponderEliminarno se si entendi el planteo, pero si quieres exponer algo como servicio seria la comunicacion de la capa de UI con la de negocio la que debe desacoplarse
o sea el WCF lo pondrias en medio de estas dos capas, cncretamente lo que haces es poner una capa de servicio remoto que se comunica con el negocio
saludos
yendo a un ejemplo concreto, en el proyecto chinook existe este archivo
ResponderEliminarCustomerBO.cs qeu es usado por el UI
con el codigo mostrado abajo, yo pregunto:
Algo de este archivo deberia convertir a WCF?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Chinook.Entities;
using Chinook.DataAccess;
namespace Chinook.BusinessLayer
{
public static class CustomerBO
{
public static List GetAll()
{
return CustomerDAL.GetAll();
}
public static CustomerEntity GetById(int id)
{
return CustomerDAL.GetById(id);
}
public static CustomerEntity Save(CustomerEntity customer)
{
if (CustomerDAL.Exists(customer.CustomerId))
return CustomerDAL.Update(customer);
else
return CustomerDAL.Create(customer);
}
}
}
hola Unknown
ResponderEliminaren ese archivo en concreto no tocas nada, solo pones en medio una capa de servicio, o sea ya no sera la UI quien acceda a esa clase BO sino que sera WCF y la UI referecniara los servicio expuestos
por supuesto algo que se debera evaluar es como defines los DTO (data transfer object) o sea las clases DataContract, podrian ser las mismas entities o podrian ser clases separadas que conviertes de una a otra usando AutoMapper
saludos
Gracias por el codigo, me sirvio mucho. Saludos
ResponderEliminarHola Leandro otra vez molestandote jeje, favor de indicarme por que cuando trato de usar un SqlConnection estatico que debe ser usado con la sentencia using() la cadena se limpia luego de usarla una vez
ResponderEliminarMe explico mejor: tengo un combobox en el cual listo los clientes(todo ok), seguidamente con el item seleccionado llamo un procedimiento que me listara sus facturas en otro combobox pero al llegar a la capa de datos en este segundo procedimiento me indica que la connectionstring no ha sido inicializada!!
Gracias por la atencion y saludos!!
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Configuration;
using System.Data.SqlClient;
using Entidad;
namespace Datos
{
public class clsdatos
{
static SqlConnection cn = new SqlConnection(
ConfigurationManager.ConnectionStrings["conex"].ConnectionString.ToString());
public static DataTable d_trab2_listar_clientes()
{
using (cn)
{
using (SqlDataAdapter da = new SqlDataAdapter(
"trab2_listar_clientes", cn))
{
DataTable dt = new DataTable();
da.Fill(dt);
return dt;
}
}
}
public static DataTable d_trab2_listar_facturas(clsentidad objEN)
{
using (cn)
{
using (SqlDataAdapter da = new SqlDataAdapter(
"trab2_listar_facturas " + objEN.codcli, cn))
{
DataTable dt = new DataTable();
da.Fill(dt);
return dt;
}
}
}
}
}
hola Franky
ResponderEliminares que nunca deberias definir el objeto de conexion como static, eso es incorrecto
la cadena de conexion se define cuando las vas a utilizar dentro del bloque using, pero NUNCA como static
si has analizado el ejemplo del articulo veras que no lo implemento de ea forma
ademas crear una clase de Datos, no lo veo una buena idea, defina una capa de datos donde te centras en las entidades del dominio y no en encapsular la logia de los componentes de ado.net
saludos
Hola Leandro, a continuacion detallo mi estructura:
ResponderEliminar1.Capa Datos
1.1 Acceso a datos ADO.NET
2.Capa Negocio
2.1 Logica y algunas validaciones
3.Capa Entidad
3.1 Propiedades de bd
4.Capa Presentacion
4.1 Interfaz
Me gustaria saber si debo modificar esa estructura porque e visto que en algunos casos en una clase encapsulas tanto las propiedades como los metodos de un objeto(tabla).
Lo de la cadena estatica me lo habian dicho por un tema de no estar a cada momento instanciandola, pero de ahora en adelante seguire tus indicaciones pero me agradaria tambien saber cuales son las razones por las cual no debe ser estatica!!
Gracias
Saludos
Ademas e visto que en web algunos no usan mucho la capa de datos, la remplazan con el webservice?? Es una mala practica o no??
ResponderEliminarhola Franky
ResponderEliminarcuando en la entidad de dominio defienen las propiedades y tambien comportamiento con metodos es que programan usando DDD (Domain driven design)
si bien es una buena tecnica puede no ser simple de implementar, ya que debes lograr no acoplar la logica de negocio con al entidad usando para esto interfaces y ademas framework de IoC (inversion of control)
sino usas estos no apliques DDD
las cadenas de conexion no deberian ser estaticas porque se supone que cuando llevas la aplciacion de un entorno a otro ls info de conexion al servidor cambia y la idea es poder aplciar se cambios sin tener que recompilar
no tiene nada que ver el uso servicio con reemplazar la cada de datos, quizas lo que viste es WCF Data Services, o quizas algo de RIA Services, WebAPI, al algo como eso
si bien alli se implementan servicios estos no reemplazas la capa de datos, sino que la hacen remota
quizas hayas visto que desaparecio porque en estos se usa Entity Framework
mala practica no es, yo he implementado apliaciones que definen una capa remota, pero era la capa de negocio no la de datos la que implementa servicio
saludos
Hola tuttini, baje tu ejemplo donde no tienes nada en capas, y usas un using transactions; ese no me sale, me sale error, osea aparece en rojo subrayado.
ResponderEliminarhola CRISTIAN
ResponderEliminarcual es el mensaje de error que recibes?
es al compilar o al ejecutar?
saludos
Hola tuttini, gracias por responder. osea ese using me aparece subrayado en rojo, entonces no lo puedo usar.
ResponderEliminarIgual me pasa en esta linea "using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["default"].ToString()"
Justo donde dice"ConfigurationManager.ConnectionStrings" no me reconoce ese codigo es como si no existiera o si estuviera mal. pero en tu codigo descargado si funciona y esta igual. no entiendo que pasa.
hola CRISTIAN
ResponderEliminarvalidaste que el proyecto tenga al referencia a System.Configuration ?
no solo la referencia tambien tenga definido la linea
using System.Configuration;
porque es alli donde esta definida la clase ConfigurationManager
saludos
Hola tuttini, es que justo hay esta la falla,escribo using system.configuration y lo subraya en rojo. Como si esa referencia no existiera en visual studio.
ResponderEliminarHola tuttini ya he visto como se agrega, no existe esa libreria asi que toca buscarla, ya la tengo pero lo raro es que de igualmodo no me reconoce le comando configurationmanager en C# que puede ser?
ResponderEliminarBuen dia tuttini, tengo una consulta, cuando agrego esto "dgvLineaCompra_SelectedIndexChanged)"
ResponderEliminarAparece como si no existiera, sale subrayado en rojo. sabes que daltaria?
hola CRISTIAN
ResponderEliminareso seria la declaracion de un evento del DataGridView, pero ese evento no existe en el grid, salvo que en una de las columnas las definas como un combobox
la verdad no sabria decirte porque lo marca, pero claramente ese evento en el DataGridView no existe
DataGridView Events
saludos
Hola tuttini, pero se supone que en las líneas de arriba se agrego, según entiendo no se hace nada manual para el combo en el datagridview, si no en el código de arriba es que se creo, lo tengo igual pero me sale subrayado en rojo esa línea. me dice que ese nombre no existe en el contexto actual
ResponderEliminarHola tuttini, revise el archivo que das a descargar y vi que habían partes de código que no habías explicado, entonces solucione el error. ya termine todo el tutorial y me quedo un solo error, te agradezco si me colaboras a solucionarlo para poder continuar con la segunda parte.
ResponderEliminar"cliente", dice que el nombre 'cliente' no existe en el contexto actual.
ese lo usamos en private void btnBuscarCliente_Click(object sender, EventArgs e)
y la verdad no se de donde salio. es el único error que tengo y podría proseguir.
De antemano Gracias
hola
ResponderEliminarsi revisas en el codigo buscando la variable veras que la variable esta definida a nivel del formulario
en la linea
DataRow cliente = null;
saludos
Hola Leandro, es recomendable usar la clase SqlHelper.
ResponderEliminarhola Cleider
ResponderEliminarno se si entendi el planteo de la pregunta
cual seria la responsabilidad de este helper ?
saludos
Este comentario ha sido eliminado por el autor.
ResponderEliminarbuenos dias Leandro,
ResponderEliminarme han servido de mucho tus ejemplos, que buen trabajo haces.
Estoy desarrollando una aplicacion en vb.net 2010, tengo un formulario de búsqueda que es el mismo que utilizo en todas los maestros, cuando se abre la pantalla hago una búsqueda y las presento en un datagridview. Hasta ahí no tengo problemas, pero si en el datagridview escojo cualquier fila que no sea la primera, al dar doble click la presento en el formulario de donde abrí la busqueda, con las cajas de texto no tengo problema al presentar los datos con el registro seleccionado, pero cuando quiero presentar los datos en un combobox que esta en el formulario desde donde hago la búsqueda me presenta los datos del primer registro del datagridview (en realidad lo toma desde el dataset), como puedo solucionar esto?
a las cajas de texto le pongo: txtbox = .Rows(.CurrentCellAddress.Y).Cells("Codigo").Value.
en el combo tengo:
With frm1.combo
.DisplayMember = "nombre"
.ValueMember = "codigo"
.DataSource = dataset
End With
Agradezco tu respuesta,
hola roleve
ResponderEliminarporque veo alli que usas With frm1 ? no es que el grid esta en el mismo forma qe los controles
porque si es asi deberias usar Me y no frm1
para el combo seleccione un valor deberias asignar el codigo que tomarias del grid, pero no se si en elguna columna muestras ese id o solo estas mostrando la descripcion
quizas la entidad en lugar de tomarla del grid deberias realizar una query nueva para tomar todos los datos de la entidad seleccionada en la row del grid
saludos
Muy buen blogg pero tengo una duda como seria el ejemplo anterior si en vez de un combobox fuera un textbox con datos que el usuario agrega como ejemplo el código de barras de un producto y en la siguiente celda aparezca la descripción.
ResponderEliminarhola Unknown
ResponderEliminarAlgo no entendi, dende estarina los checkbox? porque en el ejemplo del articulo no hay ninguno
En este se van completando items en el grid y luego se persiste
Ya sea que vayas agreando de forma manual o por medio de un codigo de barra al final obtienes una lista que debes enviar a la capa de negocio
saludos
Como así?
ResponderEliminarno mira lo que yo quiero hacer es que tengo un DataGridView con las siguientes columnas código descripción Cantidad y precio unitario total lo que quiero es que el usuario en la columna código lo inserte de manera manual ejemplo 01 entonces que automáticamente se llene el campo de descripción ejemplo arroz igual que el de precio unitario y la cantidad también la ingresaría el usuario y el total la multiplicación del precio unitario por la cantidad.
Gracias por tu ayuda es que soy novato en esto.
Hola Leandro, estoy haciendo un programa en 3 capas y quisiera volcar en un combobox las provincias (previamente insertadas en SQLServer 2014)...Tengo una tabla PACIENTES donde uno de sus campos es el cod_prov que esta relacionada con la tabla PROVINCIAS... Cuando yo quiera insertar un paciente y especificarle la provincia de donde es quiero que estas se muestren en un combobox, pero no se como hacerlo! Gracias!
ResponderEliminarAlejandro, que es mas eficiente , utilizar Listas o datatables?
ResponderEliminarMuchas Gracias antemano.
buenas leandro me gusta la forma que hicieron, solo una pregunta si quiero guardar los detalles de la factura en un datatable o lista para luego acceder e ir guardando, el problema seria que como es con transaccion meter el maestro y los varios detalles de la factura en un procedure con transaccion lo de la transacion esta lista solo es como hacer un procedimiento que guarde el maestro y todos los detalles factura gracias
ResponderEliminarpor favor tienes el codigo pero en Visual.net
ResponderEliminarSaludos Maestro, una duda. al abrir el formulario y guardar informacion lo hace todo bien
ResponderEliminarpero cuando abro el formulario ya no esta la informacion de nuevo.
tiene una configuracion especial el proyecto?
hola amigo por favor me puedes ayudar con esto, lo que quiero es como puedo hacer para poner el pie de página el total del monto en letras por página ósea si la pagina 1 tiene 20 debe salirme son veinte y 00/100 soles, la pagina 2 tiene 35 debe salirme treinta y cinco y 00/100 soles, te lo agradeceré mucho gracias , yo sabré reconocer tu trabajo por favor ayúdame gracias, el codigo debe ir implementado en c# en print documento en el area de codigo personalizado en propiedades del informe mi correo es imvt26@gmail.com para enviarte capturas del proyecto que estoy haciendo
ResponderEliminarHola una consulta y si deseas modificar y eliminar como h arias para retornar todos esos datos?
ResponderEliminarGracias
Amigo, como pasar los datos de tu capa presentación del detalle factura (List) a capa de datos.
ResponderEliminar