Testing y QA con Visual Studio y TFS

por Alejandra Federico  13. agosto 2012

 El martes 4 de Septiembre realizaremos una jornada en el MUG sobre:

Testing y QA con Visual Studio y TFS.

Orador: Victor Passador 

Días y Horario: La actividad dura dos días. Inicia el martes 4 de septiembre y finaliza el jueves 6 en el horario de 18:30 a 21:30 hs. 

En muchos ámbitos se sigue viendo al testing como un costo y no como una inversión. ¿Están equivocados o no tanto? En otro extremo podemos encontrar a los que no conciben escribir una sola línea de código sin que exista previamente un test que esté fallando. ¿Es una utopía o algo factible? En esta charla/laboratorio nos proponemos mostrar algunos conceptos teóricos, técnicas de testing y también reflexionar sobre este interesante tema.

Temario:

Conceptos teóricos, unificando vocabulario

                ¿Qué es y qué implica el testing? Técnicas, tipos y métricas

                Algunas reflexiones sobre TDD

Aplicación en una herramienta

                Reflexiones sobre la problemática diaria y una lista de deseos

                Las tareas del tester (o tratando de armar un proceso)

                El rol de Visual Studio. ¿Automatización al 100%?

Prácticas

                TDD con Visual Studio

                Test de carga/stress

                Testing funcional

                Automatización de tests

Datos del Evento:

Martes, 4 de Septiembre del 2012.

en el Auditorio del MUG, Rivadavia 1479, Piso 1°A, Capital Federal.

Para más información e inscripción AQUÍ

 

Tags: , ,

SQL Server | Testing | Visual Studio

Custom InfoPath Email Activity para Sharepoint Designer 2010.

por Mauricio Rodriguez  14. marzo 2012

Recientemente he finalizado, junto con el equipo de Sharepoint, un proyecto interesante en cuanto al nivel de personalización que el cliente exigía.

El requerimiento era crear un workflow de aprobación reusable para formularios de solicitud de empleo. Lo significativo de este workflow era que debía trabajar sin lista de tareas ( esencial en los workflows de aprobación de Sharepoint ) y con emails personalizados, los cuales debían llevar adjunto el formulario (hecho en InfoPath), de manera que los usuarios involucrados en el proceso de solicitud, puedan abrirlos desde Outlook y completarlos.

Desde el inicio pensamos en una Custom Activity que pueda cumplir con el requerimiento de envío de mails .

En esta oportunidad vamos a ver como crear una Custom Activity para enviar mails con formularios InfoPath adjuntos para poder usarla desde el Sharepoint Designer en el diseño de una solución.

1 - Creación de la estructura de la solución.

Para comenzar vamos a crear un Empty SharePoint Project en Visual Studio 2010.

Le damos nombre al proyecto y seleccionamos “Ok”. Se abrirá una ventana en la cual deberemos especificar el Servidor de Sharepoint donde se hará el deploy de la solución, y si lo queremos hacer como Sandboxed Solution o FarmSolution. En nuestro ejemplo vamos a usar una Farm Solution.

image

Finalizamos, y Visual Studio nos creará un proyecto en base al template Seleccionado que se verá como la siguiente imagen.

Vamos a modificar el nombre del archivo key.snk por el nombre de nuestro proyecto, en este caso VEMN.CustomActivity. Esto es mas que nada para mantener un orden y una correspondencia.

 

Una vez hecho esto, agregamos una Feature, le damos un nombre, siempre es preferible usar el mismo nombre del proyecto para mantener una coherencia.

image

Luego vamos a agregar una Sharepoint Mapped Folder. Nos paramos sobre el proyecto, Clik Derecho –> Add –> Sharepoint Mapped Folder. Es de vital importancia el siguiente paso ya que este cambia de acuerdo al lenguaje en que esté nuestro Sitio. Como el nuestro está inglés seleccionaremos la ruta …/TEMPLATE/1033/Workflow. En caso de que nuestro sitio este en Español debemos seleccionar el directorio 3082 o el código que le corresponda.

image

Esto mapea esta carpeta a nuestro proyecto en donde vamos a agregar un archivo xml que va a contener la metadata y las definiciones de parámetros de entrada y salida de nuestra Custom Activity. Este lo llamaremos con mismo nombre que el proyecto y la extensión se la cambiaremos de .xml a .actions, como se puede ver en la imagen siguiente.

image

 

2 - Definición del archivo ACTIONS

Definiremos a continuación, el archivo .actions que acabamos de agregar. Es importante tener en cuenta que este archivo es leído por el SPD para levantar nuestra custom activity y habilitarla en el menú Action. Por lo cual deberemos prestar mucha atención al momento de crearlo, para no tener problemas en la implementación.

Nuestro xml debe quedar con las siguiente estructura. Luego discutiremos sobre que es cada cosa:

   1: <WorkflowInfo Language="en-US">
   2:    <Actions Sequential="then" Parallel="and">
   3:       <Action Name="Send mail with attachment infopath form"
   4:            ClassName="VEMN.CustomActivity.SendMailActivity"
   5:            Assembly="VEMN.CustomActivity,Version=1.0.0.0,Culture=neutral,PublicKeyToken=7819e4834cee4b0e"
   6:            AppliesTo="all"
   7:            Category="Custom Actions">
   8:           <RuleDesigner Sentence="Send mail with this %1 attachment to %2. Use %3 like sender. >
   9:              <FieldBind Field="AttachmentFileName" Text="form (url)" DesignerType="TextArea" Id="1"/>
  10:              <FieldBind Field="To,CC,Subject,Body" Text="these user(s)" DesignerType="Email" Id="2"/>
  11:              <FieldBind Field="From" Text="this user" DesignerType="TextArea" Id="3"/>  
  12:           </RuleDesigner>
  13:           <Parameters>
  14:             <Parameter Name="__Context" Type="Microsoft.SharePoint.WorkflowActions.WorkflowContext" Direction="In" />
  15:             <Parameter Name="__ListId" Type="System.String, mscorlib" Direction="In" />
  16:             <Parameter Name="__ListItem" Type="System.Int32, mscorlib" Direction="In" />
  17:             <Parameter Name="__ActivationProperties" Type="Microsoft.SharePoint.Workflow.SPWorkflowActivationProperties, Microsoft.SharePoint" Direction="Out" />
  18:             <Parameter Name="AttachmentFileName" Type="System.String, mscorlib" Direction="In" />
  19:             <Parameter Name="To" Type="System.Collections.ArrayList, mscorlib" Direction="In" />
  20:             <Parameter Name="CC" Type="System.Collections.ArrayList, mscorlib" Direction="Optional" />
  21:             <Parameter Name="Subject" Type="System.String, mscorlib" Direction="In" />
  22:             <Parameter Name="Body" Type="System.String, mscorlib" Direction="Optional" />
  23:             <Parameter Name="From" Type="System.String, mscorlib" Direction="In" />
  24:          </Parameters>
  25:       </Action>
  26:    </Actions>
  27: </WorkflowInfo>

El nodo WorkflowInfo solo definimos el lenguaje del sitio en el cual vamos a utilizar nuestra funcionalidad. El nodo Actions define la forma en el texto se construye luego de la descripción de la acción en el diseñador del flujo de trabajo. Por ejemplo, en una acción secuencial seria algo así: “<actiondescription> then”.

Lo realmente interesante está en el nodo Action, en el, los atributos Name, ClassName, Assembly, ApplyTo and Category definen: el nombre de la acción en el Menú de Acciones (o Actions Menu), la Clase y el Assembly para el código, si se refiere a List Items , sólo documentos, o todos, y la categoría bajo la cual aparecerá listada nuestra Acción.

El nodo RuleDesigner específica el texto a mostrar y los parámetros de entrada. El atributo Sentence determina la sentencia que se mostrará en el Diseñador de workflows. Podemos especificar cada parámetro con un signo % seguido de un numero, por ejemplo 1, que apuntará al primer parámetro y así sucesivamente. Estos parámetros son referenciados por los FieldBind. El atributo text del mismo reemplazara a los % del Sentence. El atributo ID debe coincidir con el orden del parámetro.

Y por ultimo el nodo Parameters y sus hijos definen los parámetros que serán pasados a nuestra Custom Activity. Los nombres de los attibutos Field de los Nodos FieldBind deben coincidir exactamente con los atributos Name de los Nodos Parameters para que sean transferidos a nuestro código.

3 - Definición de nuestra clase principal.

Crearemos una clase que llamada SendMailActivity.cs o como quieran llamarla. La misma debe heredar de System.Workflow.ComponentModel.Activity (no olvidemos agregar la referencia a esta dll) la cual contiene un método llamado Execute que es el punto clave para nuestra Custom Activity que luego vamos a sobrescribir.

Ahora, definamos las propiedades en nuestra clase de modo que coincidan con lo que acabamos de establecer en el archivo .actions para que quede todo mapeado. Este mapeo se logra gracias al objeto DependencyProperty como veremos luego.

Al inicio nos ocuparemos de definir las propiedades del WorkflowContext y luego las personalizadas.

   1: #region [ Workflow Context Properties ]
   2:  
   3: public static DependencyProperty __ContextProperty = DependencyProperty.Register("__Context", typeof(WorkflowContext), typeof(SendMailActivity));
   4:  
   5: [ValidationOption(ValidationOption.Required)]
   6: public WorkflowContext __Context
   7: {
   8:     get
   9:     {
  10:         return ((WorkflowContext)(base.GetValue(__ContextProperty)));
  11:     }
  12:     set
  13:     {
  14:         base.SetValue(__ContextProperty, value);
  15:     }
  16: }
  17:  
  18: public static DependencyProperty __ListIdProperty = DependencyProperty.Register("__ListId", typeof(string), typeof(SendMailActivity));
  19:  
  20: [ValidationOption(ValidationOption.Required)]
  21: public string __ListId
  22: {
  23:     get
  24:     {
  25:         return ((string)(base.GetValue(__ListIdProperty)));
  26:     }
  27:     set
  28:     {
  29:         base.SetValue(__ListIdProperty, value);
  30:     }
  31: }
  32:  
  33: public static DependencyProperty __ListItemProperty = DependencyProperty.Register("__ListItem", typeof(int), typeof(SendMailActivity));
  34:  
  35: [ValidationOption(ValidationOption.Required)]
  36: public int __ListItem
  37: {
  38:     get
  39:     {
  40:         return ((int)(base.GetValue(__ListItemProperty)));
  41:     }
  42:     set
  43:     {
  44:         base.SetValue(__ListItemProperty, value);
  45:     }
  46: }
  47:  
  48: public static DependencyProperty __ActivationPropertiesProperty = DependencyProperty.Register("__ActivationProperties", typeof(SPWorkflowActivationProperties), typeof(SendMailActivity));
  49:  
  50: [ValidationOption(ValidationOption.Required)]
  51: public SPWorkflowActivationProperties __ActivationProperties
  52: {
  53:     get
  54:     {
  55:         return (SPWorkflowActivationProperties)base.GetValue(__ActivationPropertiesProperty);
  56:     }
  57:     set
  58:     {
  59:         base.SetValue(__ActivationPropertiesProperty, value);
  60:     }
  61: }
  62:  
  63: #endregion [ Workflow Context Properties ]

Pasemos ahora, a crear las propiedades personalizadas.

   1: #region [ Custom Workflow Properties ]
   2:  
   3: public static DependencyProperty ToProperty = DependencyProperty.Register("To", typeof(ArrayList), typeof(SendMailActivity));
   4:  
   5: [ValidationOption(ValidationOption.Required)]
   6: public ArrayList To
   7: {
   8:     get
   9:     {
  10:         return ((ArrayList)(base.GetValue(SendMailActivity.ToProperty)));
  11:     }
  12:     set
  13:     {
  14:         base.SetValue(SendMailActivity.ToProperty, value);
  15:     }
  16: }
  17:  
  18: public static DependencyProperty CCProperty = DependencyProperty.Register("CC", typeof(ArrayList), typeof(SendMailActivity));
  19:  
  20: [ValidationOption(ValidationOption.Optional)]
  21: public ArrayList CC
  22: {
  23:     get
  24:     {
  25:         return ((ArrayList)(base.GetValue(SendMailActivity.CCProperty)));
  26:     }
  27:     set
  28:     {
  29:         base.SetValue(SendMailActivity.CCProperty, value);
  30:     }
  31: }
  32:  
  33: public static DependencyProperty SubjectProperty = DependencyProperty.Register("Subject", typeof(string), typeof(SendMailActivity));
  34:  
  35: [ValidationOption(ValidationOption.Required)]
  36: public string Subject
  37: {
  38:     get
  39:     {
  40:         return ((string)(base.GetValue(SendMailActivity.SubjectProperty)));
  41:     }
  42:     set
  43:     {
  44:         base.SetValue(SendMailActivity.SubjectProperty, value);
  45:     }
  46: }
  47:  
  48: public static DependencyProperty BodyProperty = DependencyProperty.Register("Body", typeof(string), typeof(SendMailActivity));
  49:  
  50: [ValidationOption(ValidationOption.Optional)]
  51: public string Body
  52: {
  53:     get
  54:     {
  55:         return ((string)(base.GetValue(SendMailActivity.BodyProperty)));
  56:     }
  57:     set
  58:     {
  59:         base.SetValue(SendMailActivity.BodyProperty, value);
  60:     }
  61: }
  62:  
  63: public static DependencyProperty AttachmentFileNameProperty = DependencyProperty.Register("AttachmentFileName", typeof(string), typeof(SendMailActivity));
  64:  
  65: [ValidationOption(ValidationOption.Required)]
  66: public string AttachmentFileName
  67: {
  68:     get
  69:     {
  70:         return ((string)(base.GetValue(AttachmentFileNameProperty)));
  71:     }
  72:     set
  73:     {
  74:         base.SetValue(AttachmentFileNameProperty, value);
  75:     }
  76: }
  77:  
  78: public static DependencyProperty FromProperty = DependencyProperty.Register("From", typeof(string), typeof(SendMailActivity));
  79:  
  80: [ValidationOption(ValidationOption.Required)]
  81: public string From
  82: {
  83:     get
  84:     {
  85:         return ((string)(base.GetValue(SendMailActivity.FromProperty)));
  86:     }
  87:     set
  88:     {
  89:         base.SetValue(SendMailActivity.FromProperty, value);
  90:     }
  91: }
  92:  
  93:  
  94: #endregion [ Custom Workflow Properties ]

Ahora bien,  podemos avanzar en la creación de nuestro método. Aquí explicaré con más detalle la solución.

Lo que necesitabamos hacer para poder adjuntar el formulario infopath a un email era acceder físicamente al mismo. El problema de esto era que no tenia forma de obtener la referencia hacia ninguna ruta “física” del item (formulario), pero sí, por medio del contexto del Workflow, podía saber cual era su ubicación lógica (Url) desde la propiedad CurrentItemUrl. Aquí llegamos al tema más interesante de este post.

Primer paso importante:

Como podrán ver luego, se usó la url del ítem actual para crear un WebRequest a partir del cual podía obtener un ResponseStream. Algo bastante sugestivo, y algo que no podemos olvidar de hacer al crear el Request para un formulario InfoPath, es enviar un parámetro en el header del mensaje de modo que cuando se ejecute, este no sea redireccionado a FormServer.aspx (redirección por default cuando accedemos a un archivo infopath). El modo de enviar este parámetro es el siguiente:  myRequest.Headers.Add("Translate:f");.

Les dejo un link referente a este ultimo tema. Parámetros con los que puede trabajar InfoPath Forms.

http://msdn.microsoft.com/en-us/library/ie/ms772417.aspx

Segundo paso importante:

Luego de obtener el Stream para el adjunto, se crea el Attachment del email, estableciendo para su ContentType correspondiente. Ejemplo:

   1: Attachment form = new Attachment(msForm, new ContentType("application/x-microsoft-InfoPathForm")); 

Tercer paso importante:

Agregar los siguientes headers al Mensaje:

   1: message.Headers.Add("Content-Class", "InfoPathForm.InfoPath");
   2: message.Headers.Add("Message-Class", "IPM.InfoPathForm.InfoPath");

 

Como expliqué en los párrafos anteriores, nuestra clase hereda de Activity. Esto implica que estamos obligados a implementar el método Execute sobrescribiéndolo con nuestra propia lógica. En el siguiente ejemplo podemos ver como quedaría nuestro código siguiendo las indicaciones anteriores:

   1: protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
   2: {
   3:     using (var web = __Context.Web)
   4:     {
   5:         try
   6:         {
   7:             // Obtiene la informacion necesaria para el mensaje que se envía, 
   8:             //relacionada al item actual sobre el cual esta corriendo el workflow. 
   9:             var message = BuildMailMessage(web);
  10:             
  11:             try
  12:             {
  13:                 using (SPSite site = new SPSite(AttachmentFileName))
  14:                 {
  15:                     using (SPWeb fileWeb = site.OpenWeb())
  16:                     {
  17:                         string nameForm;
  18:  
  19:                         //Stream del Archivo InfoPath xml
  20:                         Stream msForm = CreateResponseStream(fileWeb, AttachmentFileName, out nameForm);
  21:                         
  22:                         Attachment form = new Attachment(msForm, new ContentType("application/x-microsoft-InfoPathForm"));
  23:  
  24:                         message.Headers.Add("Content-Class", "InfoPathForm.InfoPath");
  25:                         message.Headers.Add("Message-Class", "IPM.InfoPathForm.InfoPath");
  26:  
  27:                         message.Attachments.Add(form);
  28:                     
  29:                     }
  30:                 }
  31:  
  32:             }
  33:             catch (Exception ex)
  34:             {
  35:                 // NO se pudo encontrar e archivo.
  36:                 Common.WriteFailToHistoryLog(web, WorkflowInstanceId, string.Format(CultureInfo.InvariantCulture, "No se pudo adjuntar el archivo: '{0}' - Mensaje: {1}.", AttachmentFileName, ex.Message));
  37:             }
  38:  
  39:             if (!string.IsNullOrEmpty(From))
  40:             {
  41:                 //Se obtiene la cuenta del usuario enviador del mail
  42:                 message.From = GetMailAddress(__Context.Web, From);
  43:                 Common.WriteFailToHistoryLog(web, WorkflowInstanceId, string.Format(CultureInfo.InvariantCulture, "From: {0}", message.From));
  44:             }
  45:  
  46:             if (message.To.Count > 0)
  47:             {
  48:                 // Se establece el objeto SMTP en base a la configuracion del smtp de nuestro Sharepoint
  49:                 SmtpClient smtpClient = LoadSmtpInformation();
  50:  
  51:                 if (message != null)
  52:                     Common.WriteFailToHistoryLog(web, WorkflowInstanceId, string.Format(CultureInfo.InvariantCulture, "Direccion de mail del From: {0} - To: {1} -  ", message.From.Address, message.To[0].Address));
  53:                 smtpClient.Send(message);
  54:  
  55:                 // Log en el historial del Workflow
  56:                 Common.WriteSuccessToHistoryLog(web, WorkflowInstanceId, string.Format(CultureInfo.InvariantCulture, "Email correctamente enviado a los usuarios: {0}", message.To));
  57:             }
  58:             else
  59:             {
  60:                 // Log en el historial del Workflow
  61:                 StringBuilder emailAddressesTo = new StringBuilder();
  62:                 for (int i = 0; i < To.Count; i++)
  63:                 {
  64:                     emailAddressesTo.AppendFormat(CultureInfo.InvariantCulture, "{0}, ", To[i]);
  65:                 }
  66:                 // Trim de la utlima coma
  67:                 emailAddressesTo = emailAddressesTo.Remove(emailAddressesTo.Length - 1, 2);
  68:  
  69:  
  70:                 Common.WriteFailToHistoryLog(web, WorkflowInstanceId, string.Format(CultureInfo.InvariantCulture, "Unable to send email out. No valid user email addresses for the following users ({0}) were found.", emailAddressesTo));
  71:             }
  72:         }
  73:         catch (Exception ex)
  74:         {
  75:             // Log en el historial del Workflow
  76:             Common.WriteFailToHistoryLog(web, WorkflowInstanceId, string.Format(CultureInfo.InvariantCulture, "Falló el envío de mail. Mensaje de Error: {0}", ex.Message));
  77:         }
  78:         finally
  79:         {
  80:             // Cleanup - Dispose de las propiedades privadas antes de salir
  81:             if (__ActivationProperties != null)
  82:             {
  83:                 __ActivationProperties.Dispose();
  84:             }
  85:  
  86:             if (__Context != null)
  87:             {
  88:                 __Context.Dispose();
  89:             }
  90:         }
  91:     }
  92:  
  93:     return ActivityExecutionStatus.Closed;
  94: }
  95:  
  96: private Stream CreateResponseStream(SPWeb fileWeb, string nameFile, out string name)
  97: {
  98:     SPFile attachment = null;
  99:     attachment = fileWeb.GetFile(nameFile);
 100:     name = attachment.Name;
 101:     WebRequest req = WebRequest.Create(nameFile);
 102:     req.Headers.Add("Translate:f");
 103:     req.Credentials = CredentialCache.DefaultNetworkCredentials;
 104:     WebResponse response = req.GetResponse();
 105:     Stream msForm = response.GetResponseStream();
 106:  
 107:     return msForm;
 108: }
 109:  
 110: private static SmtpClient LoadSmtpInformation()
 111: {
 112:   string smtpServer = SPAdministrationWebApplication.Local.OutboundMailServiceInstance.Server.Address;
 113: return new SmtpClient(smtpServer);
 114: }

Veamos un poco el código. Primero obtenemos el Contexto Web actual. Luego usando  algunos métodos helpers construimos el mensaje (BuildMailMessage)

Después de crear el mensaje tratamos de cargar el adjunto especificado por la propiedad. Para esto usamos la propiedad AttachmentFileName para abrir el SPSite donde el archivo reside. Luego abrimos un SPWeb para obtener el archivo en cuestión. (dentro del método CreateResponseStream).

Desarrollamos los tres pasos definidos en los párrafos anteriores. Y finalmente establecemos el SMTP de nuestro Sharepoint (LoadSmtpInformation ) y con el método GetMailAddress obtenemos las direcciones de correo en caso de que en los parámetros nos llegue el Account Name.

4 - Registración de Custom Action.

Antes de usar nuestra Custom Action debemos, primero, registrarla en el Web.config de nuestro Sharepoint. Para hacer esto usaremos un Feature Reciver que ejecute esta acción durante la activación.

Como agregar un EventReciver:

 

image

 

Esto nos generará una clase Vemn.EventReceiver.cs en la cual vamos a implementar los siguientes métodos:

   1: public override void FeatureActivated(SPFeatureReceiverProperties properties)
   2: {
   3:     SPWebApplication webapp = (SPWebApplication)properties.Feature.Parent;
   4:     UpdateWebConfig(webapp, true);
   5:  
   6: }
   7:  
   8: public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
   9: {
  10:     SPWebApplication webapp = (SPWebApplication)properties.Feature.Parent;
  11:     UpdateWebConfig(webapp, false);
  12:  
  13: }
  14:  
  15: private void UpdateWebConfig(SPWebApplication webApp, bool featureActivated)
  16: {
  17:     SPWebConfigModification modification =
  18:        new SPWebConfigModification("authorizedType[@Assembly=\"Vemn.CustomActivity, Version=1.0.0.0, Culture=neutral, PublicKeyToken=7819e4834cee4b0e\"][@Namespace=\"Vemn.CustomActivity\"][@TypeName=\"*\"][@Authorized=\"True\"]", "configuration/System.Workflow.ComponentModel.WorkflowCompiler/authorizedTypes");
  19:  
  20:     modification.Owner = "Vemn.CustomActivity";
  21:     modification.Sequence = 0;
  22:     modification.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
  23:     modification.Value =
  24:          string.Format(CultureInfo.InstalledUICulture,
  25:          "<authorizedType Assembly=\"{0}\" Namespace=\"{1}\" TypeName=\"{2}\" Authorized=\"{3}\"/>",
  26:          new object[] { "Vemn.CustomActivity, Version=1.0.0.0, Culture=neutral, PublicKeyToken=7819e4834cee4b0e", "Vemn.CustomActivity", "*", "True" });
  27:  
  28:     if (featureActivated)
  29:         webApp.WebConfigModifications.Add(modification);
  30:     else
  31:         webApp.WebConfigModifications.Remove(modification);
  32:  
  33:  
  34:     SPFarm.Local.Services.GetValue<SPWebService>().ApplyWebConfigModifications();
  35: }

Esto agregará o removerá un nodo authorizedType en el Web.config de nuestro Sharepoint, para habilitar el uso de nuestra Action en el Workflow Designer.

Agregar una entrada Safecontrol en el manifiesto de nuestro Package.

Para asegurarnos de que Sharepoint Cargue de manera segura nuestra CustomAction es que añadimos la entrada SafeControl sobrescribiendo el archivo Package.Template.xml de la siguiente manera:

   1: <Assemblies>
   2:     <Assembly Location="Vemn.CustomActivity.dll" DeploymentTarget="GlobalAssemblyCache">
   3:         <SafeControls>
   4:             <SafeControl Assembly="$SharePoint.Project.AssemblyFullName$" Namespace="$SharePoint.Project.FileNameWithoutExtension$" TypeName="*" />
   5:         </SafeControls>
   6:     </Assembly>
   7: </Assemblies>

Ahora si ya estamos en condiciones de compilar, hacer el deploy y testear.

Creamos un Workflow Reusable y revisamos si nuestra Custom Acrivity está disponible para usar.

image

Deberíamos poder seleccionarla e insertarla como sentencia.

image

Conclusión

Como pudimos ver, Sharepoint 2010 nos permite extender las capacidades de la versión OOB. Esto sumado a los Workflows Reusables nos dan una gran flexibilidad para el diseño y desarrollo de soluciones que exigen considerados niveles de personalización.

Tags: , , , ,

.NET | Sharepoint | Visual Studio

Testing unitario y TDD con Visual Studio 2010

por Victor Passador  4. agosto 2011

“Creating a unit test helps a developer to really consider what needs to be done. Requirements are nailed down firmly by tests. There can be no misunderstanding a specification written in the form of executable code.” ExtremeProgramming.org

 

Un poco de contexto

Como generalmente sucede en todos los ámbitos, una de las cosas más difíciles de conseguir es el equilibrio, y la creación de Tests Unitarios no queda fuera de esta premisa.

Por un lado es imposible negar los beneficios que aporta contar con pruebas automatizadas, pero por otro lado también se hace difícil contradecir a aquellos que argumentan que es muy costoso su mantenimiento ante cambios en los requerimientos.

No es la idea de este post entrar en un debate interminable defendiendo una postura u otra, sólo me limitaré a decir que no somos fanáticos de los extremos y que si bien consideramos que es indispensable contar con un buen set de pruebas unitarias, tampoco perdemos de vista el costo de su mantenimiento y vamos adaptando el modelo según las necesidades y posibilidades.

Antes de ir directamente a la herramienta, repasemos algunas definiciones.

 

El ABC de las pruebas unitarias

Toda prueba unitaria que se precie de tal, deberá contar sí o sí con las siguientes características:

  • Ejecución rápida
  • Ambito limitado
  • Automatizable y repetible
  • Cualquiera puede ejecutarlo
  • Independiente
  • Alta cobertura de código

 

El ABC del TDD

En el Desarrollo Guiado por Pruebas (Test-Driven Development – TDD) se dice que no existe ningún motivo para escribir código salvo alguna prueba que esté fallando. Esto obliga (a modo de resumen) a trabajar con un ciclo de vida que incluye los siguientes pasos:

  • Ante cada nuevo requerimiento, escribir un test. El test inevitablemente fallará porque no hay código que implemente ese requerimiento todavía.
  • Escribir el código que pase el test. Se deben enfocar los recursos a que el test pase, sin preocuparse por cuestiones “superfluas” (.. del tipo “ya que estamos, agrego una sobrecarga que reciba tal parámetro porque probablemente lo necesite más adelante”). Tampoco por la elegancia del código. El único objetivo es que el test pase.
  • Refactorizar: Es aquí donde se modifca el código para mejorar cuestiones como mantenibilidad, limpieza de código duplicado, etc., sin afectar obviamente la funcionalidad. Esto se comprueba corriendo sucesivamente las pruebas (aquí se ve el beneficio de pruebas que se puedan ejecutar en poco tiempo).

 

Entrando ahora sí de lleno en la herramienta, veamos qué nos aporta en este sentido.

 

Ayudas en la edición de código

Como soporte a la práctica de TDD, Visual Studio 2010 cuenta con algunas funcionalidades orientadas a la mejora en la productividad en este ámbito:

Code Snippets: permiten rápidamente generar clases de test (“testc”) o métodos de test (“testm”)

imageimage

Al escribirse primero las pruebas, obviamente fallaran las referencias a las clases a testear, pero con la generación automática de código, se facilita la tarea de crear el esqueleto de esas clases:

image

image

Intellisense en modo Suggestion: En el modo al que estábamos acostumbrados a usar Intellisense (modo Completion), era fácil agregar una referencia a un tipo o variable existente, pero esto no es muy “TDD Friendly”. En VS2010 existe también el modo Suggestion (se activa con Ctrl+Alt+Space) que muestra, mientras estamos tipiando, un cuadro de texto sobre la lista de tipos sugeridos sin obligarnos a seleccionar ninguno.

image

Ejecución de pruebas unitarias

En cuanto a la ejecución de las pruebas unitarias, más allá de nuestro objetivo de ver muchos iconitos verdes, una de las metas buscadas es conseguir una alta cobertura de código. De nada sirve tener toda una batería de pruebas unitarias si estamos cubriendo (probando) menos de la mitad del código escrito.

La cobertura de código (Code Coverage) es una métrica importantísima que deberíamos obtener para poder certificar que nuestros casos de prueba están cubriendo la totalidad (o la mayor parte posible) del código de la aplicación.

Para habilitar los reportes de Code Coverage en VS2010 tenemos que modificar el archivo “Local.testsettings” y dentro de la opción “Data and Diagnostics” marcar el check correspondiente a “Code Coverage”

CodeCoverageSettings

Una vez ejecutadas las pruebas, en la ventana Code Coverage Results, podremos ver los porcentajes de ese indicador y a su vez, en el editor de código, ver resaltadas las líneas afectadas por la prueba.

CodeCoverageResults

Posts anteriores de esta serie:

Para ir calentando motores …

Contribuyendo a la mejora de calidad de software

Algunas referencias más:

http://geeks.ms/blogs/adiazmartin/archive/2010/01/12/m-225-s-r-225-pido-con-visual-studio-2010-intellisense-para-tdd.aspx

http://blogs.msdn.com/b/ukvsts/archive/2010/03/17/test-driven-development-with-visual-studio-2010.aspx

 

Hasta la próxima!

Tags: , ,

Testing | Visual Studio | ALM

Testing con VSTS 2010–Contribuyendo a la mejora de calidad de software

por Victor Passador  19. abril 2011

Como habíamos prometido, daremos inicio con este post a la serie de publicaciones con las que intentaremos mostrar de qué manera se puede incrementar la calidad del software que nuestra empresa produce, mejorando el proceso de testing en las diferentes formas que toma a lo largo del ciclo de vida de la aplicación.

Tipos de testing vs. herramientas de testing

En las primeras etapas del ciclo de vida de la aplicación, en aquellas donde se comienzan a implementar mayormente requerimientos no funcionales (APIs, frameworks, etc.), se requiere de un tipo de tester especializado, con altos conocimientos de programación, que genere casos de pruebas unitarias con un alto grado de automatización (cuando digo alto apunto al 100% de automatización)

A medida que las iteraciones avanzan y se empiezan a obtener las primeras piezas funcionales, aparece la necesidad de un testing totalmente diferente, opuesto, donde el tester necesita altos conocimientos del proceso de negocio y pocos o nulos conocimientos de programación.

Si trazáramos una línea, podríamos decir entonces que tenemos en una punta un testing de tipo “especializado”, con mayoría de casos de test automatizados construidos por expertos en programación, y en la otra punta un testing más “generalista” con gran cantidad de casos de test manuales a cargo de especialistas en el negocio en cuestión, con toda una escala de grises en el medio.

Generalista vs. Especialista 001

Según algunos estudios, se indica que la mayoría de las herramientas de apoyo al testing apuntan a la parte “especialista”, siendo que el grueso del esfuerzo que realiza la empresa se ubica en la parte “generalista”

Generalista vs. Especialista 002

VSTS 2010 como solución ALM ante la problemática del testing

Con este enfoque como guía, iremos mostrando en sucesivas entregas, de qué manera VSTS 2010 contribuye a la mejora del proceso de testing en todo el espectro.

Comenzaremos mostrando las herramientas que propone la solución de MS desde el extremo más especialista, hasta llegar al punto opuesto, abarcando los siguientes tópicos:

  • Testing unitario y las mejoras para aplicación de técnicas de TDD (Test Driven Development)
  • Intellitrace
  • Coded UI Test
  • Lab Management
  • Integración Continua

 

Hasta la próxima !

Tags: , , ,

ALM | TFS | Testing | Visual Studio

Visual Studio 2010 and .NET Framework 4 Training Kit

por Victor Passador  26. marzo 2011

Ya van quedando cada vez menos excusas para no estar entrenado con lo último. Acaba de salir publicado un Training Kit gratuito para VS 2010 y .NET 4.0.

Es un download de un poco más de 400MB que en disco se transforman en casi 2GB de presentaciones, papers, hand-on-labs, código de ejemplo, etc.

Pueden descargarlo desde aquí.

Enjoy !

Tags: ,

.NET | Visual Studio

Testing con VSTS 2010 – Para ir calentando motores …

por Victor Passador  18. febrero 2011

Estamos preparando una serie de post dedicados a testing con VSTS 2010 en las diferentes variantes en que se presenta a lo largo del ciclo de vida de la aplicación, y para ir introduciendo el tema quisiera dejarles unos links para que tengan a mano.

Se trata del blog de Mathew Aniyan, PM de Microsoft. En su blog, Mathew ha estado publicando una serie de Tips relacionado a lo que se conoce como Coded UI Test (Pruebas programáticas de Interfaz de Usuario).

Al momento de publicar este post, llegó hasta el N° 8, posteado con fecha enero/2010. Esperamos que retome la iniciativa y siga publicando más trucos.

Pueden ver la serie de tips aquí.

Saludos !

Tags: , , ,

ALM | TFS | Visual Studio | Testing

TFS 2010–”The server returned content type text/html, which is not supported” al hacer Get Latest Version

por Victor Passador  20. enero 2011

En realidad este error lo obtuvimos inicialmente al intentar encolar una build en un proyecto recientemente “trasplantado” desde otro servidor. Obviamente en el servidor de origen no había problemas.

Como era muy escasa la información registrada en los diferentes logs, investigamos un poco y nos dimos cuenta que el error ocurría cuando el build server intentaba descargar la propia definición del build (basada en el DefaultTemplate.xml) para hacer su trabajo. Podíamos obtener ese mismo error desde Visual Studio intentando descargar cualquier otro template de la carpeta BuildProcessTemplates del Source Control.

Sabiendo que no era un tema del build server sino algo más genérico, volvimos a buscar y apareció la solución. Hay que seguir estos pasos:

  • Detener IIS
  • Borrar la caché local del servidor TFS (buscar en C:\Program Files\Microsoft Team Foundation Server 2010\Application Tier\Web Services\_tfs_data)
  • Reiniciar IIS

 

Voilà !!

Hasta la próxima !!

Tags: , , ,

ALM | TFS | Visual Studio | Build Automation

Versiones de VS 2010

por Daniel Laco  15. abril 2010

Siempre es motivo de preguntas el tema de que trae cada versión de Visual Studio y cuanto cuesta cada una de ellas. Aqui les dejo un link donde ver una matriz con las comparaciones de cada versión de Visual Studio 2010.

Tags:

Visual Studio

Extensibilidad en el Entity Framework Designer en VS2010

por Daniel Laco  23. marzo 2010

 

Una de las cosas mas intersantes que tiene el diseñador del EF en Visual Studio 2010 es la posiblidad de extender las funcionalidades mediante el agregado de nuevas propiedades a una entidad, o la posibilidad de poder interceptar el momento de grabar un archivo edmx, u otras opciones mas.

Esto es muy útil para cuando hay equipos de desarrollo extendidos, o varios proveedores diferentes trabajando en una empresa  y se quiere mantener una uniformidad al momento de escribir código.

Asi que mediante un ejemplo sencillo explicaremos los pasos necesarios para poder implementar una propiedad en las entidades que nos permita definir si una entidad es auditada o no.

Mostraremos como agregar esa propiedad y como utilizarla posteriomente en nuestro código.

Aclaración: todo lo mostrado en este artículo, está realizado sobre la RC de VS2010.

Como paso siguiente, manos a la obra!!

El equipo de ADO.NET publicó en CodePlex el ADO.NET Entity Data Model Designer Extension Starter Kit que nos permite hacer mucho mas fácil la tarea, y ya tiene varias opciones configuradas por default.

Podemos por ejemplo: 

      • Que el ADO.NET Entity Data Model Wizard agregue anotaciones personalizadas en cada tipo de entidad. El fundamento del tema lo encontramos en un ar tículo del blog de ADO.NET referido a diseño en http://blogs.msdn.com/efdesign/archive/2008/08/12/structural-annotations-one-pager.aspx (en inglés)
      • Que el ADO.NET Entity Data Model Wizard agregue anotaciones personalizadas durante las actualizaciones en cada entidad que se agregue al modelo conceptual.
      • Que el ADO.NET Entity Data Model Wizard agregue anotaciones personalizadas cuando las entidades son seleccionadas en el Designer o en el Model Browser.
      • También se puede extender el modo en se que cargan y graban los archivos .admx.
      • O permitir que el Diseñador grabe o carge archivos en un formato personalizado.

 

Paso 1: Instalar la extensión que se baja desde CodePlex

ExtensionManager

Paso 2: Crear el proyecto.

CrearProyecto

Paso 3: Una vez que tenemos el proyecto creado, podemos hacer las modificaciones que nos interesan. En este caso vamos a crear una propiedad para cada Entidad que nos permita habilitar o deshabilitar la auditoría. Creamos entonces una clase AuditarProperty:

using System;
using System.Linq;
using System.Xml.Linq;
using System.ComponentModel;
using Microsoft.Data.Entity.Design.Extensibility;
 
namespace EFEjemploExtension
{
    /// <summary>
    /// </summary>
    class AuditarProperty
    {
        internal static readonly string _namespace = "http://schemas.vemn.com/EFExtensions";
        internal static XName _xnMyNamespace = XName.Get("AuditarProperty", _namespace);
        internal const string _category = "Extensiones";
 
        private XElement _parent;
        private PropertyExtensionContext _context;
 
        public AuditarProperty(XElement parent, PropertyExtensionContext context)
        {
            _context = context;
            _parent = parent;
        }
 
        // Esta propiedad This property is saved in the conceptual content of an .edmx document in the following format:
        // <EntityType>
        //  <!-- other entity properties -->
        //  <MyNewProperty xmlns="http://schemas.vemn.com/MyNewProperty">True</MyNewProperty>
        // </EntityType>
        [DisplayName("Auditar Entidad")]
        [Description("Configura si la entidad es Auditable")]
        [Category(AuditarProperty._category)]
        [DefaultValue(false)]
        public bool AuditarEntidad
        {
            get
            {
                bool propertyValue = false;
                if (_parent.HasElements)
                {
                    XElement lastChild = _parent.Elements().Where<XElement>(element => element != null && element.Name == AuditarProperty._xnMyNamespace).LastOrDefault();
                    if (lastChild != null)
                    {
 
                        bool boolValue = false;
                        if (Boolean.TryParse(lastChild.Value.Trim(), out boolValue))
                        {
                            propertyValue = boolValue;
                        }
                    }
                }
                return propertyValue;
            }
 
            set
            {
                bool propertyValue = value;
 
 
                using (EntityDesignerChangeScope scope = _context.CreateChangeScope("Set AuditarEntidad"))
                {
                    if (_parent.HasElements)
                    {
                        XElement lastChild = _parent.Elements().Where<XElement>(element => element != null && element.Name == AuditarProperty._xnMyNamespace).LastOrDefault();
                        if (lastChild != null)
                        {
 
                            lastChild.SetValue(propertyValue.ToString());
                        }
                        else
                        {
                            _parent.Elements().Last().AddAfterSelf(new XElement(_xnMyNamespace, propertyValue.ToString()));
                        }
                    }
                    else
                    {
                        _parent.Add(new XElement(_xnMyNamespace, propertyValue.ToString()));
                    }
  scope.Complete();
                }
            }
        }
    }
}

 

Paso 4: Creamos el Factory.

using System.Xml.Linq;
using System.ComponentModel.Composition;
using Microsoft.Data.Entity.Design.Extensibility;
 
namespace EFEjemploExtension
{
    [PartCreationPolicy(CreationPolicy.Shared)]
    [Export(typeof(IEntityDesignerExtendedProperty))]
    [EntityDesignerExtendedProperty(EntityDesignerSelection.ConceptualModelEntityType)]
    class AuditarPropertyFactory : IEntityDesignerExtendedProperty
    {
        /// <summary>
        /// </summary>
        /// <param name="element"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        public object CreateProperty(XElement element, PropertyExtensionContext context)
        {
            return new AuditarProperty(element, context);
        }
    }
}

 

Paso 5: Una vez que hemos terminado, se compila y queda el arc hivo .vsix para ser in stalado en Visual Studio. En el Extension Manager veremos algo asi:

ExtensionInstalada>

Paso 6: Ya tenemos diponible la propiedad en el Diseñador de EF. Cuando agreguemos nuevas tablas o cuando se haga una actualización, las entidades tendrán ahora una nueva propiedad. Si seleccionamos una entidad veremos algo como:

AuditorPropiedad

 Paso 7: Por último, unos extension methods para poder consultar la Metadata del modelo que  nos permita obtener el valor de la propiedad:

 

public static class EFExtension
{
    /// <summary>
    /// Retorna el valor de una propiedad generada en la extensión del Diseñador del Entity Framework
    /// </summary>
    /// <typeparam name="T">Entidad para obtener la propiedad</typeparam>
    /// <param name="workspace"></param>
    /// <param name="propertyName">Ejemplo: http://schemas.vemn.com/EFExtensions:AuditarProperty</param>
    /// <returns></returns>
    public static bool GetExtentionPropertyAsBoolean<T>(this MetadataWorkspace workspace, string propertyName)
    {
        return (GetExtentionPropertyInternal<T>(workspace,propertyName).ToString().ToLower() == "true") ;   
    }
    /// <summary>
    /// Retorna el valor de una propiedad generada en la extensión del Diseñador del Entity Framework
    /// </summary>
    /// <typeparam name="T">Entidad para obtener la propiedad</typeparam>
    /// <param name="workspace"></param>
    /// <param name="propertyName">Ejemplo: http://schemas.vemn.com/EFExtensions:AuditarProperty</param>
    /// <returns></returns>
    public static string GetExtentionPropertyAsString<T>(this MetadataWorkspace workspace, string propertyName)
    {
        return GetExtentionPropertyInternal<T>(workspace, propertyName).ToString();
    }
 
    /// <summary>
    /// Retorna el valor de una propiedad generada en la extensión del Diseñador del Entity Framework
    /// </summary>
    /// <typeparam name="T">Entidad para obtener la propiedad</typeparam>
    /// <param name="workspace"></param>
    /// <param name="propertyName">Ejemplo: http://schemas.vemn.com/EFExtensions:AuditarProperty</param>
    /// <returns></returns>
    public static int  GetExtentionPropertyAsInt<T>(this MetadataWorkspace workspace, string propertyName)
    {
        return Convert.ToInt32(GetExtentionPropertyInternal<T>(workspace, propertyName));
    }
    /// <summary>
    /// Retorna el valor de una propiedad generada en la extensión del Diseñador del Entity Framework
    /// </summary>
    /// <typeparam name="T">Entidad para obtener la propiedad</typeparam>
    /// <param name="workspace"></param>
    /// <param name="propertyName">Ejemplo: http://schemas.vemn.com/EFExtensions:AuditarProperty</param>
    /// <returns></returns>
    public static object GetExtentionPropertyAsObject<T>(this MetadataWorkspace workspace, string propertyName)
    {
        return GetExtentionPropertyInternal<T>(workspace, propertyName);
    }
    /// <summary>
    /// Retorna el valor de una propiedad generada en la extensión del Diseñador del Entity Framework
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="workspace"></param>
    /// <param name="propertyName">Ejemplo: http://schemas.vemn.com/EFExtensions:AuditarProperty</param>
    /// <returns></returns>
    private static object GetExtentionPropertyInternal<T>(MetadataWorkspace workspace, string propertyName)
    {
        StructuralType objectSpaceType;
        workspace.LoadFromAssembly(typeof(T).Assembly);
        if (!workspace.TryGetItem<StructuralType>(typeof(T).FullName, DataSpace.OSpace, out objectSpaceType))
        {
            throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, "No se puede encontrar este tipo de datos en la Metadata", typeof(T)));
        }
        StructuralType edmType = workspace.GetEdmSpaceType(objectSpaceType);
 
        MetadataProperty mp = edmType.MetadataProperties.Where(x => x.Name == propertyName).Single();
        XElement xc = (XElement)mp.Value;
        return xc.Value;
    }
 
}

 

La forma de utilizar esta propiedad en nuestro codigo es:

NorthwindEntities ctx = new NorthwindEntities();
bool audita = ctx.MetadataWorkspace.GetExtentionPropertyAsBoolean<Customers>("http://schemas.vemn.com/EFExtensions:AuditarProperty");
//Hago algo con la auditoria

 

De esta forma, hemos visto como de manera relativamente sencilla se puede extender el Entity Designer y de que manera nos puede ayudar al momento de estandarizar configuraciones en las entidades.

Por supuesto que la cantidad de escenarios e ideas posibles con esta funcionalidad son muchas como pueden ser:

  • Revisión o Comenarios sobre los datos.
  • Seguridad en los Datos.
  • Agregado de Atributos del CLR
  • Validaciones de Anotaciones.
  • etc.
  • etc.

 

Aqui pueden bajar el código de ejemplo utilizado en el artículo.

 

 

 

 

Tags: , ,

ADO.NET | Entity Framework | ORM | Visual Studio

Como depurar Store Procedures, Triggers y Functions de SQL Server desde Visual Studio 2008

por emmanuel  22. marzo 2010

Una tarea imprescindible en el desarrollo y programación con SQL Server es la depuración (debugging) de Store Procedures, Triggers y Functions. El debugging nos permite ejecutar paso a paso código T-SQL, establecer breakpoints, examinar el contenido de las variables y modificarlos en tiempo de depuración, etc.

Anteriormente, los desarrolladores escribían y depuraban con el Analizador de consultas de SQL Server 2000 (Query Analyzer).

Luego, el SQL Server Management Studio 2005 que lo reemplazó no tenía ningún depurador, por lo cual para poder depurar código T-SQL se utilizaba el depurador que tiene el Visual Studio 2005.

A partir de la versión 2008 de SQL Server, las capacidades de debugging ya vienen incluidas en el Management Studio (solo funcionan con una conexión contra una instancia
de SQL Server 2008), sin embargo en ciertas ocasiones es más conveniente utilizar Visual Studio para el debugging de objetos de SQL.

Requerimientos

  • Versión Professional o Team Edition de Visual Studio.
  • Es importante destacar que al depurar se detendrán todos los threads de ejecución de SQL, de modo que no es recomendable depurar en un servidor que se encuentre en producción.
  • Activar el debugging de CLR en SQL: para esto se debe primero crear una conexión a la base de datos mediante la ventana "Server Explorer" (Menú "View"). Luego dar click derecho sobre la conexión y tildar la opción "Allow SQL/CLR Debugging"

 

EnableDebugging

Permisos

Para poder depurar código SQL desde Visual Studio 2008 existen dos cuentas de usuario para considerar:

  • La cuenta de la aplicación: es la cuenta de usuario sobre la cual se ejecuta Visual Studio. Esta cuenta es una cuenta de usuario de Windows, y debe ser miembro del grupo sysadmin en el SQL Server a ser depurado.
  • La cuenta de conexión: es la identidad usada para conectarse con SQL Server (Definida en el dialogo de conexión del "Server Explorer", o en el connection string). Esta cuenta puede ser una cuenta de usuario de Windows usando Windows Authentication (en este caso es la misma sobre la cual corre Visual Studio), o puede ser una cuenta de conexión SQL, en cuyo caso debe ser miembro del rol sysadmin para poder depurar.

 

Si no se cumplen los permisos requeridos para la Depuración de SQL Server, y se intenta depurar código de SQL Server, se obtiene el siguiente error:
User 'dbo' could not execute stored procedure 'master.dbo.sp_enable_sql_debug' on SQL Server VSQL01.

Pasos para la depuración

  • Ubicar el objeto a depurar en la ventana "Server Explorer".
  • Dar click derecho sobre el objeto que se desea depurar y seleccionar "Step into ..." ("Step into Store Procedure", "Step into Function", etc.)

 

StepInto 

  • Si requiere parámetros, aparecerá un cuadro de diálogo que permite asignar los valores para cada parámetro.

 

Parametros

  • En todo momento se puede visualizar o modificar el valor de variables locales, para lo cual podemos utilizar la ventana "Locals" de Visual Studio, disponible el menú "Debug" (solo cuando la depuración ha sido iniciada), o también mediante la ventana "Watch".

 

Debug

 

 

  • Para depurar un Trigger, se debe tener en cuenta que no se le puede invocar directamente, por lo cual, lo que se puede hacer es crear un Store Procedure cuyo código fuente invoque al Trigger, y utilizar Step Into (F11 - paso a paso por instrucciones) en vez de Step Over (F10 - paso a paso por el proceso actual) para la depuración, de manera de entrar en el código del Trigger en cuestión.

 

image

En el ejemplo se muestra un store procedure que ejecuta un UPDATE sobre la tabla que tiene el trigger a depurar. Al presionar F11 sobre la instrucción UPDATE se ingresa en modo depuración al código del trigger.

 

image

Tags: ,

SQL Server

Acerca de los Autores

Este es el blog del equipo de VEMN SA 
Presentaremos temas que nos parezcan de interés sobre tecnología .NET, Procesos y Metodologías y todo aquello relacionado con el proceso de desarrollo de Software

Month List

BlogRoll

Download OPML file OPML