Bienvenidos a antidot-net. En este archivo principalmente técnico, espero que encontréis pequeños antídotos que os ayuden a sobrevivir en el mundo .NET (que no es fácil)
miércoles, 21 de septiembre de 2011
Utilizar TFS 2010 desde VB 6
Microsoft publicó hace un tiempo un proveedor / componente para permitir que productos que no soportan de forma nativa la conexión con el control de versiones de TFS puedan soportarlo.
Según la documentación de aquí, este proveedor permite conectar a TFS 2010 desde, entre otros, Visual Studio .NET 2003, Visual Visual Basic 6 SP6 y SQL Server Management Studio.
Mi experiencia personal ha sido con VS.NET 2003 y funciona 'de lujo' así que entiendo que con el resto de productos funcionará también perfectamente.
¡Ojo! Requiere .NET 4.0 y Team Explorer 2010 para funcionar. El proveedor está disponible para su descarga en el enlace anterior.
viernes, 9 de septiembre de 2011
Excepción BadImageFormatException en IIS 7.5
Problema:
Al acceder a la dirección web de la aplicación, se produce una excepción de ASP.NET del tipo BadImageFormat.
Solución (¿solución?):
Fácil (yujuu!) El equipo en el que la aplicación está instalada es un Windows 7 en 64 bits, con lo que si está dando esa excepción, el sospechoso más habitual indica que el proyecto en Visual Studio está compilado en 32 bits, así que para solucionar el problema, basta con acceder a las propiedades del proyecto y especificar que la compilación sea compatible con 'Any CPU', tal y como vemos en la siguiente captura (Como el proyecto es en VB.Net, la captura corresponde a las propiedades de un proyecto en ese lenguaje)
Listo. Cambiamos la opción de compilación, compilamos la solución y la instalamos, y a funcionar… un momento, ¿no funciona?
Problema 2:
Al acceder a la aplicación ya compilada correctamente, aparece una nueva excepción. Esta excepción, de la que no tengo captura, decía que no podía cargarse en memoria uno de los ensamblados referenciados por la aplicación (en concreto se trata de un ensamblado Interop) toca 'bucear' un poco en la excepción y darle un poco al 'coco'… Vale, ya lo tenemos!!!
Solución 2:
La segunda excepción está directamente relacionada con la primera, ya que al compilar la aplicación para 'Any CPU' hemos conseguido que la aplicación arranque correctamente en IIS (64 bits) peeero cuando esa aplicación realiza una instancia del componente Interop (compilado en 32 bits) resulta que no puede crear los objetos porque no son compatibles. Menos mal que IIS 7.5 tiene la opción de permitir aplicaciones en 32 bits… ¿dónde?
Vamos al listado de los Application Pools, y en el Pool asociado a la aplicación vamos a las opciones avanzadas:
Aceptamos el cambio, IISReset por si acaso, y a volar!!!!
Explicaciones:
El primer error como he comentado estaba provocado por el tipo de compilación del proyecto. Al compilarlo en 32 bits, los procesos de IIS que se ejecutan en 64 bits no eran capaces de arrancar la aplicación, con lo que cambiamos el modo de compilación a 'Any CPU' para que el compilado sea capaz de adaptarse tanto a 64 como a 32 bits (opción recomendada)
El segundo error estaba provocado precisamente por el cambio realizado en el primer punto, ya que al iniciar la aplicación en 64 bits, cualquier instancia que se realice de ensamblados o dll's compilados en 32 bits, sencillamente 'casca'
Consideración Especial:
Vistos los dos problemas que nos aparecieron, entiendo que la directa hubiera sido configurar el IIS para que permitiera aplicaciones en 32 bits, pero, ¿dónde está la gracia si lo solucionamos tan rápido y sin tener que pensar?
Nota: El segundo problema (ensamblado Interop) en este caso parecía claro que era por una compilación en 32 bits, eso NO quiere decir que en todos los casos esta solución sea válida al 100%.
martes, 6 de septiembre de 2011
Los Informes de TFS 2010 no funcionan (no funcionaban)
Error de ejecución de consulta para el conjunto de datos 'dsBurndown'. (rsErrorExecutingCommand)
Para obtener más información acerca de este error, vaya al servidor de informes en el equipo del servidor local o habilite los errores remotos"
Tras mucho mirar por ahí y realizar pruebas, la solución resultó ser 'medianamente' sencilla.
miércoles, 12 de mayo de 2010
Tipos de contenido multi-idioma en Sharepoint
Objetivo
El objetivo de este documento es explicar el procedimiento a seguir para generar tipos de contenido con soporte para varios idioma.
Al final del documento conseguiremos un Tipo de contenido cuyas columnas se mostrarán en el idioma del sitio en el que estemos.
Captura del resultado en un sitio en inglés:

Captura del resultado en un sitio en español:

Consideraciones previas
Entorno:
Visual Studio 2008
Visual Studio 2008 extensions for Windows SharePoint Services 3.0 (version 1.3) Sharepoint Server 2007
Procedimiento
Creación del proyecto
En Visual Studio 2008, creamos un Nuevo proyecto vacío, de tipo Sharepoint:

Y elegimos el nivel máximo de seguridad (Full Trust)

Creación del tipo de contenido
Hacemos clic con el botón derecho del ratón sobre el nombre del proyecto que hemos creado, y elegimos “Add / New Item…”Seleccionamos la plantilla de “Content Type” y hacemos clic en “Add”

Seleccionamos el tipo de contenido ‘Base’ del que deseamos partir y hacemos clic en “OK”.

con un tipo de contenido básico al que no se le añadirá ningún manejador de
eventos, ya que el procedimiento elegido funcionará de la misma manera si
añadimos los manejadores de eventos. Se ha querido mantener el documento lo más
liviano posible.

Haciendo doble clic en el archivo “feature.xml” accedemos a la definición de la característica, que indica cómo se muestra la misma en la pantalla “Características de la colección de sitios”. En este archivo realizaremos los primeros cambios para disponer de nuestro tipo de contenido
multi-idioma.
Definición de la característica por defecto:

Definición de la característica modificada:

Como podemos observar en el código definitivo, hemos modificado la siguiente información:



la estructura física de carpetas en la que se instala la característica.
Creación de las columnas del tipo de contenido
En la ventana de “WSP View” hacemos doble clic sobre el archivo “Multi1.xml” para acceder a la definición del tipo de contenido.Definición del tipo de contenido por defecto:

Definición del tipo de contenido modificada:

Las modificaciones realizadas son:




Añadir un archivo de recursos
Hasta el momento hemos utilizado el ‘token’ “$Resources:” para indicar al sistema que el valor del atributo reside en un archivo de recursos; pero, ¿cómo incluimos estos archivos al sistema?El objetivo de esta parte del documento es conseguir la siguiente estructura en la carpeta de instalación de la característica:

Cada vez que el sistema encuentre el ‘token’ “$Resources:” en los archivos de definición de la característica y/o del tipo de contenido, irá a buscar los recursos a la carpeta “Resources” que aparece en la captura anterior.
Accedemos en Visual Studio al explorador de soluciones, y generamos la siguiente estructura de carpetas en el proyecto:

Como vemos en la captura anterior, tenemos que ‘recrear’ la estructura física que tendrá la característica una vez instalada en el servidor Sharepoint.
En la carpeta “Resources” hacemos clic con el botón derecho del ratón y elegimos Add, New Item… y elegimos la plantilla de archivo de recursos:

Haciendo doble clic en el archivo “Resources.resx” accedemos al archivo de recursos y generamos tantas entradas como ‘tokens’ hayamos utilizado:

Generar el paquete de la solución
En el explorador de soluciones, hacemos clic con el botón derecho del ratón sobre el proyecto y elegimos ‘Package’Esta operación nos genera el archivo WSP que instalaremos en el servidor Sharepoint.
Resultado
Una vez instalada la característica y desplegada en los portales necesarios, aparecerá en la página de “Características de la colección de sitios”
Antes de activar la característica, vamos a fijarnos en los literales que aparecen en la captura… son los literales definidos en el archivo de recursos, sí; pero, un momento, ¡según el botón “Activar”, el sitio de Sharepoint está en castellano! ¿Qué ha pasado aquí?
Como hemos comentado antes, el sistema intenta localizar un archivo de recursos que se ajuste al idioma del sitio en el que estamos. Si no lo encuentra utiliza el archivo por defecto (en este caso el único que hemos añadido)
Añadiendo más archivos de recursos
Para añadir más archivos de recursos (teóricamente uno para cada idioma que tengamos disponible) vamos en el proyecto a la carpeta Resources y añadimos un nuevo archivo de recursos, que se llamará exactamente igual que el primero que hemos añadido, pero con el sufijo id-ID que indicará el idioma y la localización de los recursos. La estructura de esta carpeta Resources quedará:
El contenido del archivo tiene que ser exactamente igual que el archivo de recursos por defecto, obviamente traducido al idioma del archivo (en este caso es-ES, español de España)

Volvemos a generar el paquete WSP, lo reinstalamos en el servidor Sharepoint, y observamos el resultado:

¡¡¡Ya tenemos la característica traducida!!!
Ahora sólo hace falta activar la característica y ya tenemos el tipo de contenido generado tal y como se veía al principio, con distintos literales para cada idioma.
Detalle del tipo de contenido:

Añadir este tipo de soporte multi-idioma es relativamente sencillo, pero hay que tener en cuenta las limitaciones especificadas anteriormente. Una vez que un tipo de contenido se utiliza en una lista de Sharepoint, las modificaciones realizadas al tipo de contenido raíz mediante este procedimiento NO se trasladan a dichos tipos de contenido ‘hijos’.
martes, 24 de junio de 2008
Utilizar formularios abstractos desde Visual Studio 2008
Implementación de Formularios Base
Una de las características más eficaces de la herencia es la posibilidad de realizar cambios en una clase base que se propagan a las clases derivadas. Cuando hablamos de aplicaciones de tipo Windows Forms, lo anterior implica que podemos tener un formulario base del que hereden el resto de formularios de la aplicación, facilitando que la aplicación sea homogénea, así como la reutilización de código.
En el presente artículo se va a tratar una problemática muy común con el uso de clases bases y heredadas en formularios y se van a ofrecer alternativas para facilitar estas operaciones.
Problema:
Cuando trabajamos con formularios heredados lo hacemos con el objeto de tener una serie de clases (formularios) base de los cuales hereden todos los formularios de nuestra aplicación, creando así pantallas homogéneas tanto en diseño como en el código asociado. Sin embargo, con esta arquitectura aparece un problema en tiempo de diseño cuando la clase base de la que heredamos es una clase abstracta. En la siguiente captura vemos el error que aparece al abrir en el diseñador de Visual Studio la clase ‘heredada’:
Como podemos observar en la captura, El diseñador de Visual Studio nos indica que no puede crear una instancia de la clase ‘base’ porque está declarada como una clase abstracta.
Detalle de la clase Base:
Public MustInherit Class Base
Inherits Form
Detalle de la clase heredada:
Public Class Inherited
Inherits Base
En este punto más de uno estará pensando ‘¿Por qué está intentando crear una instancia de la clase base, si yo estoy abriendo la clase heredada?’ En el blog de Brian Pepin tenemos las razones de este comportamiento, aunque más adelante en este documento se darán más detalles.
Una vez revisado el artículo de Brian Pepin, alguien estará pensando ‘vale, pues no utilizaré formularios abstractos’ aquí no voy a intentar convencer a nadie de que la utilización de este tipo de clases es imprescindible, pero me vais a permitir que ponga un ejemplo:
Ejemplo:
Todos los formularios de tipo ‘Mantenimiento’ de la aplicación tienen un botón ‘Grabar’ que se encarga de guardar la información introducida en una Base de Datos. Para solucionar este requerimiento, tomamos la siguiente decisión:
- Genero una clase base que, heredando de la clase ‘Form’ incorpore el botón. De esta forma, cada vez que incorporemos un formulario de tipo ‘Mantenimiento’ a la aplicación, en lugar de que herede de ‘Form’, indicamos que hereda de nuestra clase base y ya tenemos el botón automáticamente en el formulario, con el diseño que le hayamos dado en la clase base. Si tenemos que cambiar el diseño de dicho botón, lo haremos en la clase base y el resto de formularios automáticamente aplicarán el nuevo diseño.
- Además, yo quiero que este botón en todos los formularios realice la misma operación (SaveData) y que además sólo la realice si se ha validado la posible información existente en la pantalla (ValidateData) para esto vamos a codificar el evento del botón en la clase base, de forma que ese comportamiento se ‘herede’ por todos los formularios derivados de nuestra clase base. En este punto no sabemos qué campos tenemos que validar, ni tampoco que objeto de la Base de Datos vamos a actualizar, con lo que decidimos generar la firma para las dos llamadas en la clase base, obligando mediante ‘MustOverride’ a todas las clases derivadas a que implementen esas funciones, ya que ellas son las que realmente saben con qué datos están trabajando:
Protected MustOverride Function ValidateData() As Boolean
Protected MustOverride Function SaveData() As Boolean - En este punto, Visual Studio nos va a obligor a marcar la clase como ‘MustInherit’ ('Base' must be declared 'MustInherit' because it contains methods declared 'MustOverride'.) y esto ¿qué significa? Al marcar la clase base como ‘MustInherit’ la estamos convirtiendo en una clase abstracta y acabamos en la situación del error anterior.
Como veis en el ejemplo no es descabellado llegar a utilizar estas clases abstractas; sin embargo, como explicaba anteriormente no podemos editar en modo ‘Diseño’ los formularios que hereden de una clase abstracta.
Explicación:
(Interpretación libre del artículo de Brian Pepin) Cuando abrimos un formulario en la vista de diseño, el IDE de Visual Studio lo que hace es crear una instancia de la clase base de ese formulario, diseñar los controles existentes en la clase base mediante la rutina ‘InitializeComponents’ y a continuación mediante su propia rutina ‘InitializeComponents’ realizar su propio diseño personalizado.
Al trabajar de esta manera, cuando la clase base es una clase abstracta el diseñor devuelve la excepción anterior ya que las clases abstractas no pueden ser instanciadas directamente.
Solución Propuesta:
En múltiples foros, artículos y demás recursos de la red, se propone utilizar la compilación condicional para solucionar este problema, con estas soluciones, el inicio de la clase base sería:
#If CONFIG = "Release" Then
Public MustInherit Class Base
Inherits Form
Public MustOverride Function ValidateCode() As Boolean
Public MustOverride Function LoadData() As Boolean
#Else
Public Class Base
Inherits Form
Public Overridable Function ValidateCode() As Boolean
Throw New NotImplementedException
End Function
Public Overridable Function LoadData() As Boolean
Throw New NotImplementedException
End Function
#End If
Esta técnica funciona correctamente, pero implica tener siempre el componente donde definamos las clases base siempre en modo ‘Debug’ para poder diseñar los formularios de la aplicación; dificultaría la encapsulación de clases base en dll’s independientes, ya que si esa dll está compilada en modo ‘Release’ las clases son abstractas. Adicionalmente, al no marcar los métodos como ‘MustOverride’, corremos el riesgo de que los formularios heredados no implementen dicho método (en este caso daría una excepción, pero en determinadas circunstancias puede ser complicado detectar esas excepciones.
En el blog de
El IDE de Visual Studio utiliza ‘Reflexión’ para pintar los formularios; la solución que vamos a utilizar consiste en aplicar un atributo personalizado a la clase base de forma que el IDE al encontrar ese atributo utilice una implementación personalizada de la clase para realizar el ‘pintado’ de los controles en la vista de ‘diseño’.
Descripción de la Solución:
En los fragmentos de código que se verán a continuación he utilizado la raíz ‘Base’
- Atributo personalizado. Necesitamos un atributo para la clase Base que permitirá indicar qué implementación de la clase se utilizará al acceder en la vista de diseño. Este atributo puede ser único para todas las clases Bases.
El código del atributo es:[AttributeUsage(AttributeTargets.Class)]_ MSDN.
Friend Class BaseFormAttribute
Inherits Attribute
Private _designType As Type
Public ReadOnly Property DesignType() As Type
Get
Return _designType
End Get
End Property
Public Sub New(ByVal design As Type)
_designType = design
End Sub
End Class
La propiedad ‘DesignType’ especifica que clase (tipo) se utilizará para mostrar el formulario en la vista de diseño. El constructor se ejecutará cuando apliquemos el atributo a la clase base; adicionalmente hemos marcado esta clase con el atributo ‘AttributeUsage’ para indicar que este objeto sólo se aplicaría a tipos ‘Class’ más información en el sitio del - Proveedor. Necesitamos una clase auxiliar que reemplace la lógica de reflexión utilizada por defecto por el IDE con las rutinas necesarias para aplicar la solución. El proveedor heradará de ‘TypeDescriptionProvider’ y se aplicará a la clase base como atributo. Para que cumpla su comentido tendremos que sobrecargar los métodos ‘GetReflectionType’ y ‘CreateInstance’. Este proveedor puede ser único para todas las clases Bases.
El código del proveedor (simplificado, el código completo está en el proyecto que acompaña a este documento) es:
Friend Class BaseFormProvider
Inherits TypeDescriptionProvider
Private _abstractType As Type
Private _designType As Type
Private Sub EnsureTypes(ByVal objectType As Type)
.........
End Sub
Public Overloads Overrides Function CreateInstance(ByVal provider As IServiceProvider, ByVal objectType As Type, ByVal argTypes As Type(), ByVal args As Object()) As Object
.........
End Function
Public Overloads Overrides Function GetReflectionType(ByVal objectType As Type, ByVal instance As Object) As Type
.........
End Function
End Class
Donde:
* _abstractType es el tipo de la clase Base.
* _designType es el tipo de la clase ‘puente’ para la vista de diseño.
* EnsureTypes se encarga de establecer la clase Base y la clase para Diseño.
* CreateInstance y GetReflectionType son las sobrecargas utilizadas para evitar que el diseñador llegue hasta la clase base y realice la reflexión en su lugar con la clase para Diseño. - Diseñador. Esta clase es la que se utilizará para ‘pintar’ los controles del formulario heredado en la vista de diseño. De esta clase heredará la clase base y contiene toda la parte de diseño de este formulario. Esta clase sólo se utiliza para diseñar; en él añadimos los controles que queremos en la clase base, su ubicación y estilo en pantalla, pero nada más. NO PONDREMOS NADA DE CÓDIGO EN ESTA CLASE. El nombre para esta clase terminará en ‘Designer’ y es necesario un diseñador para cada clase Base.
- Clase Base. La clase final de la solución; esta clase hereda del diseñador (BaseFormDesigner) y en ella incluiremos TODO el código el código que necesitemos en la clase Base así como las declaraciones de los métodos ‘MustOverride’ que necesitemos. La clase irá decorada con los atributos que hemos generado anteriormente.
Un ejemplo de código de la clase Base (simplificado, el código completo está en el proyecto que acompaña a este documento) es:[TypeDescriptionProvider(GetType(BaseFormProvider)), _ BaseForm(GetType(BasicDataFormDesigner))]_
Public MustInherit Class BasicDataForm
Inherits BasicDataFormDesigner
Protected MustOverride Function ValidateData() As Boolean
Protected MustOverride Function SaveData() As Boolean
Podéis observer la utilización del proveedor, del diseñador y del atributo personalizados en la decoración de la clase.
Un ejemplo de código adicional en la clase Base (por ejemplo el del botón Grabar del ejemplo:
Private Sub cmdOK_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOK.Click
If ValidateData() Then
SaveData()
End If
End Sub
Como he explicado antes, es en la clase heredada donde tendremos que implementar las dos rutinas, hasta el punto de que si no están implementadas no podremos compilar el proyecto.
Consideraciones:
Código Fuente:
Todo el código fuente que os he enseñado / explicado en este documento está disponible en
La Release concreta es CTSI.Framework.Winforms 1.0.0.0.
martes, 15 de enero de 2008
Dar permisos totales a una unidad de red para .NET
Bueno, pequeña 'píldora' para facilitar el desarrollo de aplicaciones con Microsoft. NET.
Cuando intentamos cargar un proyecto de VS.NET project desde una unidad de red (por ejemplo la N:) lo normal es que recibamos el siguiente error:
"The project location is not trused."
"Running the application may result in security exceptions when it attemps to perform actions which require full trust."
Lo que está pasando es que .NET por defecto restringe los permisos a ensamblados en la red.
Para dar permisos totales a estas unidades de red, tenemos que añadir un nuevo "Code Group" especificando en formato URL que queremos otorgarle confianza total. Esto podemos hacerlo mediante la herramienta de configuración de .NET Framework o a través de la línea de comando de la siguiente manera:
c:\>caspol -q -machine -addgroup 1 -url file://n:/* FullTrust -name "Unidad N"
Una vez generado este nuevo grupo, cualquier proceso nuevo .NET que arranquemos dará permisos totales a los ensamblados de la unidad N.
OJO con las posibles implicaciones de seguridad, TODOS los ensamblados de la N cogen permisos totales (confianza)
jueves, 15 de noviembre de 2007
Sobrevivir al .NET
Sed bienvenidos y disculpad cualquier incongruencia que podáis encontrar en este espacio.
Como comento en la descripción del sitio, espero poder publicar pequeños (o no tan pequeños) trucos o antídotos que en una ocasión u otra me hayan podido 'salvar el cuello' -- Bueno, o que me lo podrían haber salvado en alguna otra ocasión.
Nada más, no soy persona de muchas palabras, espero que os resulte útil todo lo que encontréis aquí.
Besitos para ellas y abrazos para ellos.