lunes, 4 de noviembre de 2013

Serializar un objeto .net

Serializar un objeto


1. Objeto a xml


Private Function CrearXMLDesdeObj(ByVal objClase As MiClase) As XmlDocument
Dim oXmlDocument As XmlDocument = New XmlDocument()
Dim oXmlSerializer As XmlSerializer = New XmlSerializer(objClase.GetType())
Dim oStringBuilder As StringBuilder = New StringBuilder()
Dim oStringWriter As StringWriterWithEncoding = New StringWriterWithEncoding(oStringBuilder, Encoding.UTF8)
oXmlSerializer.Serialize(oStringWriter, objClase )
oXmlDocument.LoadXml(oStringBuilder.ToString())
Return oXmlDocument
End Function

2. Quitar elemento vacíos


Serializar si no se quieren cosas raras, es muy fácil. El problema es cuando por ejemplo no quieren que salga la etiqueta cuando el valor es null, y por supuesto no se quiere poner la propiedad nulable. Aqui una funcion que borra esos elmentos.

Cuando se pone IsNullable = true si el valor es null se serializa


Public Function EliminarNullElementsFromXML(ByVal oXmlDocument As XmlDocument) As XmlDocument
Dim oXmlDeclaration As XmlDeclaration = CType(oXmlDocument.FirstChild, XmlDeclaration)
Dim oXDocument As XDocument = XDocument.Parse(oXmlDocument.OuterXml)
Dim oXElements As IEnumerable(Of XElement) = oXDocument.Descendants()
Dim oXElementsDelete As List(Of XElement) = New List(Of XElement)
Dim elem_list As IEnumerable(Of XElement) = From elem In oXElements Select elem
For Each element As XElement In elem_list
If (element.IsEmpty) Then
oXElementsDelete.Add(element)
End If
Next
oXElementsDelete.Remove()
oXmlDocument.LoadXml(oXDocument.ToString(SaveOptions.DisableFormatting))
oXmlDocument.InsertBefore(oXmlDeclaration, oXmlDocument.FirstChild)
Return oXmlDocument
End Function


3. Serializar Bool que pueden ser null

[Serializable]
public class Foo
{
[XmlIgnore]
public bool? Bar { get; set; }

[XmlAttribute("Bar")]
[EditorBrowsable(EditorBrowsableState.Never)]
public string xmlBar
{
get { return Bar.ToString(); }
set
{
if (string.IsNullOrEmpty(value)) Bar = null;
else Bar = bool.Parse(value);
}
}
}

4. Quitar los elementos vacios.

La funcion CleanEmptyTags, quita los elementos vacios , si la propiedad es un string tendrias varios

Public Class UtilXml

Public Shared Function Serialize(ByVal objectToSerialize As Object) As String
Dim oXmlDocument As XmlDocument = New XmlDocument()
Dim oXmlSerializer As XmlSerializer = New XmlSerializer(objectToSerialize.GetType())
Dim oStringBuilder As StringBuilder = New StringBuilder()
Dim oStringWriter As StringWriterWithEncoding = New StringWriterWithEncoding(oStringBuilder, Encoding.UTF8)
oXmlSerializer.Serialize(oStringWriter, objectToSerialize)
Return (CleanEmptyTags(oStringWriter.GetStringBuilder().ToString()))
End Function


Private Shared Function CleanEmptyTags(ByVal xml As String) As String
Dim regex As RegularExpressions.Regex = New RegularExpressions.Regex("(\s)*<(\w)*(\s)*/>")
Return regex.Replace(xml, String.Empty)
Return xml
End Function
End Class

martes, 29 de octubre de 2013

Sql Server int / int = ?

Esto es parecido a otra entrada de la sum

Tenemos lo siguiente

declare @p1 as integer
declare @p2 as integer
set @p1 = 12
set @p2 =10000
print @p1/@p2
Salida = 0

declare @p1 as integer
declare @p2 as decimal(16,2)
set @p1 = 12
set @p2 =10000
print @p1/@p2

Salida = 0.00120000000000000

Alguno estará diciendo que es obvio, pero os puedo asegurar que no siempre lo es.

martes, 17 de septiembre de 2013

Google nos hizo vagos….


Ya nadie se preocupa de escribir bien en un campo de búsqueda, sabemos que google lo hara.


Es difícil explicar al cliente que no somos Google y que eso lleva más trabajo de lo que cree, por muy fácil que Google lo haga.

Bueno pongamos a ello, vamos a darle al cliente lo que quiere (bueno más o menos)
  1. Saber cómo funciona las búsquedas, algo más que "like "
  2. Realizar búsquedas de palabras fonéticamente similares Soundex ()
  3. freetext 
  4. Problemas del freetext
  5. Si no estamos hablando de algo sencillo como un directorio de personas, no descartes el Lucene lo hay en .net buen artículo en dotnetslackers.com en
Con esto podrías intentar luego hacer las búsquedas para usuarios "vagos" (todos).

domingo, 1 de septiembre de 2013

Sum() & Sql Server

Curioseando, esto pasa en SqlServer no sé si con otros pasa (puedo asegurar que con Informix no pasa)

Ejemplo
SELECT SUM(precio) FROM libros

Error ->Mens. 8115, Nivel 16, Estado 2, Línea 1
Error de desbordamiento aritmético al convertir expresión al tipo de datos int.


Explicación

sum(column1) throws "Arithmetic overflow error"
sum() adopts the data type of the column type, so cast the column within the sum expression:

Genial usa el tipo de la columna, que en mi caso era un int


Solución
SELECT SUM( cast ( precio as bigint) ) FROM libros


martes, 27 de agosto de 2013

ADODB.Recordset está cerrado después de llamar a un sp

La chorrada de hoy me ha llevado, más de 3horas.

Situación:
- Un procedimiento que funciona en el SQL Server Management, con parámetros de entrada y recordset de salida. El SP hacia un insert y retornaba un recordset (hacia más cosas, esto es para resumir :P)
- Código Vb6, cuando ejecutaba el código y quería ver el contenido del recordset, el VB6 me daba un error de “La operación no está permitida si el objeto está cerrado.”
- El SP se había ejecutado porque tenía un registro más.

Di por supuesto que el problema estaba en como hacia la llamada al SP desde Vb6 (el SP funcionaba bien, se había ejecutado). Después de hacer un poco de I (sin +D porque desarrollo cero) pregunte a expertos en VB6 mayores que yo y nada.

La solución fue:
Añadiren el SP SET NOCOUNT ON al comienzo del código y SET NOCOUNT OFF justo antes de devolver los resultados finales

Si el procedimiento envía varios mensajes al cliente, el resulset no los procesa adecuadamente y suele dar ese mensaje de error.

La solución gracias a:
http://microsoft.public.es.sqlserver.narkive.com/fNVcXJuO/modo-correcto-de-ejecutar-sp

Por si alguno le interesa:
Llamar con command

A mí me valía con

Dim cSql as String
Dim cSql = " execute miProceimiento ‘parametro1’, ‘parametro2’ "
Set rst = New ADODB.Recordset
rst.Open cSql, gConexion, adOpenForwardOnly, adLockReadOnly
If Not rst.EOF Then

End if

miércoles, 14 de agosto de 2013

Sql, curioseando

Unas utilidades para conocer la estructura de una base de datos de sqlserver ,

Obtener las bases de datos de un servidor

select name as descripcion
from sys.databases
WHERE name not like 'EXTRA%'

Obtener todas las tablas o vistas de un servidor

select name, object_id ,create_date from sys.tables order by name
select name , object_id ,create_date from sys.views order by name

Obtener todas las columnas de un todas las tablas o vistas de una bbdd

select t.object_id, c.name,system_type_id, max_length, c.precision , scale ,is_nullable, c.column_id from sys.columns c ,
sys.tables t where t.object_id= c.object_id
select t.object_id, c.name,system_type_id, max_length, c.precision , scale ,is_nullable,c.column_id from sys.columns c ,
sys.views t where t.object_id= c.object_id
order by t.OBJECT_ID, c.column_id

Obtener las columas que forman parte de todos los índices de una base de datos

select t.TABLE_NAME, COLUMN_NAME from INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE as c
inner join INFORMATION_SCHEMA.TABLE_CONSTRAINTS as t on
t.CONSTRAINT_NAME = c.CONSTRAINT_NAME
where t.CONSTRAINT_TYPE='PRIMARY KEY'
order by TABLE_NAME

Obtener los trigger de una bbdd

select t.object_id, name, e.type, e.type_desc, t.parent_id
from sys.triggers t, sys.trigger_events e
where t.type ='TR' and t.object_id=e.object_id

Obtener todos los SP , funciones de una bbdd

select name, object_id from sys.objects where type in ('FN' ,'P', 'TF', '[IF]') order by name
Obtener todos los parámetros de entrada de todos los sp de una base de datos
SELECT SPECIFIC_NAME, PARAMETER_NAME, data_type, CHARACTER_MAXIMUM_LENGTH , NUMERIC_PRECISION, NUMERIC_SCALE, max_length , is_nullable
FROM INFORMATION_SCHEMA.PARAMETERS, sys.types
WHERE PARAMETER_MODE='IN' and INFORMATION_SCHEMA.PARAMETERS.DATA_TYPE =sys.types.name
ORDER BY SPECIFIC_NAME, ORDINAL_POSITION

Buscar texto dentro de un procedimiento o función de sql

-- @texto -->texto a buscar
--@BBDD --> nombre de la base de datos, si no se pasa ninguno busca en todas las bases de datos del servidor
Create PROCEDURE [dbo].[Find_Text](@texto varchar(50), @BBDD varchar(75)= null) as
begin

declare @ssQL nvarchar(max)
declare @nombreBBDD varchar(75)
declare @sWhere varchar(max)
declare @texo_find varchar(75)
declare @index_Final int
declare @tablaSP table
(
name_SP varchar(500),
name_BBDD varchar(500),
Tipo varchar(10),
ID INT
)
declare @tablaBasesDatos table
(
nombreBBDD varchar(75)
)

INSERT INTO @tablaBasesDatos
select db.name from sys.databases db WHERE DB.name = ISNULL(@BBDD,DB.NAME) and DB.name not in('msdb','DatabaseControl','master','model','tempdb')
order by db.database_id

set @sWhere = 'where '
if CHARINDEX(',',@texto)=0
begin
set @sWhere = @sWhere+ ' sc.text like ''%'+@texto+'%'' '
end
else
begin
WHILE CHARINDEX(',',@texto,0) > 0
begin
Set @index_Final= CHARINDEX(',',@texto,0)+1
set @texo_find= substring(@texto,0, @index_Final)
set @texto= REPLACE(@texto,@texo_find,'')
set @sWhere = @sWhere+ ' sc.text like ''%'+ ltrim(rtrim(replace( @texo_find,',',''))) +'%'' OR '
end
if @texto<> ''
BEGIN
set @sWhere = @sWhere+ ' sc.text like ''%'+ ltrim(rtrim( @texto)) +'%'' '
END
end

WHILE EXISTS (SELECT * FROM @tablaBasesDatos)
BEGIN

SELECT top 1 @nombreBBDD = nombreBBDD FROM @tablaBasesDatos

set @ssQL='select DISTINCT so.name ,'''+@nombreBBDD+''', so.XTYPE, sc.ID
from ' + @nombreBBDD + '..sysobjects so inner join ' + @nombreBBDD + '..syscomments sc on so.id = sc.id AND sc.encrypted= 0 '+ @sWhere

BEGIN TRY
INSERT INTO @tablaSP
execute sp_executesql @ssQL
END TRY
BEGIN CATCH
END CATCH;
DELETE TOP (1) @tablaBasesDatos
END

SELECT name_SP,
CASE WHEN Tipo='FN' THEN 'F. ESCALAR'
WHEN Tipo='P' THEN 'PROCEDIMIENTO'
WHEN Tipo='TF' THEN 'F. TABLA' ELSE Tipo END as tipo
,name_BBDD , ID FROM @tablaSP order by name_BBDD,Tipo,name_SP

end

Ojo cuidado cuando se usa el syscomments !!!! Ejemplo de un "problemilla"

Creamos un procedimiento : nombre1 ( esto aparece en el syscomments sin problemas)
Cambiamos el nombre del procedimiento: pulsando sobre el nombre,boton derecho "Cambiar Nombre" y ponemos el nuevo nombre por "NEW _NOMBRE"
Ahora mira el syscomments !!! pues si en el syscomments en el texto del SP está el nombre viejo.
Está documentado http://support.microsoft.com/kb/243198
"Al cambiar el nombre de un procedimiento almacenado, una vista o un desencadenador no se actualiza la tabla SYSCOMMENTS"

domingo, 26 de mayo de 2013

Variables de SQL Server

Para controlar el anidamiento de SQL server podemos usar la variable
@@NESTLEVEL

MSDN
Ejemplo de uso, muy útil Ejemplo