miércoles, 29 de mayo de 2013
sábado, 25 de mayo de 2013
Una regla de color uniforme, redondeada, con cero y colores desplazados (y 5)
ENUNCIADO
En la entrada (1) de esta serie definimos los objetivos: una regla de color personalizada para un mapa generado mediante Reporting Services que fuera uniforme, redondeada, con cero y colores desplazados
En la entrada (2) vimos como podemos cargar un informe en memoria, para modificarlo y volcarlo en un control ReportViewer.
En la entrada (3) modificamos la definición XML del informe dejando pendientes las funciones que cálculan los rangos y colores.
En la entrada (4) calculamos los intervalo numéricos de la regla.
En está última entrada de la serie veremos como obtener una escala de colores con degradados centrados en el rango que contiene el valor cero.
En nuestro caso para simplificar y porque los colores son los mas intuitivos situaremos el centro en el color amarillo y degradaremos hacia el azulo por los valores negativos y hacia el rojo por los valores positivos.
En mi algoritmo el color amarillo se situará a la derecha o a la izquierda del cero según el número de rangos que haya en cada lado. El objetivo es aminorar la perdida de definición en los colores y visualizar siempre los tres colores, lo que podría no ocurrir si el cero estuviera en primera o última posición.
SOLUCIÓN
El algoritmo rellena la lista en tres pasos:Primero por la parte derecha donde:
- El azul está en máximos (#FF o 255) y decrece hasta casi #00.
- El rojo y el verde crecen desde #00 hasta "casi" #FF
Luego la parte derecha:
- El azul se mantiene en #00
- El rojo que empieza en casi #FF deberá terminar en #FF (aunque puede sufrir una perdida de intensidad intermedia).
- El verde empieza el casi #FF pero termina en #00
- El punto medio donde situamos el amarillo
- Los pasos para cada color en los degradados derecho e izquierdo.
- 'Operar' con colores para generar el string hexadecimal.
'''''' Calcula un rango de colores que empieza en azul, pasa por el verde en el valor cero y termina en el rojo ''' ''' colección de extremos de rango que contiene el valor cero '''devuelve la lista de colores '''Public Function coloresCero(rango As List(Of Double)) As List(Of String) Dim colores As New List(Of String) Dim posicionMedio = rango.IndexOf(0) + 1 If posicionMedio > rango.Count / 2 Then posicionMedio -= 1 Dim cuentaIniciales = posicionMedio - 1 Dim cuentaFinales = rango.Count - posicionMedio Dim pasoInicial = 255 / cuentaIniciales Dim pasoMedio = 255 / (cuentaIniciales + 1) For i = 0 To cuentaIniciales - 1 colores.Add(HexString(pasoMedio * i, pasoMedio * i, 255 - pasoInicial * i)) Next colores.Add(HexString(255, 255, 0)) Dim pasoFinal = 255 / cuentaFinales pasoMedio = 255 / cuentaFinales For i = 1 To cuentaFinales colores.Add(HexString((pasoFinal * i) + (pasoMedio * (cuentaFinales - i)), pasoMedio * (cuentaFinales - i), 0)) Next Return colores End Function Function HexString(r As Integer, g As Integer, b As Integer) As String ' cálcula el string hexadecimal de color RGB Return "#" + hex(r) + hex(g) + hex(b) End Function Function hex(v As Integer) As String ' Obtiene el hexadecimal de un entero con cero por delante si hace falta Dim r = String.Format("{0:x}", v) If r.Length > 1 Then Return r Else Return "0" + r End If End Function
Reconozco que hay un poco de juego fácil en la elección de los colores, pero eran los que me pedían. ¡Más fácil habría sido elegir como central el verde¡.
Una regla de color uniforme, redondeada, con cero y colores desplazados (4)
ENUNCIADO
En la entrada (1) de esta serie definimos los objetivos: una regla de color personalizada para un mapa generado mediante Reporting Services que fuera uniforme, redondeada, con cero y colores desplazadosEn la entrada (2) vimos como podemos cargar un informe en memoria, para modificarlo y volcarlo en un control ReportViewer.
En la entrada (3) modificamos la definición XML del informe dejando pendientes las funciones que cálculan los rangos y colores.
Durante la modificación de las reglas de color del informe utilizamos una función rangosMapa que desde una sentencia MDX devuelve la lista de valores doubles para construir los rangos de la regla.
Dicho de otra forma, los números que vemos aquí:
Recordemos que estos números deben satisfacer las condiciones:
- Cada rango de color debe contener aproximadamente al misma cantidad de países.
- Los extremos de cada rango deben redondearse con ceros a la derecha sin afectar la condición anterior.
- Si los valores inferior y exterior tienen distinto signo dos rangos de color deben tener como extremo el cero, con mínima afección sobre el requisito 1.
SOLUCIÓN
En nuestro ejemplo los datos provederán de un servidor SQL Server Analisys Services por lo que necesitaremos la librería Microsoft.AnalysisServices.adomdClient
Imports Microsoft.AnalysisServices.AdomdClient
Dividimos nuestro trabajo en dos pasos: evaluar la sentencia MDX y calcular los rangos:
Public Function RangosMapa (s As String) As List(Of Double, numRangos as Integer) Dim lista As List(Of Double)= EvaluaMDX(s) Dim Rangos as List (Of Double)=RangosRedondos(lista, NumRangos, 0, True) Return (Rangos) End FunctionEvaluar la expresión MDX y almacenamos los resultados en una lista de doubles no tiene gran secreto:
Public Function EvaluaMDX(s As String) As List(Of Double) Dim lista As New List(Of Double) Dim cn As New AdomdConnection(My.Settings.SSASConexion) cn.Open() Dim cmd As AdomdCommand = cn.CreateCommand() cmd.CommandText = evaluaSentencia(s) Dim lector = cmd.ExecuteReader() Do While lector.Read lista.Add(lector.GetDecimal(4)) 'el valor está en la posición 4 Loop lector.Close() cn.Close() cn.Dispose() Return (lista) End Function
El procedimiento siguiente será crear la lista de rangos.
El truco está en ordenar la lista y luego la recorrerla en saltos con un paso calculado desde el total de valores y numero de intervalos que queramos. En cada salto extremos el valor y lo añadimos a la lista de rangos Obtendríamos así una la lista que satisfacerla la condición 1 más arriba..
Para el caso de que la lista contenga el cero (condición 2), el procedimiento es ligeramente distinto. Trabajamos con dos listas (positivos y negativos) y aplicamos el algoritmo anterior a cada uno de ellos con un numero de rangos en cada lista proporcional a la posición del cero.
Esta lista tendrá valores extraídos de la lista por ejemplo 9457,89. La siguiente tarea será redondear a un múltiplo de 10 sin alterar los valores incluidos en cada rango, por ejemplo 9000,00. pero deberemos hacer evitando afectar el número de valores entre los nuevos extremos.
Veamos el código que quizás sea lo mas simple:
'''''' Desde una lista de valores decimales obtiene otra lista de valores decimales que reparte de forma uniforme los valores. ''' ''' Lista original ''' Número de rangos en los que se repartirá ''' número decimales '''Lista de rangos '''Public Function RangosRedondos(lista As List(Of Double), numRangos As Integer, decimales As Integer, redondear As Boolean) As List(Of Double) Dim rangos As List(Of Double) lista.Sort() 'ordenamos 'obtención del rango variable If lista(0) <= 0 And lista(lista.Count - 1) >= 0 Then 'cuando hay un cero deberá aparecer en un extremo de los rangos rangos = repartoConCero(lista, numRangos) Else rangos = reparto(lista, numRangos) End If 'ahora vamos redondeando sin alterar el reparto If redondear Then rangos = RedondeoLista(rangos, lista, decimales) 'y para terminar nos aseguramos de que los valores extremos esten incluidos rangos(0) -= 1 rangos(rangos.Count - 1) += 1 Return rangos End Function ''' ''' Crea una lista de rangos que contienen aproximandamente la misma cantidad de elementos ''' ''' lista ordenada de números decimales desde donde se genera ''' numero de rangos '''lista con numrangos + 1 valores decimales que definen los rangos '''Private Function reparto(lista As List(Of Double), numRangos As Integer) As List(Of Double) Dim paso As Decimal = lista.Count / numRangos Dim rangos As New List(Of Double) rangos.Add(lista(0)) For i = 1 To numRangos - 1 rangos.Add(lista(Math.Round(i * paso, System.MidpointRounding.AwayFromZero))) Next rangos.Add(lista(lista.Count - 1)) Return rangos End Function ''' ''' Reparte los valores de una lista decimal que cruza el valor cero entre una serie de rangos. ''' Dos rangos compartiran el extremo con valor cero ''' ''' lista ordenada de números decimales desde donde se genera ''' numero de rangos '''lista con numrangos + 1 valores decimales que definen los rangos '''Private Function repartoConCero(lista As List(Of Double), numRangos As Integer) As List(Of Double) Dim Negativos = (From v In lista Where v < 0).ToList Dim Positivos = (From v In lista Where v >= 0).ToList Dim rangoNegativos = reparto(Negativos, numRangos * Negativos.Count / lista.Count) Dim rangoPositivos = reparto(Positivos, numRangos * Positivos.Count / lista.Count) If (-rangoNegativos(rangoNegativos.Count - 1)) < rangoPositivos(0) Then 'el mayor número negativo está mas próximo a cero que el menor positivo rangoNegativos(rangoNegativos.Count - 1) = 0 rangoPositivos.RemoveAt(0) Else rangoPositivos(0) = 0 rangoNegativos.RemoveAt(rangoNegativos.Count - 1) End If Dim resultado As New List(Of Double) resultado.AddRange(rangoNegativos) resultado.AddRange(rangoPositivos) Return resultado End Function ''' ''' Redondea todo lo posible los valores del rango. ''' ''' rango a redondear ''' lista que controla el rango ''' número de decimales a conservar '''''' Private Function RedondeoLista(rangos As List(Of Double), lista As List(Of Double), decimales As Integer) As List(Of Double) For i = 1 To rangos.Count - 2 Dim limiteRango = rangos(i) 'calculamos la diferencia entre el último valor en el rango anterior y el primero del siguiente Dim difSupInf = (From v In lista Where v >= limiteRango).Min - (From v In lista Where v < limiteRango).Max 'calculamos el primer digito no significativo mediante la parte entera del logaritmo en base 10 Dim factorDigitos = 10 ^ Math.Floor(Math.Log10(difSupInf) + decimales - 1) 'le sumamos los decimales (ver linea siguiente) Dim factorDecimales = 10 ^ decimales limiteRango *= factorDecimales 'estamos trasladando los decimales a la parte entera. limiteRango = Math.Floor(limiteRango / factorDigitos) * factorDigitos rangos(i) = limiteRango / factorDecimales Next Return rangos End Function
Antes de terminar llamar la atención sobre dos parámetros que no he comentado:
- decimales: que permite establecer el número de decimales con los que trabajamos en el redondeo.
- redondeo: por si en un momento preferimos obtener la lista sin redondeo.
Una regla de color uniforme, redondeada, con cero y colores desplazados (3)
ENUNCIADO:
En la primera entrada de esta serie presentamos nuestro objetivo: Personalizar la regla de colores en un mapa incluido en un informe SSRS de forma que los valores y colores cumplieran ciertos requisitos.En la segunda entrada vimos cómo podíamos cargar en memoria del cliente la definición XML de un informe SSRS para poder modificarla antes de entregarsela al control ReportViewer.
En esta entrada veremos como modificamos la entrada XML.
SOLUCIÓN:
El código esta comentado:Public Function ParametrizaMapa(informe As XDocument,
s as string,
numRangos as integer) As XDocument ' s contiene una sentencia MDX
' numRangos numero de rangos que deseamos 'Calculamos los rangos de la regla de colores dim rangosMapa As List(Of Double)=rangosMapa(s,numRangos) ' Para escribir menos. Dim df = informe.Root.Name.Namespace Dim capas = informe.Root.Element( df + "ReportSections").Element( df + "ReportSection").Element( df + "Body").Element( df + "ReportItems").Element( df + "Map").Element( df + "MapLayers").Element( df + "MapPolygonLayer") ' comienzan las reglas de color Dim reglasColor As XElement ' borramos cualquier regla existente en el informe original If capa.Element(df + "MapPolygonRules").Element(df + "MapColorRangeRule") IsNot Nothing Then capa.Element(df + "MapPolygonRules").Element(df + "MapColorRangeRule").Remove() end if If capa.Element(df + "MapPolygonRules").Element(df + "MapCustomColorRule") IsNot Nothing Then capa.Element(df + "MapPolygonRules").Element(df + "MapCustomColorRule").Remove() end if 'Vamos a establecer los colores y sus degradados If rangosMapa.Contains(0) Then 'mapa coloreado alrededor del cero 'hay que generar la personalización "MapColorRangeRule" mediante la función coloresCero capa.Element(df + "MapPolygonRules").Add(New XElement(df + "MapCustomColorRule")) reglasColor = capa.Element(df + "MapPolygonRules").Element(df + "MapCustomColorRule") 'Personalizado reglasColor.Add(New XElement(df + "MapCustomColors")) Dim colorines = reglasColor.Element(df + "MapCustomColors") dim colores as list (of String)= coloresCero(rangosMapa) 'obtenemos una lista de colores For Each color In colores colorines.Add(New XElement(df + "MapCustomColor", color)) Next Else 'mapa estándar podemos dejar utilizar la generación automática "MapColorRangeRule" capa.Element(df + "MapPolygonRules").Add(New XElement(df + "MapColorRangeRule")) 'Generado por SSRS reglasColor = capa.Element(df + "MapPolygonRules").Element(df + "MapColorRangeRule") reglasColor.Add(New XElement(df + "StartColor", My.Settings.ReglaColorInicio)) reglasColor.Add(New XElement(df + "MiddleColor", My.Settings.ReglaColorIntermedio)) reglasColor.Add(New XElement(df + "EndColor", My.Settings.reglaColorFin)) End If 'algunas cosas que siempre son necesarias reglasColor.Add(New XElement(df + "ShowInColorScale", "true")) reglasColor.Add(New XElement(df + "DataValue", "=Sum(Fields!Dato.Value)")) reglasColor.Add(New XElement(df + "BucketCount", rangosMapa.count.toString)) reglasColor.Add(New XElement(df + "LegendText", "#FROMVALUE{N0} - #TOVALUE{N0}")) 'Vamos a rellenar los rangos de valores Dim culturaUSA = New System.Globalization.CultureInfo("en-US") If rangosMapa.Count = 2 Then 'solo max y min reglasColor.Add(New XElement(df + "DistributionType", "EqualInterval")) reglasColor.Add(New XElement(df + "StartValue"), rangosMapa(0).ToString(culturaUSA.NumberFormat)) reglasColor.Add(New XElement(df + "EndValue"), rangosMapa(1).ToString(culturaUSA.NumberFormat)) Else 'aquí es donde realmente estamos personalizando con nuestro rango personalizado reglasColor.Add(New XElement(df + "DistributionType", "Custom")) reglasColor.Add(New XElement(df + "MapBuckets")) Dim rangos = reglasColor.Element(df + "MapBuckets") For i = 0 To rangosMapa.Count - 2 rangos.Add(New XElement(df + "MapBucket")) Dim rango = rangos.Elements(df + "MapBucket")(i) rango.Add(New XElement(df + "StartValue", rangosMapa(i).ToString(culturaUSA.NumberFormat))) rango.Add(New XElement(df + "EndValue", (rangosMapa(i + 1)).ToString(culturaUSA.NumberFormat))) Next End If 'Establecemos la sentencia MDX que suministra los datos al informe Dim xdatos = (From f In doc.Root.Element(df + "DataSets").Elements(df + "DataSet")).First( Function(x As XElement) x.Attribute("Name") = "Datos").Element(df + "Query").Element(df + "CommandText") xdatos.SetValue(s) Return informe End Function
Con esto hemos terminado la personalización del informe, nos quedan por definir las dos funciones rangosMapa y coloresCero que presentaremos en la siguientes entradas
Una regla de color uniforme, redondeada, con cero y colores desplazados (2)
ENUNCIADO:
En la primera entrada de esta serie presentamos nuestro objetivo: Personalizar la regla de colores en un mapa incluido en un informe SSRS de forma que los valores y colores cumplieran ciertos requisitos.El diseñador de informes de MS (BIDS) no nos permite definir reglas de color automáticas para nuestros requisitos, deberemos utilizar intervalos y colores personalizados.
Dado que esta personalización depende de los valores de la consulta no puede estar establecida en el informe, y deberemos modificar este cada vez que vayamos a visualizarlo.
En esta entrada vamos a ver como cargar un informe desde el servidor SSRS en nuestro programa cliente, modificar el XML e inyectar el resultado en un control ReportViewer que finalmente solicitará al servidor el procesado.
SOLUCIÓN:
Partimos de que hemos creado una página web que contiene un control ReportViewer llamado Visor y que está configurado para remoteProcessing.Usaremos la libreria ReportingServices2010 nos permite recuperar la definición de un informe mediante los servicios Web de reporting services:
Imports mapasAduanas.Microsoft.SqlServer.ReportingServices2010
El esquema de nuestro proceso será:
Sub Main()
Dim informe as byte()=cargaInforme(nombre as string) 'cargamos el informe
Dim informeXML as system.XML.linq.xDocument= bytesToXML(informe) 'lo pasamos a XML informeXML=parametrizaMapa(informeXML, sentenciaMDX,10) 'Personalizamos e informe informe = XMLtoBytes(InformeXML) 'Volvemos a array de bytes Visor.ServerReport.LoadReportDefinition(informe) 'lo cargamos en el visor de informes. Visor.ServerReport.Refresh() End Sub
Deberemos importar la librería de acceso proxy al SSRS
Imports mapasAduanas.Microsoft.SqlServer.ReportingServices2010
Para poder cargar el informe:
Private Function CargaInforme(nombre As String) As byte() Dim SSRS As New ReportingService2010 SSRS.Credentials = (New MyReportServerCredentials).NetworkCredentials Dim informe = SSRS.GetItemDefinition(My.Settings.pathMapas + nombre) SSRS = Nothing Return doc End Function
Para conectarnos necesitamos suministrar una credenciales utilizando la clase myReportServerCredentials descrita en Autenticación Reporting Services.
La función getItemDefinition y nuetra función CargaInforme nos devuelven una matriz de bytes que deberemos convertir en un documento system.XML.link.xDocument para poder editarla:
Private Function bytesToXML(informe As byte()) As xDocument Dim stream = New System.IO.MemoryStream(informe) Dim lector = System.Xml.XmlReader.Create(stream) Dim doc As XDocument = XDocument.Load(lector, LoadOptions.None) Return doc End Function
Cuando hayamos terminado de personalizar este documento deberemos volverlo a convertir en matriz de bytes.
Private Function XMLBytes(doc As XDocument) As System.IO.Stream Dim stream As New System.IO.MemoryStream Dim escritor = System.Xml.XmlWriter.Create(stream) doc.Save(escritor) escritor.Close() stream.Position = 0 Return stream End Function
En la siguiente entrada editaremos el XML del informe usando linq-to-xml.
viernes, 24 de mayo de 2013
Una regla de color uniforme, redondeada, con cero y colores desplazados (1)
ENUNCIADO
Desde una consulta que puede devolver valores negativos y positivos para todos los países del mundo quieres generar un mapa geográfico cuyos colores reflejen estos valores con una regla de color basada en tres colores: color inferior, color medio y color superior con degradados desde el color medio al inferior y desde el color medio al superior.Hasta aquí todo se puede resolver con los mapas del servidor de informes del Microsoft (SSRS). Pero además deben satisfacerse los siguientes requisitos:
- Uniforme: Cada rango de color debe contener aproximadamente la misma cantidad de países.
- Redondeada: Los extremos de cada rango deben redondearse con ceros a la derecha sin afectar la condición anterior.
- Con cero: Si los valores inferior y exterior tienen distinto signo dos rangos de color deben tener como extremo el cero, con mínima afección sobre el requisito 1.
- Colores desplazados: En caso de visualizarse el cero el color medio se situara en un rango que contenga este valor entre sus extremos.
Fíjate en los números redondeados y en el cero que desplaza el color amarillo a la derecha de la barra.
UN COMENTARIO PREVIO
Este entrada reemplaza a algunas que escribí anteriormente, pero en esta nueva versión he mejorado los algoritmos y voy a redactarlo mas claramente. De hecho dedicaré cuatro entradas a presentar la solución.
Comencemos.
Comencemos.
SOLUCIÓN
El mapa lo visualizaremos mediante un control reportViewer de WindowsForms o de WebForms configurado para informes de servidor SSRS (remote processing).
Aunque partiremos de un informe diseñado BIDS y plublicado en el SSRS, el visor no utilizará este informe directamente. En su lugar nuestra aplicación cargará la definición XML, la modificará en memoria y la cargará en el visor de informes que solicitará el procesado al SSRS.
Las modificaciones consistirán en personalizar las reglas de color de color de la regla tanto en lo que atañe a los rangos de valor (extremo superior e inferior para cada uno) como en los colores aplicados en cada rango.
Aunque partiremos de un informe diseñado BIDS y plublicado en el SSRS, el visor no utilizará este informe directamente. En su lugar nuestra aplicación cargará la definición XML, la modificará en memoria y la cargará en el visor de informes que solicitará el procesado al SSRS.
Las modificaciones consistirán en personalizar las reglas de color de color de la regla tanto en lo que atañe a los rangos de valor (extremo superior e inferior para cada uno) como en los colores aplicados en cada rango.
Para personalizar los rangos numéricos utilizaremos una función que nos devolverá un lista de "Double" que contendrá los extremos de estos rangos según los requisitos 1, 2, 3 dichos arriba. A esta función dedicaremos la tercera entrada. Otra función nos devolverá una lisa de colores centrada en el rango de cambio de signo.
Dedicaremos cuatro entradas a estos trabajos
Dedicaremos cuatro entradas a estos trabajos
- Una regla de color uniforme, redondeada, con cero y colores desplazados (2): Carga y convertir a XML la definición de un informe para poder personalizarla posteriormente visualizar el informe con procesado remoto en un control reportViewer.
- Una regla de color uniforme, redondeada, con cero y colores desplazados (3): presenta la edición XML de la definición del informe
- Una regla de color uniforme, redondeada, con cero y colores desplazados (4): Generar una colección de rangos uniformemente poblada, con los extremos redondeados a la potencia de 10 mas próxima e incluyendo el cero cuando sea necesario.
- Una regla de color uniforme, redondeada, con cero y colores desplazados (5) Definir gradientes de colores con un dos colores extremos y un color central ubicado en el valor cero de la regla:.
Comentario:
Antes de seguir quería decir que desarrollar una aplicación para visualizar magnitudes sobre mapas me ha recordado una vez más que cuando ves resultados estadísticos tienes que estar muy atento a lo que te muestran y como te lo muestran.Por ejemplo el mapa mas arriba visualizaba las variaciones de ventas interanuales entre octubre 2009 y octubre 2008.
Si en lugar del acumulado mostráramos el acumulado interanual de ventas en octubre de 2009, podriamos decir ¿ Crisis? ¿Qué crisis?:
martes, 21 de mayo de 2013
Añadir etiquetas con jerarquía en el mapa del documento de un informe Microsoft
ENUNCIADO
Tienes un informe con grupos y detalles, ¿Donde añadir las etiquetas para que el mapa del documento esté estructurado jerarquicamente. ¿En los cuadros de texto? ¿En la celda del tablix?SOLUCIÓN
Olvídate del área de diseño.En los grupos y detalles del el panel inferior, les das propiedades y usas la ficha Avanzado del formulario que se abre.
viernes, 17 de mayo de 2013
Repetir las filas de encabezados en informes Microsoft
Enunciado
¿Cómo hacer que se repitan las filas de encabezado en tablix de report server?Respuesta
En el panel de grupos (abajo) activa la vista avanzada(La vista avanzada se consigue desplegando una flecha mínima a la derecha del panel que incluye los titulos grupo de filas y grupos de columnas ¡!)
Entonces veras unos grupos de filas (static) que corresponden a tus encabezados (si hay varios).
Selecciona uno.
Cambia en el panel de propiedades a la derecha:
KeepWithGroup: After
RepeatOnNewPage: True
martes, 14 de mayo de 2013
T-SQL para cambiar la intercalación
Enunciado
Cambiar la intercalación de todas las columnas de todas las tablas de una base de datos.Solución
Ejecutas el siguiente T-sql en una consulta SSMS.DECLARE @collation NVARCHAR(64) SET @collation = 'Modern_Spanish_CI_AI' SELECT 'ALTER TABLE [' + INFORMATION_SCHEMA.COLUMNS.TABLE_SCHEMA + '].[' + INFORMATION_SCHEMA.COLUMNS.TABLE_NAME + '] ' + 'ALTER COLUMN [' + COLUMN_NAME + '] ' + DATA_TYPE + '(' + CASE CHARACTER_MAXIMUM_LENGTH WHEN -1 THEN 'MAX' ELSE CAST(CHARACTER_MAXIMUM_LENGTH AS VARCHAR) END + ') ' + 'COLLATE ' + @collation + ' ' + CASE WHEN IS_NULLABLE = 'NO' THEN 'NOT NULL' ELSE 'NULL' END FROM INFORMATION_SCHEMA.columns INNER JOIN INFORMATION_SCHEMA.TABLES ON INFORMATION_SCHEMA.TABLES.TABLE_NAME=INFORMATION_SCHEMA.COLUMNS.TABLE_NAME WHERE COLLATION_NAME IS NOT NULL AND COLLATION_NAME <> @collation AND INFORMATION_SCHEMA.TABLES.TABLE_TYPE ='BASE TABLE'
El resultado lo copias y lo pegas en otra ventana de consulta.
Revisa ese código generado y si está correcto ejecutas.
viernes, 10 de mayo de 2013
Generar miniaturas de archivos TIF, JPG, PDF,...
Enunciado
Desarrollas una aplicación donde los usuarios deben subir archivos varios gráficos en diversos formatos gráficos (JPG, TIF,...etc), así como archivos PDF.Quieres devolverles un informe en PDF con los archivos y las informaciones asociadas.
Sin embargo no los informes de Microsoft no permiten el uso de muchos de los tipos de archivos o estos tienen excesivo peso lo que repercute en la generación y peso mismo del informe.
Solución
Genera las miniaturas en el momento en que suban el archivo, para ello utiliza http://imageresizing.net que puedes instalar mediante NuGet.Para usarla lee esta página: http://imageresizing.net/docs/howto/upload-and-resize
Para que pueda leer y procesar PDFs añádele el plugin PdfRenderer.
Para instalar este último plugin usa NuGet pero además debes editar el archivo web.config.
..... sigue el web config
El código será el mismo que para generar las miniaturas desde archivos gráficos. El plugin le añade la capacidad de leer PDFs.
Comentarios
Es una librería perfecta que tiene muchas mas aplicaciones. Es tan simple de usar que al principio no te lo crees.
Suscribirse a:
Entradas (Atom)