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:
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
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.
¡Muchas gracias por el aporte!
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
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
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
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
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
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
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!
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
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..
hola Juan Manue
cuando 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.
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 )
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);}]
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
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.
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
Muy bueno el tutorial. me da un error en el uso de la transaccion. me podria explicar como hace referencia a ella.
Hola leandro tengo un problemita me da un error porque no se como hacer referencia a la transaccion.
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
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?)
Estoy trabajando la aplicacion en C#.
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
Lo hice agregando esa referencia al proyecto. pero me aparecen mas errores. lo revisare bien. gracias
Me pregunto si puedo usar una base de dato local.Database1.sdf
porque 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.
y no me funciona muy bien.
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
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.
Ok. entendi la usare. gracias por la sugerencias.
Hola leandro me funciono.
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
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
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
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
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...
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
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??
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
deja lo pruebo y te digo como me fue...gracias
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??
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
ya quedo leandro muchas gracias.. lo probe de las dos maneras y me resulto util...gracias
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)
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
ok..gracias leandro,,
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
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
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.
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
Publicar un comentario en la entrada