Lanzamiento Virtual “La eficiencia en tus manos”

por Daniel Laco  16. abril 2010

Hola a todos, quiero recordarles que este 21 de Abril será el evento virtual de lanzamiento de 11 importantes productos y donde muchos MVPs estaran como speakers y como expertos para resolver tus dudas.

clip_image003_010b7c0b-5450-4bae-8599-79e3ef3ca26b

 clip_image001_0d130959-9982-4736-8319-5fff7ce34ca4

 

 

clip_image002_f403aaf7-c2c3-4e8d-beb7-f7bb8c027891

Regístrate ya!

Tags:

.NET | ASP.NET | Entity Framework | General | Sharepoint | Visual Studio

Cuál es la mejor forma de realizar procesos batch con Entity Framework?

por Daniel Laco  16. abril 2010

Es muy común que en las aplicaciones sea necesario realizar procesos de actualizaciones masivas. Siempre está la tentación de usar EF para hacer estos procesos, ya que es muy fácil buscar y actualizar registros. Pero en el caso de EF el costo que se paga por toda la infraestructura que tiene es muy alto.

En el caso de procesos masivos, si hay que actualizar o borrar un registro, primero hay que traerlo (esto implica que EF mantenga el tracking con el ObjectStateManager) posteriormente hacer la actualización o borrado y por último realizar la actualización con el ObjectContext.

Resumiento, para realizar procesos batch lo mejor es NO USAR Entity Framework !!

Pero que opciones tengo entonces?

Voy a mostrar 2 alternativas de las muchas que hay, simulando que hay que leer un archivo .TXT de longitud fija  y posteriormente hay que realizar una actualización de miles de registros en una tabla de la base de datos.

El escenario es que recibo un archivo con códigos de productos de la empresa y códigos de productos de proveedores externos. El proceso que hay que realizar es: leer el archivo y actualizar los códigos de de los proveedores externos en cada registro de los productos de la empresa.

Para esto usaremos como ayuda la excelente libreria FileHelpers realizada por Marcos Meli

El formato en que viene el archivo es el siguiente:

 

/// <summary>
/// Archivo de Unión de Códigos de Productos de la Empresa y Proveedores
/// </summary>
 
[FixedLengthRecord(FixedMode.AllowLessChars)]
public class UnionProductos
{
    [FieldFixedLength(7)]
    public int EmpresaCodigo;
 
    [FieldFixedLength(7)]
    public string Proveedor1Codigo;
 
    [FieldFixedLength(7)]
    public int Proveedor2Codigo;
 
}

 

El proceso de lectura con FileHelpers:

FileHelperEngine engine = new FileHelperEngine(typeof(UnionProductos));
UnionProductos[] UnionLeido = engine.ReadFile(nombreArchivo) as UnionProductos[];
 
 

Opción 1

Recorrer el array de Productos y ejecutar un Store Procedure con la actualización correspondiente:

 

string stringDeConexion = "XXXXXXXXXXXXXXXX";
using (SqlConnection conn = new SqlConnection(stringDeConexion))
{
    conn.Open();
    SqlCommand cmd = new SqlCommand("nombreStore", conn);
    cmd.Parameters.Add("@empresaCodigo", SqlDbType.Int);
    cmd.Parameters.Add("@proveedor1Codigo", SqlDbType.VarChar);
    cmd.Parameters.Add("@proveedor2Codigo", SqlDbType.Int);
 
 
    foreach (UnionProductos producto in UnionLeido)
    {
        cmd.Parameters[0].Value = producto.EmpresaCodigo;
        cmd.Parameters[1].Value = producto.Proveedor1Codigo;
        cmd.Parameters[2].Value = producto.Proveedor2Codigo;
 
        cmd.ExecuteNonQuery();
 
    }
}

 

Por supuesto el Store hace algo asi como:

UPDATE Productos 
SET Proveedor1Codigo = @proveedor1Codigo, 
    Proveedor2Codigo = @proveedor2Codigo
WHERE Productos.Codigo = @empresaCodigo

 

Opción 2

Esta es la mas performante de las 3 opciones. El único problema es que solo es posible en SQL Server 2008.

Está implementada utilizando una Tabla como Parámetro y realizando toda la actualización mediante una sentencia SQL.

Veamos como hacelo:

Se crea un tipo de dato Table en SQLServer

CREATE TYPE UnionProductosTbl AS TABLE 
(
    EmpresaCodigo int,
    Proveedor1Codigo varchar(7),
    Proveedor2Codigo int
)

 

Se crea el Procedimiento Almacenado usando ese tipo como parámetro:

CREATE PROCEDURE ActualizarCodigosProveedores
    @unionProductosTbl dbo.UnionProductosTbl READONLY
AS
BEGIN
 
    UPDATE Productos p
    SET p.Proveedor1Codigo = tbl.Proveedor1Codigo,
    p.Proveedor2Codigo = tbl.Proveedor2Codigo
    FROM Productos
    INNER JOIN @unionProductosTbl tbl ON p.Codigo = tbl.empresaCodigo
    
END

 

Y el proceso en el código:

//Lectura del Archivo
               FileHelperEngine engine = new FileHelperEngine(typeof(UnionProductos));
               //Devuelve como DataTable
               DataTable unionLeidoDT = engine.ReadFileAsDT(nombreArchivo);
 
               //El proceso de actualización
               string stringDeConexion = "XXXXXXXXXXXXXXXX";
               using (SqlConnection conn = new SqlConnection(stringDeConexion))
               {
                   conn.Open();
                   SqlCommand cmd = new SqlCommand("nombreStore", conn);
                   //Se define un parámetro de tipo Structured para que reciba un DataTable
                   SqlParameter par = cmd.Parameters.Add("@unionProductosTbl", 
                       SqlDbType.Structured);
                   par.Value = unionLeidoDT;
                   cmd.ExecuteNonQuery();
               }
               
 

Realizando el proceso con 17.000 registros los resultados fueron los siguientes (los tiempos son orientativos):

Opción Tiempo

Utilizando EF

8 minutos

Utilizando ADO.NET nativo procesando registro a registro con un Store Procedure

4 minutos

Utilizando un Store Procedure en SQL 2008 con una tabla como parámetro

4 segundos !!

 

Para finalizar, la opción de realizar el proceso batch usando una tabla como parámetro de un Store es la mejor opción si la prioridad es la performance de la solución.

Tags: ,

ADO.NET | Entity Framework | SQL Server

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

Formulario Cabecera-Detalle en Asp.Net MVC con el default ModelBinder

por Andres Stang  30. marzo 2010

 

Descargar Codigo Fuente

Tal vez les suceda que alguna vez en su feliz programación de Asp.Net MVC quieren generar un formulario de entrada de datos que permita, sin los molestos tiempos de carga de postback, agregar N cantidad de elementos.

Es decir, generar un modelo de Cabecera-Detalle en el cliente, y luego poder leerlo del lado servidor utilizando solo MVC y Javascript. No, me refiero a SOLO MVC, es decir sin un ModelBinder personalizado, que sería la opción que primero nos viene a la mente gracias a la extensibilidad propuesta por el framework.

Podemos imaginarnos el siguiente ejemplo de modelo:

   1: public class Materia
   2: {
   3:     public int Codigo { get; set; }
   4:     
   5:     public string Nombre { get; set; }
   6:  
   7:     public List<Alumno> AlumnosInscriptos { get; set; }
   8: }

Supongamos que queremos permitir al usuario generar una nueva materia y asignarle alumnos a la misma desde una lista de selección. Para simplificar el alumno va a estar definido por:

   1: public class Alumno
   2: {
   3:     public int Matricula { get; set; }
   4:  
   5:     public string Nombre { get; set; }
   6: }

 

Entonces realizamos el siguiente formulario web:

 

En el mismo vamos a poder cargar las propiedades de la materia y mediante javascript permitiremos que se agreguen los alumnos al seleccionarlos del combo al presionar “Agregar”.

Pero… como hacemos luego para leer las propiedades de la materia y los N alumnos seleccionados del lado del servidor?

Muy fácil, utilizando el ModelBinder por defecto de MVC. Es decir, el framework ya viene preparado para este escenario, solo que (a mi apreciación) está poco documentado.

Es decir, en nuestro método de controlador solo tendremos que colocar la siguiente definición:

   1: [AcceptVerbs(HttpVerbs.Post)]
   2: public ActionResult Index(Materia nuevaMateria)
   3: {
   4:     // HACER ALGO CON LA NUEVA MATERIA
   5:  
   6:     ViewData["CantidadAlumnos"] = nuevaMateria.AlumnosInscriptos.Count;
   7:  
   8:     return View("Alumnos");
   9: }

Y automáticamente podemos leer la instancia de la clase Materia con sus alumnos relacionados.

Donde está el secreto???

Lo importante es que al generar las filas dinámicamente de la tabla de alumnos también generemos los inputs para los valores de los alumnos que vayamos agregando. Por ejemplo, al agregar la fila nueva también insertaremos dos inputs ocultos de html con los valores que requiere la clase Alumno, es decir Matricula y Nombre:

   1: <input type="hidden" value="22" name="AlumnosInscriptos[1].Matricula" 
   2:     id="AlumnosInscriptos[1].Matricula" />
   3: <input type="hidden" value="Pedro" name="AlumnosInscriptos[1].Nombre" 
   4:     id="AlumnosInscriptos[1].Nombre" />

Podemos observar la particularidad del nombre y id del tag, es decir, debemos llamarlos de la siguiente manera:

<NombrePropiedadPadre>[indice].<NombrePropiedadHijo>

O para nuestro ejemplo:

AlumnosInscriptos[0].Nombre

De esta manera, el ModelBinder interpretara que se trata un elemento perteneciente a nuestra colección y nos facilitara el trabajo.

 

 

Pongo a disposición el código fuente perteneciente al ejemplo de este post:

ListBinder.zip (264,49 kb)

Happy Programming!

Tags: ,

ASP.NET | Desarrollo Web

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

Scripts seguros: Una historia de Transacciones, Stored Procedures y GOs

por leonardo  19. marzo 2010

Cuando trabajamos con bases de datos, tarde o temprano llega la necesidad de tener que preparar scripts para actualizar el modelo de datos en el cliente, en un servidor no accesible, etc. Esto trae una carga importante de riesgos, sobre todo de pérdida de información o de dejar la base en un estado inconsistente.

Para asegurarnos de que todo quede en un estado conocido, en principio podemos abrir una transacción y poner todo el script en un bloque TRY-CATCH como este:

BEGIN TRY
    BEGIN TRANSACTION
    --
    -- Poner acá el script con los cambios de la base
    --
    -- Aceptar los cambios de la transacción
 
    COMMIT TRANSACTION
END TRY
 
BEGIN CATCH
    -- Deshacer los cambios de la transacción
    ROLLBACK TRANSACTION
    --
    -- Manejar el error o relanzarlo
    --
END CATCH

Ahora, supongamos que nuestros cambios son los siguientes:

CREATE TABLE Sucursales (
    IdAlmacen INT,
    Direccion VARCHAR(100)
)
GO
 -- Otros cambios
GO
 
DELETE Articulos
WHERE IdCategoria = 9999
GO
 
-- Cambios sensibles y peligrosos
GO
CREATE PROCEDURE HacerNegocios AS
BEGIN
    --
    -- Aca va la formula mágica de cada uno
    --
END
GO
 
-- ...mas cambios peligrosos!
GO

 

Para ponerlo dentro del bloque TRY-CATCH, la única consideración seria borrar los "GO" ya que sino la transacción y el TRY quedarían incompletos, y el script no ejecutaría correctamente.

Nota: La palabra "GO" no es parte del lenguaje TSQL sino un comando (bastante estándar) interpretado por las aplicaciones como un separador de lotes de instrucciones. Para mas información: http://msdn.microsoft.com/es-es/library/ms188037.aspx .

Con esto surge un problema, el CREATE/ALTER PROCEDURE (también aplicado a Functions y Triggers) debe ser la primera instrucción del lote debido a una restricción de sintáxis de SQL Server

Para salvar este inconveniente, una solución es ejecutar la creación del stored procedure con Dynamic SQL. Quedando asi:

EXEC(N'CREATE PROCEDURE HacerNegocios AS
BEGIN
    --
    -- aqui va la formula magica de cada uno
    --
    SELECT * FROM CC
END')

Con esto, no hay ningún problema de crear o modificar un stored procedure dentro de una transacción.

BEGIN TRY
    BEGIN TRANSACTION
    
    CREATE TABLE Sucursales (
    IdAlmacen INT,
    Direccion VARCHAR(100)
    )
 
    -- Otros cambios
 
    DELETE Articulos
    WHERE IdCategoria = 9999
 
    -- Cambios sensibles y peligrosos
 
    EXEC(N'CREATE PROCEDURE HacerNegocios AS
    BEGIN
        --
        -- aqui va la formula mágica de cada uno
        --
    END')
 
    -- ...mas cambios peligrosos!
    
    -- Aceptar los cambios de la transacción
    COMMIT TRANSACTION
END TRY
BEGIN CATCH
    -- Deshacer los cambios de la transacción
    ROLLBACK TRANSACTION
 
    --
    -- Manejar el error o relanzarlo
    --
END CATCH
 

Espero que este pequeño "truco" facilite la generación de scripts para hacer actualizaciones en las bases de datos.

Tags:

SQL Server

22 Librerias para gráficos y diagramas en la web

por Daniel Laco  17. marzo 2010

Siguiendo con la idea de tener reunido en el blog, las direcciones de plugins y librerias para gráficos en la web.

Me encontré con esta excelente lista http://www.onextrapixel.com/2009/11/24/22-awesome-visualization-libraries-charts-and-diagrams/

Aquí hay librerias para JQuery, pero tambien hechas en Flash, etc.

 

Tags: , ,

ASP.NET | Desarrollo Web | Javascript | JQuery

Que hay de nuevo en VS2010?

por Daniel Laco  4. marzo 2010

Ya se viene la liberación de la versión final de Visual Studio 2010 y .NET 4.0 y uno siempre quiere tener una vista rápida de los cambios, agregados, etc.

En http://msdn.microsoft.com/en-us/library/bb386063(VS.100).aspx pueden encontrar un listado de todos los temas nuevos de esta plataforma.

 

Tags: , , , ,

.NET | ADO.NET | ASP.NET | Entity Framework | Visual Studio | WCF | WinForms

La correcta elección de las KEYWORDS en SEO (Search Engine Optimization)

por Alejandra Federico  17. febrero 2010

A la hora de querer seleccionar cuáles son aquellas palabras claves que debemos utilizar, para que nuestro sitio web este posicionado entre los primeros en los resultados de una búsqueda, se nos hace realmente dificil.

Es por ello que aquí les dejo algunas herramientas que quizás les sirva de ayuda para la selección de las palabras claves:

  • http://www.keyworddiscovery.com/search.html Esta herramienta nos sirve permite descubrir las diferentes palabras que los usuarios están utilizando en los buscadores, relacionada con la palabra que escribimos
  • http://www.kwmap.net/  Es una herramienta gráfica que muestra las relaciones entre los término de búsqueda como un gráfico.
  • http://freekeywords.wordtracker.com/ Esta herramienta te dará una lista con al menos 100 palabras relacionada con las palabras que buscamos y, lo más importante es que lo hace en orden de importancia según la cantidad de veces buscada.
  • http://www.abakus-internet-marketing.de/tools/topword.htm Es una herramienta que analiza las palabras claves y frases de las palabras claves de un sitio. Nos puede servir para estudiar nuestro propio sitio.

 

Tags: , ,

ASP.NET | Desarrollo Web

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