viernes, 10 de agosto de 2012

Resetear una Columna Identidad (Identity) de SQL Server 2008

En muchos de los Proyectos que he realizado siempre nos hemos encontrado con el Escenario en que para algunas tablas las Claves primarias son Enteras y con la caracteristica de ser una Identity pero el problema surgia en que al eliminar todos los registros de la tabla la columna Identidad guardaba el ultimo valor entero generado y se perdia la secuencia de la misma.

Para los Beginners una columna Identity (Identidad) tiene la caracteristica de tomar valores autogenerados.

Por ejemplo.

Creo esta tabla Alumnos en mi BD SQL Server.

CREATE TABLE Alumnos
(
codigo INT PRIMARY KEY IDENTITY(1,1),
nombre VARCHAR(30)
)

Como veran el campo codigo es una columna Identidad (Identity) que iniciara con valor 1 y se ira incrementando por cada Insert sobre esta tabla en 1.

Los valores dentro de los parentesis pueden ser modificables como por ejemplo quiero que comienze en 100 y se vaya incrementando en 50 entonces quedaria de la siguiente manera

CREATE TABLE Alumnos
(
codigo INT PRIMARY KEY IDENTITY(100,50),
nombre VARCHAR(30)
)

una vez creada la tabla ingresamos 4 registros y consultamos la tabla.

INSERT INTO ALUMNOS (nombre)VALUES('Cristina')
INSERT INTO ALUMNOS (nombre)VALUES('Nicole')
INSERT INTO ALUMNOS (nombre)VALUES('Jhon')
INSERT INTO ALUMNOS (nombre)VALUES('Angel')




como observaran una columna identidad ya no participa de la sentencia insert ya que es un codigo Autogenerado, y como apreciaran el codigo inicia con el valor de 100 y se va incrementando de 50 en 50.

ahora haremos el siguiente escenario. que pasaria si elimino todos los registros de la tabla y inserto un nuevo registro.

DELETE ALUMNOS
go

INSERT INTO ALUMNOS (nombre)VALUES('Royser')

ahora consultamos la tabla nuevamente, y veamos algunas observaciones.



Observamos que una vez despues de eliminar todos los registros de la tabla Alumnos se perdie la secuencia y mas aun de iniciar en 100 como esta en la definicion cuando creamos la tabla.

Bueno para solucionar este problema tenemos que hacer el uso del siguiente comando.

DBCC CHECKIDENT (NOMBRE_TABLA, RESEED, VALOR_IDENTIDAD)
GO

hago un parentisis "(" y quiero entrar a detallar un poco la utilidad de este comando DBCC CHECKIDENT

CHECKIDENT comprueba la identidad actual de la tabla especificada y parte de su funcionalidad es cambiar el valor de la identidad, en pocas palabras modificar manualmente el valor de Identidad Actual de la columna.

la sintaxis es la siguiente:

DBCC CHECKIDENT 
( 
        NOMBRE_TABLA
        [ , { NORESEED | { RESEED [ , NUEVO_VALOR_IDENTIDAD ] } } ]
)

[ WITH NO_INFOMSGS ]


NOMBRE_TABLA: Es el nombre de la tabla del cual se va a ser la verificacion del valor de la columna Identidad.

Esta tabla debe contener una columna Identidad sino esta demas de hacer la comprobacion.

por ejemplo ejecuto lo siguiente:

DBCC CHECKIDENT(ALUMNOS)

Resultado
----------

Checking identity information: current identity value '250', current column value '250'.
DBCC execution completed. If DBCC printed error messages, contact your system administrator.

Como observaran la ionformacion de la verificacion muestra que el valor actual de la identidad es de 250 y el actual valor de la columna es 250. por lo tanto no se ha hecho algun cambio manual para que varie el valor de la columna actual.

hagamos lo siguiente: ejecutar el comando y modificar manualmente el valor de la identidad para que inicie 500 (si deseo que el siguiente registro comienze en 500 deberia iniciarlo en 450)

DBCC CHECKIDENT (ALUMNOS, RESEED, 450)
GO

Tenemos el siguiente mensaje:

Checking identity information: current identity value '250', current column value '450'.
DBCC execution completed. If DBCC printed error messages, contact your system administrator.

Ahora si notamos la diferencia, el actual valor de la columna Identidad es 250 pero si hago un nuevo INSERT tomara el valor actual de la columna identidad que es 450 + 50 el incremento = 500 = Codigo Identidad.

Comprobemos

INSERT INTO ALUMNOS (nombre)VALUES('Royser')

codigo      nombre
----------- ------------------------------
100         Cristina
150         Nicole
200         Jhon
250         Angel
500         Royser

(5 row(s) affected)
NORESEED: Indica que el valor de la identidad en este caso la columna no debe cambiar.

RESEED: Indica que el valor de la identidad en este caso la columna no si debe cambiar.

NUEVO_VALOR_IDENTIDAD: Es el nuevo valor de identidad actual.
WITH NO_INFOMSGS
 : Suprime los mensajes de informacion.

Ahora volviendo al ejercicio propuesto se debera ejecutar lo siguiente:

DBCC CHECKIDENT(ALUMNOS, reseed, 50)

ahora se preguntaran porque iniciamos en 50 y porque no en 100. Es muy simple porque si iniciamos en 100 el primer insert que se haga en la tabla hara que se incremente en 50 osea por consecuencia el primer registro tendra el codigo 100 + 50 = 150.

Es por eso que inicio con 50 para que el primer registro tenga el codigo iniciado en 50 + 50 del incremento = 100 y asi siga la secuencia inicialmente.

Antes de ejecutar el comando DBCC CHECKIDENT Eliminamos nuevamernte todos los registros y luego ejecutamos el comando DBBC CHECKIDENT y luego hacemos un nuevo Insert de los valores Iniciales

DELETE ALUMNOS
GO

DBCC CHECKIDENT (ALUMNOS, RESEED, 50)
GO

INSERT INTO ALUMNOS (nombre)VALUES('Cristina')
INSERT INTO ALUMNOS (nombre)VALUES('Nicole')
INSERT INTO ALUMNOS (nombre)VALUES('Jhon')
INSERT INTO ALUMNOS (nombre)VALUES('Angel')

hacemos un nuevo select a la tabla de alumnos y veamos el siguiente resultado.

jueves, 9 de agosto de 2012

el proceso no puede obtener acceso al archivo porque está siendo utilizado en otro proceso


el proceso no puede obtener acceso al archivo porque está siendo utilizado en otro proceso vb.net
el proceso no puede obtener acceso al archivo porque está siendo utilizado en otro proceso vb.net
La función que adjunta el fichero, lo deja abierto. No se porque, pero es así.
La idea general es crear el fichero en memoria y adjuntar ese fichero en memoria con el nombre que quieras.
Esta clase lo hace:
Imports System.Net.Mail
Imports System.Text
Imports System.Net
Public Class FrmEmail
Public Shared Function SendEMail(ByVal strOrigen As String, ByVal strDestinatario As String, ByVal strAsunto As String, ByVal strMsg As String, ByVal usuario As String, ByVal Clave As String, ByVal smtp As String, ByVal Adjunto As String) As Boolean
Dim msg As New MailMessage()
msg.[To].Add(New MailAddress(strDestinatario))
msg.From = New MailAddress(strOrigen)
msg.Subject = strAsunto
msg.Body = strMsg
‘Adjuntar fichero. No se puede ajuntar el fichero tal cual, pues se queda bloqueado.
Dim contentAsBytes As Byte() = Encoding.UTF8.GetBytes(Adjunto)
Dim memStream As System.IO.MemoryStream = New System.IO.MemoryStream(contentAsBytes)
Dim streamWriter As System.IO.StreamWriter = New System.IO.StreamWriter(memStream)
streamWriter.Flush()
memStream.Position = 0
Dim thisAttachment As Attachment = New Attachment(memStream, vbNull) ‘ “image/jpeg”)
Dim F As Long
Dim FileNameAdjunto As String
F = InStrRev(Adjunto, “\”)
If F = 0 Then
FileNameAdjunto = Adjunto
Else
FileNameAdjunto = Adjunto.Substring(F)
End If
thisAttachment.ContentDisposition.FileName = FileNameAdjunto
msg.Attachments.Add(thisAttachment)
‘msg.Attachments.GetEnumerator()
Dim clienteSmtp As New SmtpClient(smtp)
clienteSmtp.Credentials = New NetworkCredential(usuario, Clave)
Try
clienteSmtp.Send(msg)
Return True
Catch ex As Exception
MsgBox(ex.Message)
Return False
End Try
End Function
End Class

sábado, 4 de agosto de 2012

No se pudo inicializar Visual Basic para las aplicaciones; las ecuaciones y las macros no funcionaran correctamente. ¿Tiene poco espacio de disco disponible? – Solidworks


Copiar  MSO9INTL.DLL  en la carpeta de instalación de Solidworks ; la MSO9INTL.DLL buscar  en el disco C:

Si no funciona cambia el nombre de la carpeta VBA que esta en la ruta C:\Archivos de programa\Archivos comunes\Microsoft Shared\VBA a cualquier otro nombre por ejemplo m  VBA_prueba

miércoles, 1 de agosto de 2012

Crystal Reports – Cargar imagen usando una capa de reportes


Introducción
En esta oportunidad se profundizara el trabajo de imágenes pero apuntando a Reportes, concretamente con el uso de Crystal Reports.
Para este artículo se continua con el ejemplo de uno previo:
En el anterior se listaba y editaba los datos del empleado, incluida sus fotografías, en cambio en este artículo se vera como listar en un reporte esta misma información.
El resultado del reporte final del reporte seria:
Se analizara además como incluir las imágenes provenientes de una base de datos, y también un logo tomado de un archivo de imagen.
Capa de Reportes
Esto quizás aplique un poco mejor con una arquitectura en capas, pero en este caso aunque no las haya definido del todo, se puede separa en un proyecto concreto la responsabilidad de crear los reportes.
Es por eso que se observara en la solución un proyecto de nombre ReportsLayer, este será el encargado de:
- Encapsular la diseño del reporte
- Definición y estructura de datos que requieres el reporte, en este caso implementada en dataset tipados
- La carga de la información, conectándose para ello directamente a los datos, esta capa no hará uso del DataAccess, porque al usar dataset la carga de datos se torna particular, por lo tanto su funcionalidad requiere una conexión directa.
La idea con esto es separar funcionalidad y además cubrir un defecto que tiene Crystal Reports, en donde el diseñador solo toma como entidades objetos que estén local al proyecto donde se encuentra el rpt. Muchas veces poner en la Presentación un reporte implicaría además poner allí mismo los dataset tipados, lo cual ensucia el modelo.
Definición y carga de datos en el DataSet
Para este reporte se definió un dataset tipado con dos datatable en su interior.
Para al carga de los empleados se hará uso de la funcionalidad de la clase EmpleadosDAL definida dentro del propio proyecto de Reportes.
01.internal static class EmpleadosDAL
02.{
03. 
04.internal static Empleados ObtenerTodos()
05.{
06.Empleados empleados = new Empleados();
07. 
08.using (SqlConnection conn = newSqlConnection(ConfigurationManager.ConnectionStrings["default"].ToString()))
09.{
10.conn.Open();
11. 
12.string query = @"SELECT IdEmpleado, Nombre, Apellido, FechaNacimiento, EstadoCivil, Imagen
13.FROM Empleados";
14. 
15.SqlCommand cmd = new SqlCommand(query, conn);
16. 
17.SqlDataAdapter da = new SqlDataAdapter(cmd);
18.//es necesario indicar la tabla del dataset que se quiere cargar
19.da.Fill(empleados, "Empleados");
20. 
21.}
22. 
23.return empleados;
24.}
25.}
Allí se define la consulta en donde las columnas coinciden en nombre con los definidos en timepo de diseño en el dataset tipado, además vale aclarar que la clase ha sido declarada como internal de forma intencional, para que solo la capa de Reportes pueda usar esta funcionalidad, es mas la idea es que se limite al máximo el acceso a funcionalidad que solo esta clase debería utilizar, por eso el dataset tipado también tiene el modificador de acceso asignado a internal.
Un punto adicional es la carga de una imagen externa que representa el logo de la empresa, el mismo no se encuentra en la db sino que es un archivo, es por eso que luego de cargar el reporte se observan las líneas:
1.Empleados.EmpresaRow row = empleado.Empresa.NewEmpresaRow();
2.row.Logo = ImageHelper.ImageToByteArray(ImageHelper.ObtenerImagenLogoEmpresa());
3.empleado.Empresa.Rows.Add(row);
encargadas justamente de crear una row en al datatable con la imagen del logo. En este caso se hace uso de la funcionalidad del Helper de Imágenes creado para tomar la imagen embebida como recurso.
Retorno del Reporte
Esta cada de reportes solo debería ser accedida por medio de la clase Reports con su metodo ObtenerReporteEmpleados() este devolverá la instancia del reporte con la información asignada lista para ser mostrada en pantalla, o exportada si es necesario.
01.public static class Reports
02.{
03. 
04.public static ReporteEmpleados ObtenerReporteEmpleados()
05.{
06.ReporteEmpleados report = new ReporteEmpleados();
07. 
08.//
09.// Se obtienen los datos de la lista de empleados
10.//
11.Empleados empleado = EmpleadosDAL.ObtenerTodos();
12. 
13.//
14.// Se agrega el logo de la empresa a la informacion del listado
15.//
16.Empleados.EmpresaRow row = empleado.Empresa.NewEmpresaRow();
17.row.Logo = ImageHelper.ImageToByteArray(ImageHelper.ObtenerImagenLogoEmpresa());
18.empleado.Empresa.Rows.Add(row);
19. 
20.//
21.// Se asigna los datos a la instancia del reporte
22.//
23.report.SetDataSource(empleado);
24. 
25.return report;
26.}
27. 
28.}
Lanzar el Reporte en al Presentación
El ultimo punto por tratar es como se usara lo anteriormente explicado desde la presentación.
Por un lado contamos con un formulario especialmente creado para desplegar el reporte, el mismo solo cuanta con el CrystalReportViewer y recibe por parámetro la instancia del reporte que debe mostrar.
01.public partial class Reporte : Form
02.{
03.private ReportClass _report = null;
04. 
05.public Reporte()
06.{
07.InitializeComponent();
08.}
09. 
10.public Reporte(ReportClass report)
11.:this()
12.{
13._report = report;
14.}
15. 
16.private void Reporte_Load(object sender, EventArgs e)
17.{
18.crystalReportViewer1.ReportSource = _report;
19.}
20.}
Se define un nuevo constructor del formulario para pasar la instancia del reporte al formulario y es en el Load del mismo que se asigna al Viewer, para desplegar el reporte en pantalla.
Por otro lado tenemos un botón en la pantalla de ListaEmpleados, el cual recupera el reporte, con los datos asignados, y se la pasa a la instancia del formulario para que la muestre en pantalla.
01.private void btnListar_Click(object sender, EventArgs e)
02.{
03. 
04.ReporteEmpleados report = Reports.ObtenerReporteEmpleados();
05. 
06.Reporte frmReporte = new Reporte(report);
07.frmReporte.Show();
08. 
09.}

Código de ejemplo
La base de datos utilizada en el ejemplo es la Sql Server Express 2008 R2, como ver en la solución el mdf esta integrado al Visual Studio, por lo tanto con solo tener el sql server express instado esta debería funciona adjuntándose sola al servicio.
En la carpeta “script” del proyecto “DataAccess”  se encuentra un archivo .sql con las instrucciones para crear la estructura de tablas y datos que se requieren para este articulo.

[C#]
[VB.NET]