Jason Ulloa
Ideas de Chalu... el blog de .NET

Membership Provider Parte 3. Creación del Membership Provider Personalizado

Desarrollo de la Clase para obtener los datos de configuración

En mis post anterior Membership Provider Parte 2. Creación de un Modelo de Bases de datos vimos como desarrollar un modelo de bases de datos para el Membership Provider reducido e independiente del modelo original que se crea con asp.net, pero que contiene todo lo necesario para asegurar correctamente una aplicación.

En este post daremos inicio con la creación de las clases que nos permitirán utilizar ese modelo de bases de datos con el Membership provider, pero esta vez personalizado y basado en Linq.

Nuestra primer clase se llamara ProviderUtils y será la encargada de recuperar los valores de configuración desde el web.config para ser utilizados en la clase Membership. Esta clase la declararemos como internal, de modo que solo pueda ser utilizada dentro del ámbito del espacio de nombres de la librería

   1: internal static class ProviderUtils
   2:     {}

 

Posterior a la declaración de la clase, crearemos el primer método, GetConfigValue que leerá los valores de configuración desde el archivo. No voy a entrar en muchos detalles pues en el código esta bien comentado que parámetros recibe y cual es el resultado esperado

   1: /// <summary>
   2:         /// A helper function to retrieve config values from the configuration file.
   3:         /// </summary>
   4:         /// <param name="config">Provider configuration.</param>
   5:         /// <param name="configKey">Key of the configuration that should be read.</param>
   6:         /// <param name="defaultValue">Default value being used if the config does not exist.</param>
   7:         /// <returns>Configuration value or default value if not exisiting.</returns>
   8:         public static object GetConfigValue(NameValueCollection config, string configKey, object defaultValue)
   9:         {
  10:             object configValue;
  11:  
  12:             try
  13:             {
  14:                 configValue = config[configKey];
  15:                 configValue = string.IsNullOrEmpty(configValue.ToString()) ? defaultValue : configValue;
  16:             }
  17:             catch
  18:             {
  19:                 configValue = defaultValue;
  20:             }
  21:  
  22:             return configValue;
  23:         }

El segundo método se denominara EnsureApplication, el cual se encargara de verificar que la aplicación que se especifico en la configuración del Membership Provider en el web.config exista, en caso de no existir entonces lo creara para asegurarse de que la aplicación no presente problemas

   1: /// <summary>
   2:        /// Ensure that application exists. If not -> create new application.
   3:        /// </summary>
   4:        /// <param name="applicationName">Application name.</param>
   5:        /// <param name="context">Entity Framework data context.</param>
   6:        /// <returns>The application object</returns>
   7:        public static Application EnsureApplication(string applicationName, Entities context)
   8:        {
   9:            Application application = context.Application.Where(a => a.Name == applicationName).FirstOrDefault();
  10:            if (application == null)
  11:            {
  12:                // Create application
  13:                application = Application.CreateApplication(Guid.NewGuid(), applicationName);
  14:                context.AddToApplication(application);
  15:                context.SaveChanges();
  16:            }
  17:  
  18:            return application;
  19:        }

 

El ultimo método llamado Expression, se encargara de recibir ciertos elementos y valores para luego construir una expresión contenedora

   1: /// <summary>
   2:      /// Builds a contains expression.
   3:      /// </summary>
   4:      /// <typeparam name="TElement">The type of the element.</typeparam>
   5:      /// <typeparam name="TValue">The type of the value.</typeparam>
   6:      /// <param name="valueSelector">The value selector.</param>
   7:      /// <param name="values">The values.</param>
   8:      /// <returns></returns>
   9:      public static Expression<Func<TElement, bool>> BuildContainsExpression<TElement, TValue>(Expression<Func<TElement, TValue>> valueSelector, IEnumerable<TValue> values)
  10:      {
  11:          if (null == valueSelector)
  12:          {
  13:              throw new ArgumentNullException("valueSelector");
  14:          }
  15:  
  16:          if (null == values)
  17:          {
  18:              throw new ArgumentNullException("values");
  19:          }
  20:  
  21:          ParameterExpression p = valueSelector.Parameters.Single();
  22:  
  23:          if (!values.Any())
  24:          {
  25:              return e => false;
  26:          }
  27:  
  28:          IEnumerable<Expression> equals = values.Select(value => (Expression)Expression.Equal(valueSelector.Body, Expression.Constant(value, typeof(TValue))));
  29:          Expression body = equals.Aggregate(Expression.Or);
  30:          return Expression.Lambda<Func<TElement, bool>>(body, p);
  31:      }

Como vemos esta clase es bastante sencilla y nos liberara bastante trabajo cuando implementemos las demás clases personalizadas

Por ultimo, el código completo se vería así

   1: internal static class ProviderUtils
   2:    {
   3:        /// <summary>
   4:        /// A helper function to retrieve config values from the configuration file.
   5:        /// </summary>
   6:        /// <param name="config">Provider configuration.</param>
   7:        /// <param name="configKey">Key of the configuration that should be read.</param>
   8:        /// <param name="defaultValue">Default value being used if the config does not exist.</param>
   9:        /// <returns>Configuration value or default value if not exisiting.</returns>
  10:        public static object GetConfigValue(NameValueCollection config, string configKey, object defaultValue)
  11:        {
  12:            object configValue;
  13:  
  14:            try
  15:            {
  16:                configValue = config[configKey];
  17:                configValue = string.IsNullOrEmpty(configValue.ToString()) ? defaultValue : configValue;
  18:            }
  19:            catch
  20:            {
  21:                configValue = defaultValue;
  22:            }
  23:  
  24:            return configValue;
  25:        }
  26:  
  27:        /// <summary>
  28:        /// Ensure that application exists. If not -> create new application.
  29:        /// </summary>
  30:        /// <param name="applicationName">Application name.</param>
  31:        /// <param name="context">Entity Framework data context.</param>
  32:        /// <returns>The application object</returns>
  33:        public static Application EnsureApplication(string applicationName, Entities context)
  34:        {
  35:            Application application = context.Application.Where(a => a.Name == applicationName).FirstOrDefault();
  36:            if (application == null)
  37:            {
  38:                // Create application
  39:                application = Application.CreateApplication(Guid.NewGuid(), applicationName);
  40:                context.AddToApplication(application);
  41:                context.SaveChanges();
  42:            }
  43:  
  44:            return application;
  45:        }
  46:  
  47:        /// <summary>
  48:        /// Builds a contains expression.
  49:        /// </summary>
  50:        /// <typeparam name="TElement">The type of the element.</typeparam>
  51:        /// <typeparam name="TValue">The type of the value.</typeparam>
  52:        /// <param name="valueSelector">The value selector.</param>
  53:        /// <param name="values">The values.</param>
  54:        /// <returns></returns>
  55:        public static Expression<Func<TElement, bool>> BuildContainsExpression<TElement, TValue>(Expression<Func<TElement, TValue>> valueSelector, IEnumerable<TValue> values)
  56:        {
  57:            if (null == valueSelector)
  58:            {
  59:                throw new ArgumentNullException("valueSelector");
  60:            }
  61:  
  62:            if (null == values)
  63:            {
  64:                throw new ArgumentNullException("values");
  65:            }
  66:  
  67:            ParameterExpression p = valueSelector.Parameters.Single();
  68:  
  69:            if (!values.Any())
  70:            {
  71:                return e => false;
  72:            }
  73:  
  74:            IEnumerable<Expression> equals = values.Select(value => (Expression)Expression.Equal(valueSelector.Body, Expression.Constant(value, typeof(TValue))));
  75:            Expression body = equals.Aggregate(Expression.Or);
  76:            return Expression.Lambda<Func<TElement, bool>>(body, p);
  77:        }
  78:    }


Membership Provider Parte 2. Creación de un Modelo de Bases de datos

En mi post anterior se hablaba sobre la importancia del Membership Provider de asp.net y algunas de sus funciones. Sin embargo, para los que ha usado el proveedor se habrán dado cuenta de que contiene muchas tablas, procedimientos, vistas y demás que en muchas ocasiones no utilizamos o se nos dificulta modificar por su complejidad.

Así que, tomando en cuenta la facilidad con que contamos para personalizar el proveedor, empezaremos creando un modelo bastante sencillo que contendrá  4 tablas: Users, Roles, Profiles y Application. Con estas 4 tablas tendremos todo lo necesario para poder manejar los modelos de autenticación y seguridad en una aplicación de asp.net

En nuestro caso, este proveedor será de uso común, así que lo crearemos en Entity Framework para posteriormente poder manejar la implementación del código mediante LINQ.

Primer paso, creando el esquema de Entity

* Abrir visual studio

* Seleccionar archivo-nuevo

* Seleccionar proyecto

* En el menú izquierdo, en plantillas instaladas seleccionamos Visual C#-Windows-Biblioteca de Clases

*  Le ponemos un nombre y la agregamos al proyecto. Por defecto se crea una clase llamada Class1 la cual podemos eliminar

* Ahora que tenemos el proyecto, damos click derecho sobre el y nos dirigimos a la opción de agregar-nuevo elemento. Seleccionamos Ado.net Entity Data Model y lo agregamos al proyecto

Segundo paso, crear las tablas

El segundo paso es agregar las tablas al modelo de entity. Para esto se tiene dos posibilidades:

1. Agregar las tablas arrastrándolas desde el explorador de servidores

2. Crearlas paso a paso, que será nuestra forma. Así practicamos un poco.

Para crear una tabla, nos colocamos sobre el diseñador, damos clic derecho, buscamos la opción agregar y seleccionamos entidad. Tal como se muestra en la figura 1

Realizado este paso, le demos poner un nombre a la entidad (puede ser el mismo de la tabla) y damos aceptar. Con esto, la nueva entidad se agregara al modelo, a partir de ahi empezaremos a crear los campos.

 

Para añadir un campo a la entidad, damos clic sobre ella y buscamos la opción agregar, Propiedad escalar

Cada propiedad representa un campo de una tabla, por tanto deben llamarse igual a como está en la base de datos. Luego deben seleccionar la propiedad, ir a sus propiedades y modificar el Tipo de Datos, para que corresponda al que tiene en la base de datos.

La descripción de las tablas es la siguiente

Una vez creadas todas las entidades, asi se veria el diagrama

Por ahora, este post finaliza acá. Mas adelante veremos como empezar a programar las clases del Membership en Linq y por supuesto al final de los posts dejare el código.



Membership Provider Parte 1

 

Asp.net ha sido una de las tecnologías creadas por Microsoft de mas rápido crecimiento por la facilidad para los desarrolladores de crear sitios web.

Una de las partes de mayor importancia que tiene asp.net es el contar con el Membership Provider o proveedor de Membrecía, que permite la creación, manejo y mantenimiento de un sistema completo de control y autenticación de usuarios.

Para dar inicio a la serie de post que escribiré sobre que es Membeship y cuáles son las funcionalidades principales daremos unas definiciones.

Tal como se menciono anteriormente con el membership provider podemos crear un sistema de control de usuarios completos, entre las funcionalidades principales podemos encontrar:

* Creación de usuarios

* Almacenamiento de información en base de datos

* Autenticación, bloqueos y seguimiento

Otras de las ventajas que cabe resaltar, es que, algunos de los controles de asp.net ya traen "naturalmente" en sus funciones la implementación del membership provider, tal como el control "Login" o los controles de estado de usuario, lo cual nos permite que con solo arrastrarlos al diseñador estén funcionando.

Membership provider es poderoso, pero su funcionalidad y seguridad se ven aumentadas cuando se integra con otros proveedores de asp.net como lo son RoleProvider y Profile Provider (estos los discutiremos en otros post). En la siguiente figura, podemos ver como se distribuyen algunoS provider creados por Microsoft

Antes de iniciar con la implementación de membership debes conocer cosas básicas como el espacio de nombres al que pertenece, el cual es: System.Web.Security que se encuentra dentro del ensamblado System.Web. Algo que debe tomarse en cuenta, es que, para poder utilizar cualquiera de los miembros de la clase, debemos hacer la referencia respectiva.

Por defecto, el membership provider está diseñado para trabajar directamente con SQL Server, de ahí que su nombre completo seria SQL Membership Provider. Sin embargo, debido a su gran flexibilidad podemos extenderlo a cualquier base de datos o bien modificarlo para adapatarlo a nuestras necesidades.

En los siguientes posts, discutiremos como crear un proveedor personalizado utilizando Entity Framework, separando las capas de acceso y datos y cuáles son las principales funciones que podemos aplicar.

En palabras básicas y sin entrar muy hondo en el tema, hemos descrito el objetivo del Membership Provider, para todos los que desean ampliar pueden hacerlo en:

http://msdn.microsoft.com/es-es/library/system.web.security.membership%28v=vs.100%29.aspx



Menú Dinámico en MySql

Luego de varios días sin hacer post, hoy nuevamente retomo para mostrar un pequeño ejemplo de un tema que últimamente ha sido muy tocado en los foros de asp.net de MSDN. Cómo hacer un menú dinámico con MySql?

Antes de empezar a mostrar como crear el menú voy a explicar algunos problemas que encontré durante el desarrollo del ejemplo:

1. Para el ejemplo, utilizaremos la versión beta 6.5 del conector de .net para mysql, sin embargo, deben tomar en cuenta que este conector esta en desarrollo y aún carece de muchas funcionalidades que permitan implementar un mejor manejo de un menú dinámico.

2. En MySql lamentablemente no existe el Cache Dependency para poder monitorear los cambios en la base de datos y poder ver las actualizaciones del menú sin problemas. Por ello, con esta versión del .Net Connector, cada vez que agreguemos un nuevo ítem de menú a nuestra base de datos, tendremos que detener el servidor de desarrollo de visual studio y volver a iniciar para ver los cambios.

Sin embargo, aunque no sea posible por ahora con el conector monitorear el cambio (se puede hacer a código puro pero no vale la pena pues es mucho), este menú es una buena opción para poder organizar los accesos por roles.

Ahora sí, iniciemos:

Lo primero para nuestro menú, será descargar e instalar la última versión del conector .net para mysql (6.5 beta)

Conector Mysql

Entrando en el código, lo primero que realizaremos será crear una clase que herede se StaticSiteMapProvider para de esta manera poder contar con todas las opciones y métodos

   1: [MySqlClientPermission(SecurityAction.Demand,Unrestricted = true)]
   2: public class MySqlSiteMapProvider : StaticSiteMapProvider
   3: {
   4:  
   5: }

Ahora, definimos una serie de variables Privadas (solo para la clase) que nos permitirán mostrar algunos mensajes de error predeterminado y almacenar los valores recuperados del web.config

   1: private const string _errmsg1 = "Missing node ID";
   2:    private const string _errmsg2 = "Duplicate node ID";
   3:    private const string _errmsg3 = "Missing parent ID";
   4:    private const string _errmsg4 = "Invalid parent ID";
   5:    private const string _errmsg5 = "Empty or missing connectionStringName";
   6:    private const string _errmsg6 = "Missing connection string";
   7:    private const string _errmsg7 = "Empty connection string";
   8:  
   9:    private string _connect;              // Cadena de conexión a  base de datos
  10:    private int _indexId, _indexTitle, _indexUrl, _indexDesc, _indexRoles, _indexParent;
  11:    private Dictionary<int, SiteMapNode> _nodes = new Dictionary<int, SiteMapNode>(16);
  12:    private readonly object _lock = new object();
  13:    private SiteMapNode _root;

En las definiciones anteriores, los mas importante sería el Dictionary llamado _nodes, pues será el que almacenara los datos recuperados para el menú.

En el siguiente método, haremos un Override del método Initialize original y será en este en donde leamos el web.config y recuperemos todos los datos de la cadena de conexión y los proveedores

   1: public override void Initialize(string name, NameValueCollection config)
   2:     {
   3:         // Verificar que config no es null
   4:         if (config == null)
   5:             throw new ArgumentNullException("config");
   6:  
   7:         // Asignar al proveedor un nombre por defecto sino posee uno
   8:         if (String.IsNullOrEmpty(name))
   9:             name = "MySqlSiteMapProvider";
  10:  
  11:         // Add a default "description" attribute to config if the
  12:         // attribute doesn’t exist or is empty
  13:         if (string.IsNullOrEmpty(config["description"]))
  14:         {
  15:             config.Remove("description");
  16:             config.Add("description", "SQL site map provider");
  17:         }
  18:  
  19:         // Call the base class's Initialize method
  20:         base.Initialize(name, config);
  21:  
  22:         // Initialize _connect
  23:         string connect = config["connectionStringName"];
  24:  
  25:         if (String.IsNullOrEmpty(connect))
  26:             throw new ProviderException(_errmsg5);
  27:  
  28:         config.Remove("connectionStringName");
  29:  
  30:         if (WebConfigurationManager.ConnectionStrings[connect] == null)
  31:             throw new ProviderException(_errmsg6);
  32:  
  33:         _connect = WebConfigurationManager.ConnectionStrings[connect].ConnectionString;
  34:  
  35:         if (String.IsNullOrEmpty(_connect))
  36:             throw new ProviderException(_errmsg7);
  37:  
  38:       
  39:         if (config["securityTrimmingEnabled"] != null)
  40:             config.Remove("securityTrimmingEnabled");
  41:  
  42:         // Throw an exception if unrecognized attributes remain
  43:         if (config.Count > 0)
  44:         {
  45:             string attr = config.GetKey(0);
  46:             if (!String.IsNullOrEmpty(attr))
  47:                 throw new ProviderException("Unrecognized attribute: " + attr);
  48:         }
  49:  
  50:      
  51:     }

El siguiente método al que vamos a realizar el Override sera el SiteMapNode, este es uno de los mas importantes pues será en el cual nos vamos a conectar a mysql, leer los datos y enviarlos a otro método para armar el menú

   1: public override SiteMapNode BuildSiteMap()
   2:    {
   3:        lock (_lock)
   4:        {
   5:            // Return immediately if this method has been called before
   6:            if (_root != null)
   7:                return _root;
   8:  
   9:            // Query the database for site map nodes
  10:            MySqlConnection connection = new MySqlConnection(_connect);
  11:  
  12:            try
  13:            {
  14:                MySqlCommand command = new MySqlCommand("proc_GetSiteMap", connection);
  15:                command.CommandType = CommandType.StoredProcedure;
  16:                connection.Open();
  17:  
  18:                MySqlDataReader reader = command.ExecuteReader();
  19:                _indexId = reader.GetOrdinal("ID");
  20:                _indexUrl = reader.GetOrdinal("Url");
  21:                _indexTitle = reader.GetOrdinal("Title");
  22:                _indexDesc = reader.GetOrdinal("Description");
  23:                _indexRoles = reader.GetOrdinal("Roles");
  24:                _indexParent = reader.GetOrdinal("Parent");
  25:  
  26:                if (reader.Read())
  27:                {
  28:                    // Create the root SiteMapNode and add it to the site map
  29:                    _root = CreateSiteMapNodeFromDataReader(reader);
  30:                    AddNode(_root, null);
  31:  
  32:                    // Build a tree of SiteMapNodes underneath the root node
  33:                    while (reader.Read())
  34:                    {
  35:                        // Create another site map node and add it to the site map
  36:                        SiteMapNode node = CreateSiteMapNodeFromDataReader(reader);
  37:                        AddNode(node, GetParentNodeFromDataReader(reader));
  38:                    }
  39:  
  40:                  
  41:                }
  42:            }
  43:            finally
  44:            {
  45:                connection.Close();
  46:            }
  47:  
  48:            // Return the root SiteMapNode
  49:            return _root;
  50:        }
  51:    }

Los siguientes dos métodos no los voy a explicar en detalle, pero serán los encargados de tomar y extraer los roles y datos recuperados de la base de datos con el procedimiento anterior y crear cada SiteNode para el menú, almacenándolo en el Dictionay _nodes

   1: protected override SiteMapNode GetRootNodeCore()
   2:    {
   3:        lock (_lock)
   4:        {
   5:            BuildSiteMap();
   6:            return _root;
   7:        }
   8:    }
   9:  
  10:    // Helper methods
  11:    private SiteMapNode CreateSiteMapNodeFromDataReader(DbDataReader reader)
  12:    {
  13:        // Make sure the node ID is present
  14:        if (reader.IsDBNull(_indexId))
  15:            throw new ProviderException(_errmsg1);
  16:  
  17:        // Get the node ID from the DataReader
  18:        int id = reader.GetInt32(_indexId);
  19:  
  20:        // Make sure the node ID is unique
  21:        if (_nodes.ContainsKey(id))
  22:            throw new ProviderException(_errmsg2);
  23:  
  24:        // Get title, URL, description, and roles from the DataReader
  25:        string title = reader.IsDBNull(_indexTitle) ? null : reader.GetString(_indexTitle).Trim();
  26:        string url = reader.IsDBNull(_indexUrl) ? null : reader.GetString(_indexUrl).Trim();
  27:        string description = reader.IsDBNull(_indexDesc) ? null : reader.GetString(_indexDesc).Trim();
  28:        string roles = reader.IsDBNull(_indexRoles) ? null : reader.GetString(_indexRoles).Trim();
  29:  
  30:        // If roles were specified, turn the list into a string array
  31:        string[] rolelist = null;
  32:        if (!String.IsNullOrEmpty(roles))
  33:            rolelist = roles.Split(new char[] { ',', ';' }, 512);
  34:  
  35:  
  36:        // Create a SiteMapNode
  37:        SiteMapNode node = new SiteMapNode(this, id.ToString(), url, title, description, rolelist, null, null, null);
  38:  
  39:        // Record the node in the _nodes dictionary
  40:        _nodes.Add(id, node);
  41:  
  42:        // Return the node       
  43:        return node;
  44:    }

El último método, se encargara de hacer accesibles o no cada uno de los nodos del menú, eso si, para que funcione la opción SecurityTrimming tiene que ser true en la configuración del SiteMapProvider del web.config

   1: private SiteMapNode GetParentNodeFromDataReader(DbDataReader reader)
   2:     {
   3:         // Make sure the parent ID is present
   4:         if (reader.IsDBNull(_indexParent))
   5:             throw new ProviderException(_errmsg3);
   6:  
   7:         // Get the parent ID from the DataReader
   8:         int pid = reader.GetInt32(_indexParent);
   9:  
  10:         // Make sure the parent ID is valid
  11:         if (!_nodes.ContainsKey(pid))
  12:             throw new ProviderException(_errmsg4);
  13:  
  14:         // Return the parent SiteMapNode
  15:         return _nodes[pid];
  16:     }

Con lo anterior, ya tenemos la clase que se encargará del menu. Ahora veamos como se configura el web.config

Entre las etiquetas <System.Web></System.Web> se debe agregar la configuración del sitemap

   1: <siteMap defaultProvider="AspNetSqlSiteMapProvider" enabled="true">
   2:      <providers>
   3:        <add name="AspNetSqlSiteMapProvider" type="MySqlSiteMapProvider" securityTrimmingEnabled="true" connectionStringName="ApplicationServices"/>
   4:      </providers>
   5:    </siteMap>

Con esto, nuestro web.config ya estará preparado. Por supuesto no olviden cambiar el nombre de la cadena de conexión por la que tienen y el type que será el nombre de la clase

Lo ultimo será el html en la pagina

   1: <asp:SiteMapDataSource ID="SiteMapDataSource2" runat="server" SiteMapProvider="AspNetSqlSiteMapProvider"  />
   2:                    <asp:Menu ID="Menu2" runat="server" DataSourceID="SiteMapDataSource2" Orientation="Horizontal"
   3:                        DynamicHorizontalOffset="1" Font-Names="Verdana" Font-Size="0.8em" ForeColor="#990000"
   4:                        Height="10px" StaticSubMenuIndent="0px" StaticDisplayLevels="2">
   5:                        <StaticMenuStyle CssClass="toolbar" />
   6:                        <StaticSelectedStyle BackColor="#FFCC66" />
   7:                        <StaticMenuItemStyle CssClass="toolbar" HorizontalPadding="0px" VerticalPadding="1px" />
   8:                        <DynamicHoverStyle CssClass="toolbar" BackColor="#990000" ForeColor="White" />
   9:                        <DynamicMenuStyle CssClass="toolbar" BackColor="#FFFBD6" />
  10:                        <DynamicSelectedStyle BackColor="#FFCC66" />
  11:                        <DynamicMenuItemStyle HorizontalPadding="0px" VerticalPadding="1px" />
  12:                        <StaticHoverStyle BackColor="#990000" ForeColor="White" />
  13:                    </asp:Menu>

 

Una vez agregado el html, nuestro menú estará listo. Y como siempre el código de ejemplo



JqGrid y Asp.net, Maestro detalle con SubGrid (Parte 2)

Desde que escribí mi primer post sobre como utilizar JqGrid con asp.net (http://geekswithblogs.net/jaullo/archive/2011/05/19/jqgrid-y-asp.net-un-primer-acercamiento.aspx) he recibido varias consultas sobre como realizar un maestro detalle utilizando este control de jquery.

Antes de comenzar debo mencionar que para mostrar un maestro detalle con JqGrid tenemos dos opciones:

1. Realizarlo con un subgrid

2. Utilizar dos JqGrid separados

Así que, teniendo esto en cuenta, el objetivo de este post será realizar el maestro detalle utilizando el Subgrid, que a mi parecer es mucho más agradable y por supuesto mas ordenado. Posteriormente en otro post veremos como hacerlo con 2 JqGrid separados.

Para este ejemplo, no voy a profundizar mucho en como esta formado JqGrid ni como llenar los datos, pues eso ya lo vimos en el anterior post y aplica exactamente de la misma forma para el grid padre como para el grid hijo, basicamente sería cambiar el nombre de la tabla y crea un nuevo stored procedure.

En lo que si nos vamos a enfocar es en como realizar la definición del subgrid.

Si recordamos un poco como esta conformado el JqGrid, veremos que al final de la definición del mismo este nos permite configurar las opciones adicionales que tendrá el control, como por ejemplo:

width: "850",
height: "100%

Es precisamente en esta serie de opciones donde tendremos que habilitar nuestro grid para que contenga un subgrid, esto lo hacemos agregando la opción:

subGrid: true

Luego de que hemos habilitado nuestro subgrid debemos crear la función subGridRowExpanded que será la encargada de mostrar nuestro subgrid cuando expandamos el grid padre, es precisamente dentro de esta función donde declararemos nuestro subgrid.

   1: subGridRowExpanded: function (subgrid_id, row_id) {
   2:                     // we pass two parameters 
   3:                     // subgrid_id is a id of the div tag created whitin a table data 
   4:                     // the id of this elemenet is a combination of the "sg_" + id of the row 
   5:                     // the row_id is the id of the row 
   6:                     // If we wan to pass additinal parameters to the url we can use 
   7:                     // a method getRowData(row_id) - which returns associative array in type name-value 
   8:                     // here we can easy construct the flowing 
   9:                     var subgrid_table_id, pager_id;
  10:                     subgrid_table_id = subgrid_id + "_t";
  11:                     pager_id = "p_" + subgrid_table_id;
  12:                     $("#" + subgrid_id).html("<table id='" + subgrid_table_id + "' class='scroll'></table><div id='" + pager_id + "' class='scroll'></div>");
  13:  
  14:                     jQuery("#" + subgrid_table_id).jqGrid({
  15:                         //este es mi subgrid
  16:  
  17:                         datatype: function () {
  18:                             $.ajax(
  19:                 {
  20:                     url: "webservices/LoadChild.asmx/GetChilds", //PageMethod
  21:                     data: "{'pPageSize':'" + $("#" + subgrid_table_id).getGridParam("rowNum") +
  22:                     "','pCurrentPage':'" + $("#" + subgrid_table_id).getGridParam("page") +
  23:                     "','pSortColumn':'" + $("#" + subgrid_table_id).getGridParam("sortname") +
  24:                      "','id':'" + $("#" + subgrid_table_id).getGridParam("ajaxGridOptions") +
  25:                     "','pSortOrder':'" + $("#" + subgrid_table_id).getGridParam("sortorder") + "'}", //PageMethod Parametros de entrada
  26:  
  27:                     dataType: "json",
  28:                     type: "post",
  29:                     contentType: "application/json; charset=utf-8",
  30:                     complete: function (jsondata, stat) {
  31:                         if (stat == "success")
  32:                             jQuery("#" + subgrid_table_id)[0].addJSONData(JSON.parse(jsondata.responseText).d);
  33:                         else
  34:                             alert(JSON.parse(jsondata.responseText).Message);
  35:                     }
  36:                 });
  37:                         },
  38:                         jsonReader: //Set the jsonReader to the JQGridJSonResponse squema to bind the data.
  39:             {
  40:             root: "Items",
  41:             page: "CurrentPage",
  42:             total: "PageCount",
  43:             records: "RecordCount",
  44:             repeatitems: true,
  45:             cell: "Row",
  46:             id: "ID" //index of the column with the PK in it    
  47:         },
  48:  
  49:                         colModel:
  50:                 [
  51:                   { name: 'ChildId', index: 'ChildId', width: 40, align: 'left', editable: false, editrules: { edithidden: true }, hidden: true },
  52:                   { name: 'ChildName', index: 'ChildName', width: 80, align: 'left', editable: true, edittype: 'text' },
  53:                   { name: 'ChildLastName', index: 'ChildLastName', width: 200, align: 'left', editable: true, edittype: 'text', editoptions: { size: 20, maxlength: 30} },
  54:                   { name: 'ChildBirthDate', index: 'ChildBirthDate', width: 300, align: 'left', editable: true, edittype: 'select', editoptions: { size: 20, maxlength: 30} },
  55:                   { name: 'PersonId', index: 'PersonId', width: 200, align: 'left', editable: true, edittype: 'text', editoptions: { size: 20, maxlength: 30 }, formater: 'number'}],
  56:  
  57:                         pager: pager_id, //Pager.
  58:                         loadtext: 'Cargando datos...',
  59:                         recordtext: "{0} - {1} de {2} elementos",
  60:                         emptyrecords: 'No hay resultados',
  61:                         pgtext: 'Pág: {0} de {1}', //Paging input control text format.
  62:                         rowNum: "10", // PageSize.
  63:                         ajaxGridOptions: row_id,
  64:                         rowList: [10, 20, 30], //Variable PageSize DropDownList. 
  65:                         viewrecords: true, //Show the RecordCount in the pager.
  66:                         multiselect: false,
  67:                         sortname: "ChildName", //Default SortColumn
  68:                         sortorder: "asc", //Default SortOrder.
  69:                         width: "800",
  70:                         height: "100%",
  71:                         caption: "Invoice Detail"
  72:                     });
  73:                     jQuery("#" + subgrid_table_id).jqGrid('navGrid', "#" + pager_id, { edit: false, add: false, del: false })
  74:                 },

Si vemos el código anterior, la definición del subgrid es exactamente igual a como hemos realizado la definición del grid padre, solo que dentro de una función contenedora.

Finalmente, así se vería

Por último el código de ejemplo

Gracias a los comentarios de muchos, actualizo el post para agregar el Stored Procedure GetChilds, que lo disfruten

   1: USE [JQGrid]
   2: GO
   3: /****** Object:  StoredProcedure [dbo].[GetChilds]    Script Date: 07/27/2012 17:55:56 ******/
   4: SET ANSI_NULLS ON
   5: GO
   6: SET QUOTED_IDENTIFIER ON
   7: GO
   8:  
   9: -- GetPersons 3, 4, 'LastName', 'desc'
  10:  
  11: ALTER procedure [dbo].[GetChilds]
  12:  
  13: @PageSize int , 
  14: @CurrentPage int , 
  15: @SortColumn varchar(20), 
  16: @SortOrder varchar(4),
  17: @PersoId int
  18:  
  19: as
  20:  
  21: declare @RecordCount int
  22: declare @PageCount int
  23: declare @PageIndex int
  24:  
  25: Select @RecordCount = count(ChildId)
  26: from Childs
  27:  
  28: set @PageCount = Ceiling(cast (@RecordCount as float) / cast (@PageSize as float))
  29:  
  30: if (@CurrentPage > @PageCount) set @CurrentPage = @PageCount
  31:  
  32: set @PageIndex = @CurrentPage - 1
  33:  
  34: Select RecordCount = @RecordCount, PageCount = @PageCount, CurrentPage = @CurrentPage
  35:  
  36: declare @Query varchar(300)
  37:  
  38: set @Query = 
  39:     'Select ChildId, ChildName, ChildLastName, ChildBirthDate,PersonId, 
  40:      RowNumber = ROW_NUMBER() OVER (ORDER BY ' + @SortColumn + ' ' + @SortOrder + ')
  41:      from Childs' 
  42:  
  43: set @Query = 
  44:     'Select ChildId, ChildName, ChildLastName, ChildBirthDate,PersonId
  45:      from (' + @Query + ' )as result 
  46:      where PersonId='+ CAST(@PersoId as varchar(5)) +' and RowNumber BETWEEN ' + cast(@PageSize * @PageIndex + 1 as varchar(10)) + ' 
  47:                     AND ' + cast(@PageSize * (@PageIndex + 1) as varchar(10)) 
  48:                     
  49:  
  50:  
  51: Exec (@Query)


Actualización de Estándares Web - HTML5 para el Editor de Visual Studio 2010

Hace unos días recibi un email de Microsoft con noticias sobre Visual Studio, y algo que me encanto fue ver que por fin tenemos soporte para HTML y CSS 3 en el editor de VS. Así que, me di la tarea de leer el contenido de los blogs recomendados en el artículo y tome la decision de traducir el contenido del blog de Scott Hanselman’sa español (por supuesto con algunos cambios), para que toda la comunidad que no tiene aún un buen conocimiento de inglés pueda aprovechar el contenido.

Inicamos… Sonrisa

La gente ha estado preguntando "Cuando VS2010 soportará HTML5?"

Hoy, algunos amigos en el equipo de herramientas y plataformas Web, encabezada por Mads Kristensenestán encantados de anunciar la actualización de las normas Web de Visual Studio. Mejor soporte para HTML5, CSS3 y nuevas funciones de JavaScript se agregan a todas las versiones de Visual Studio.

Tome en cuenta que esta no es una versión de Microsoft oficial para el producto.

Descargue la primera actualización de Estándares Web para Visual Studio

HTML5 se mueve rápido y esta actualización tratará de mantenerse con ella. Se agrega soporte para Visual Studio y el editor de HTML5, CSS3 y nuevas funciones de JavaScript. El objetivo es tal vez una actualización cada trimestre o así como nuevas características o elementos surjan. Queremos que los desarrolladores de Asp.Net siempre puedan utilizar los estándares más recientes, así como también puedan elegir entre los estándares vigentes. Recuerde que puede utilizar HTML5 hoy junto con bibliotecas de JavaScript como Modernizar que permiten crear páginas que funcionan en casi todos los navegadores, incluyendo a “todos los viejos”.

La actualización de las normas de Web de Visual Studio proporciona intellisense y validación para:

Características de HTML 5

Video y etiquetas relacionadas
Audio y etiquetas relacionadas
Nuevos tipos de entrada como el correo electrónico, URL, fecha, etc
Soporte para Arrastrar y Soltar (Drag and Drop)
Accesibilidad estándar WAI-ARIA
Micro datos
Schema.org y más bondades para SEO amigable

APIs de Navegadores

  • Geo-Localización – La ubicación de los sitios a través de la red es una clara tendencia, y ahora tiene acceso completo a la validación e Intellisense a través de Visual Studio.  Para visualizar un bonito ejemplo, vea el código de IE9 test drive demo.
  • Almacenamiento Local – IE ha estado soportando el almacenamiento local desde IE8, así que ahora Visual Studio le proveerá con una fidelidad completa de intellisense para crear sitios que puedan guardar el estado en el navegador.  Para un ejemplo de esto, de una mirada al código del Sitio Demo de HTML 5

CSS3

Además, si usted está tratando de hacer webs que trabajen en una variedad de plataformas y navegadores, le encantará el hecho de que la actualización de los estándares Web no sólo es compatible con prefijos específicos de IE como el ms, sino también con otros prefijos -webkit y moz-.

HTML5 en Visual Studio

Cuando se inicia Visual Studio 2010, puede seleccionar qué versión de HTML desea en archivo por archivo, o establecer los valores predeterminados.

También se puede establecer como predeterminado en Herramientas | Opciones | Editor de texto | HTML | Validación. Tenga en cuenta la opción para mostrar errores.

Todas las nuevas etiquetas semánticas de HTML 5 están allí, como section, svg, video, etc. Esto es sólo el primer lanzamiento, y aunque está muy completo, estoy seguro de que tiene algunos errores. Sin embargo, será actualizada regularmente, así que asegúrese de informar todo lo que encuentre y esperamos que se fije en la próxima versión

Incluso hay apoyo para los importantes (pero sub utilizados y poco respetados) atributos de WAI-ARIA (Web Accessibility Initiative - Accesible Rich Internet Applications)

También los atributos aria-* aparecen no sólo como atributos de intellisense habilitados, sino también sus valores enumerados:

Las etiquetas son divertidas, seguro, pero ¿qué hay de los microdatos? Bing, Google y Yahoo están apoyando Microdatos y, más concretamente, los vocabularios schema.org. Por lo tanto, esta actualización añade intellisense para los vocabularios más populares, incluyendo schema.org y datos vocabulary.org.

En serio, dime que esto no es impresionante para los SEO.

CSS3 en Visual Studio
Esto es sólo el material HTML5. También hay apoyo de CSS3. Sí, eso es mejor que un CSS2.

Incluye todas las maravillas del nuevo CSS3 por ejemplo columna completa y, por supuesto, border-radius, así como animaciones y transformaciones. También hay un montón de detalles sobre proveedores específicos como -webkit, moz-y ms.

Note el Tooltip que muestra información en el drowpdown cuando seleccionamos una propiedad de transformación –webkit abreviada.

JavaScript y HTML 5 en Visual Studio

Hay un gran número de adiciones para el IntelliSense de JavaScript en esta actualización. Cosas como la geolocalización a través de navigator.geolocation y almacenamiento DOM a través de window.localStorage.

He aquí un fragmento de algo menos trivial de HTML5 y JavaScript con algunas nuevas características de almacenamiento local y mostrando el intellisense.

Diga lo que quiera acerca de intellisense como un concepto o una muleta, pero me gusta pensar en él como un navegador de objetos justo a tiempo de que me ayuda a explorar nuevas tecnologías, así como descubrir rincones que no podría ver.

No sabemos todo lo que vendrá en la próxima versión de Visual Studio o la próxima versión de HTML 5, pero para aquellos de ustedes que están interesados??, esperamos que esta "actualización viva" de la web de soporte de las Normas de Visual Studio le ayudará!

PD Se trata de una actualización para ASP.NET y HTML 5 para personas que desean utilizar HTML5, las nuevas funciones de JavaScript y CSS 3. No he mencionado Silverlight, ya que no tiene nada que ver con Silverlight. Dije una vez "sólo porque su tecnología favorita no se menciona en un discurso no quiere decir que este muerta." Supongamos que la misma regla se aplica a una entrada de blog.

Enlaces relacionados
Visual Web Developer Team Blog - Web de actualizaciones de las normas para Visual Studio 2010 SP1

 

Espero lo disfruten!



Qué control te gustaria?

Cada vez, utilizó mas Jquery para enriquecer las aplicaciones que desarrollo, pero cada vez me doy cuenta de que siempre debo leer la documentación de los controles para poder recordar todas las funciones. Esto, sumado a la cantidad de código script que debo colocar en las páginas.

Es por eso que decidi empezar a trabajar en una pequeña seríe de controles de Jquery para asp.net basado en el framework DJ Jquery. Por supuesto, una serie de controles OpenSource para la comunidad

 

Actualmente los controles disponibles son:

* Accordion

* Animation

* Autocomplete

* DatePicker

* Dialog

* Draggable

* Droppable

* Effect

* FileUpload

* FlexGrid (en desarrollo)

* Floater Menu

* JMenu (en desarrollo)

* Jquery Plugin

* Password Meter

* ProgressBar

* Resizable

* Selectable

* Slick Menu

* Slider

* Sortable

* Tabs

* ButtonEx

* Toggle Button

* Simple Button

* Simple List View

 

Así que la idea es preguntarles:

¿Qué otro control les gustaría ver en la suite?

 

Saludos,



jqGrid y Asp.net, un primer acercamiento

 

Luego de varios días de intenso trabajo, hoy he tomado un pequeño espacio de tiempo para dedicar un post al trabajo con datos en forma de tablas en asp.net

Todos los que hemos trabajado desde hace bastante con asp.net, hemos aprendido a querer al GridView, un excelente control que nos permite mostrar los datos directamente desde una base de datos y trabajar con ellos, llamase trabajar a las operaciones de: editar, buscar, agregar y eliminar. Por supuesto que existen muchas otras opciones que no son el objetivo de este post.

A pesar de que e GridView es una buena herramienta para trabajar, tiene un inconveniente, es un control que funciona del lado del servidor, lo cual ocasiona un postback hacia el servidor, afectando muchas veces el rendimiento y obligándonos a controlar los parpadeos de la página con controles adicionales.

Así que, para evitarnos todo eso, hoy veremos como trabajar con jqGrid, un control basado en jquery para realizar las operaciones básicas (CRUD). La principal ventaja de trabajar con este control, es que, al ser creado en jquery, es del lado del cliente, lo cual hace mucho mas eficiente y por supuesto, cuenta con opciones adicionales que el gridview no posee.

Antes de continuar, debo recalcar que existe ya un control basado en jqGrid para asp.net, pero este tiene un costo superior  a $300 lo cual para muchos no es accesible, así que tendremos que trabajar con el control opensource y hacer algunas adaptaciones.

Para todos aquellos que les gusta ver un ejemplo antes de sumergirse en un proceso de estos, les dejo la pagina de demos

Demos jqgrid

Ahora sí, es momento de empezar con nuestro control.

Descarga del control

Lo primero que requerimos para iniciar nuestro trabajo es descargar el control de la página oficial, en ella pueden escoger cuales serán las funciones que desean descargar, generalmente recomiendo que las descarguen todas.

Descarga jqgrid

Preparando el sitio

Una vez descargado abrimos Visual Studio seleccionamos Archivo>Nuevo>Sitio Web (en blanco sería mejor)

Ahora creamos una nueva carpeta y la llamamos js y ponemos en ella todos los scripts del jqgrid que descargamos anteriormente.

Nuevamente creamos una nueva carpeta llamada css y de igual forma ponemos hay todos los archivos CSS y las imágenes. Si en la descarga no están incluidos los archivos css, no se preocupen, los encontrarán en el archivo para descargar al final del post.

Con esto, ya estaríamos listos para iniciar a codificar el control.

Creando el control

Para este primer acercamiento, veremos como implementar la funcionalidades básicas. En otros post, veremos como crear un maestro detalle y como utilizar un SubGrid.

Primer paso, agregar las referencias javascript a la página

   1: <script type="text/javascript" src="js/jquery-1.5.2.min.js"></script>
   2:     <script type="text/javascript" src="js/grid.locale-es.js"></script>
   3:     <script type="text/javascript" src="js/jquery.jqGrid.min.js"></script>
   4:     <script type="text/javascript" src="js/grid.base.js"></script>
   5:     <script type="text/javascript" src="js/grid.common.js"></script>
   6:     <script type="text/javascript" src="js/grid.formedit.js"></script>
   7:     <script type="text/javascript" src="js/jquery.fmatter.js"></script>
   8:     <script type="text/javascript" src="js/jsonXml.js"></script>
   9:     <script type="text/javascript" src="js/jquery.tablednd.js"></script>
  10:     <script type="text/javascript" src="js/ui.multiselect.js"></script>
  11:     <script type="text/javascript" src="js/grid.inlinedit.js"></script>
  12:     <script type="text/javascript" src="js/jQDNR.js"></script>
  13:     <script type="text/javascript" src="js/jqModal.js"></script>

Si vemos las referencias, únicamente hemos agregado algunas, que serán las que nos brinden las funcionalidades que requerimos para trabajar con las operaciones básicas.

Una vez agregadas, crearemos una nueva función javascript en la cual codificaremos el jqgrid.

Si vemos la documentaciónoriginal de jqGrid, veremos que jqgrid utiliza un atributo llamado colModel para crear las columnas del grid y que su definición es manual, es decir, se hace directamente en el control. Algo así:

   1: colNames:['ID Number','Notes'],
   2:     colModel:[
   3:       {name:'id',index:'id', width:90, sorttype:"int", editable: true},                    
   4:       {name:'note',index:'note', width:200, sortable:false,editable: true,edittype:"textarea", editoptions:{rows:"2",cols:"10"}}                      
   5:         ],

Esto, a mi parecer es algo no muy agradable, porque simplemente obligaría a tener que tocar código sensible del control cada vez que queramos mostrar información distinta. Así que, me di a la tarea de crear una nueva funcionalidad para el jqGrid que obtiene una serie de elementos desde un webservice vía Json y los mete en un array de jquery, de forma tal que podamos tener las columnas dinámicas. Este método, lo ejecutare antes de empezar la definición del jqGrid, es decir antes de la línea

   1: $("#grid").jqGrid(

Nuestro nuevo método luce así

   1: //aca es modificacion
   2:             $.ajax({
   3:                 dataType: "json",
   4:                 type: "post",
   5:                 url: "JqGridEjemplo.aspx/GetNames",
   6:                 data: "{}",
   7:                 contentType: "application/json;",
   8:                 async: false, //esto es requerido, de otra forma el jqgrid se cargaria antes que el grid
   9:                 success: function (data) {
  10:                     var cars = JSON.parse(data.d);
  11:                     $.each(cars, function (index, car) {
  12:                         colMode.push({ name: car.Name, index: car.index, width: car.width, align: 'left', editable: true, editrules: { edithidden: true }, hidden: false });
  13:                      })
  14:  
  15:                 } //or
  16:  
  17:             }),

El método anterior no tiene mucho especial que explicar:

1. Primero con el type=”post” hacemos referencia a la forma en que obtendremos los datos.

2.Con el data:”{}” le decimos al json que no vamos a enviarle parámetros

3. La url que direcciona al webservice

4. A la variable cars le asignamos el valor obtenido del Json

5. y Por ultimo con un each recorremos cada campo y lo metemos en el arreglo.

El código del Jqgrid

Al igual que el procedimiento anterior, para el jqgri tenemos que especificar los tipos de datos, la url y en este caso algunos parámetros que luego utilizaremos para traer los datos

   1: $("#grid").jqGrid(
   2:        {
   3:            datatype: function () {
   4:                $.ajax(
   5:                {
   6:                    url: "JqGridEjemplo.aspx/GetPersons", //PageMethod
   7:  
   8:                    data: "{'pPageSize':'" + $('#grid').getGridParam("rowNum") +
   9:                    "','pCurrentPage':'" + $('#grid').getGridParam("page") +
  10:                    "','pSortColumn':'" + $('#grid').getGridParam("sortname") +
  11:                    "','pSortOrder':'" + $('#grid').getGridParam("sortorder") + "'}", //PageMethod Parametros de entrada
  12:  
  13:                    dataType: "json",
  14:                    type: "post",
  15:                    contentType: "application/json; charset=utf-8",
  16:                    complete: function (jsondata, stat) {
  17:                        if (stat == "success")
  18:                            jQuery("#grid")[0].addJSONData(JSON.parse(jsondata.responseText).d);
  19:                        else
  20:                            alert(JSON.parse(jsondata.responseText).Message);
  21:                    }
  22:                });
  23:            },
  24:            jsonReader: //Set the jsonReader to the JQGridJSonResponse squema to bind the data.
  25:            {
  26:            root: "Items",
  27:            page: "CurrentPage",
  28:            total: "PageCount",
  29:            records: "RecordCount",
  30:            repeatitems: true,
  31:            cell: "Row",
  32:            id: "ID" //index of the column with the PK in it    
  33:        },
Luego, establecemos las columnas utilizando el colmodel y algunos parámetros de configuración de nuestro grid
   1: colModel: colMode,
   2:  
   3:         pager: "#pager", //Pager.
   4:         loadtext: 'Cargando datos...',
   5:         recordtext: "{0} - {1} de {2} elementos",
   6:         emptyrecords: 'No hay resultados',
   7:         pgtext: 'Pág: {0} de {1}', //Paging input control text format.
   8:         rowNum: "10", // PageSize.
   9:         rowList: [10, 20, 30], //Variable PageSize DropDownList. 
  10:         viewrecords: true, //Show the RecordCount in the pager.
  11:         multiselect: true,
  12:         sortname: "Name", //Default SortColumn
  13:         sortorder: "asc", //Default SortOrder.
  14:         width: "800",
  15:         height: "230",
  16:         caption: "Personas",
  17:         ondblClickRow: function (id) {
  18:             gdCustomers.restoreRow(lastSel);
  19:             gdCustomers.editRow(id, true);
  20:             lastSel = id;
  21:         }

Jqgrid tiene la posibilidad de mostrar en su barra inferior las opciones de insert,delete, add, update y refresh, así que las configuraremos de la siguiente manera

   1: }).navGrid("#pager", { edit: true, add: true, search: true, del: true },
   2:     { url: "jqGridEjemplo.aspx/EditData", closeAfterEdit: true },
   3:     { url: "jqGridEjemplo.aspx/EditData", closeAfterAdd: true },
   4:     { url: "jqGridEjemplo.aspx/DeleteData" });

Acá vemos que al pager, le estamos diciendo que si vamos a permitir todas las opciones y además, para cada una de ellas estamos asignando una dirección en especifico, que de igual forma, apuntan a un procedimiento en un webservice. Estas opciones son completamente configurables y por supuesto que bueno estar deshabilitadas o no presentes por ahora, tenemos que colocarlas manualmente, luego trabajaremos en hacerlo de forma dinámica.

Para concluir con la configuración de nuestro jqgrid, necesitamos configurar las opciones de agregar y borrar utilizando el método jquery.extend, de otra forma aunque configuremos el jqgrid estas opciones nos devolverían un error.

   1: jQuery.extend(jQuery.jgrid.edit, {
   2:                 ajaxEditOptions: { contentType: "application/json" },
   3:                 recreateForm: true,
   4:                 serializeEditData: function (postData) {
   5:                     return JSON.stringify(postData);
   6:                 }
   7:             });
   8:  
   9:             jQuery.extend(jQuery.jgrid.del, {
  10:                 ajaxDelOptions: { contentType: "application/json" },
  11:                 serializeDelData: function (postData) {
  12:  
  13:                     return JSON.stringify(postData);
  14:                 }
  15:             });
  16:  
  17:  
  18:  
  19:      });

Finalizando el código completo seria

   1: <script type="text/javascript">
   2:         var $table = $('#grid');
   3:         var lastSel;
   4:         var colMode = [];
   5:         jQuery(document).ready(function () {
   6:             //aca es modificacion
   7:             $.ajax({
   8:                 dataType: "json",
   9:                 type: "post",
  10:                 url: "JqGridEjemplo.aspx/GetNames",
  11:                 data: "{}",
  12:                 contentType: "application/json;",
  13:                 async: false, //esto es requerido, de otra forma el jqgrid se cargaria antes que el grid
  14:                 success: function (data) {
  15:                     var cars = JSON.parse(data.d);
  16:                     $.each(cars, function (index, car) {
  17:                         colMode.push({ name: car.Name, index: car.index, width: car.width, align: 'left', editable: true, editrules: { edithidden: true }, hidden: false });
  18:                      })
  19:  
  20:                 } //or
  21:  
  22:             }),
  23:             //acá
  24:             $("#grid").jqGrid(
  25:         {
  26:             datatype: function () {
  27:                 $.ajax(
  28:                 {
  29:                     url: "JqGridEjemplo.aspx/GetPersons", //PageMethod
  30:  
  31:                     data: "{'pPageSize':'" + $('#grid').getGridParam("rowNum") +
  32:                     "','pCurrentPage':'" + $('#grid').getGridParam("page") +
  33:                     "','pSortColumn':'" + $('#grid').getGridParam("sortname") +
  34:                     "','pSortOrder':'" + $('#grid').getGridParam("sortorder") + "'}", //PageMethod Parametros de entrada
  35:  
  36:                     dataType: "json",
  37:                     type: "post",
  38:                     contentType: "application/json; charset=utf-8",
  39:                     complete: function (jsondata, stat) {
  40:                         if (stat == "success")
  41:                             jQuery("#grid")[0].addJSONData(JSON.parse(jsondata.responseText).d);
  42:                         else
  43:                             alert(JSON.parse(jsondata.responseText).Message);
  44:                     }
  45:                 });
  46:             },
  47:             jsonReader: //Set the jsonReader to the JQGridJSonResponse squema to bind the data.
  48:             {
  49:             root: "Items",
  50:             page: "CurrentPage",
  51:             total: "PageCount",
  52:             records: "RecordCount",
  53:             repeatitems: true,
  54:             cell: "Row",
  55:             id: "ID" //index of the column with the PK in it    
  56:         },
  57:  
  58:         colModel: colMode,
  59:  
  60:         pager: "#pager", //Pager.
  61:         loadtext: 'Cargando datos...',
  62:         recordtext: "{0} - {1} de {2} elementos",
  63:         emptyrecords: 'No hay resultados',
  64:         pgtext: 'Pág: {0} de {1}', //Paging input control text format.
  65:         rowNum: "10", // PageSize.
  66:         rowList: [10, 20, 30], //Variable PageSize DropDownList. 
  67:         viewrecords: true, //Show the RecordCount in the pager.
  68:         multiselect: true,
  69:         sortname: "Name", //Default SortColumn
  70:         sortorder: "asc", //Default SortOrder.
  71:         width: "800",
  72:         height: "230",
  73:         caption: "Personas",
  74:         ondblClickRow: function (id) {
  75:             gdCustomers.restoreRow(lastSel);
  76:             gdCustomers.editRow(id, true);
  77:             lastSel = id;
  78:         }
  79:     }).navGrid("#pager", { edit: true, add: true, search: true, del: true },
  80:     { url: "jqGridEjemplo.aspx/EditData", closeAfterEdit: true },
  81:     { url: "jqGridEjemplo.aspx/EditData", closeAfterAdd: true },
  82:     { url: "jqGridEjemplo.aspx/DeleteData" });
  83:  
  84:  
  85:             jQuery.extend(jQuery.jgrid.edit, {
  86:                 ajaxEditOptions: { contentType: "application/json" },
  87:                 recreateForm: true,
  88:                 serializeEditData: function (postData) {
  89:                     return JSON.stringify(postData);
  90:                 }
  91:             });
  92:  
  93:             jQuery.extend(jQuery.jgrid.del, {
  94:                 ajaxDelOptions: { contentType: "application/json" },
  95:                 serializeDelData: function (postData) {
  96:  
  97:                     return JSON.stringify(postData);
  98:                 }
  99:             });
 100:  
 101:  
 102:  
 103:      });
 104:        
 105:     </script>

Ahora que la configuración el grid esta completa, es hora de trabajar el html donde se mostrarán los datos, siendo tan sencillo como agregar una tabla y un div

   1: <table id="grid">
   2:     </table>
   3:     <div id="pager">

 

Los datos a mostrar, los traeremos de SQL utilizando un procedimiento almacenado que luce asi

   1: USE [JqGrid]
   2: GO
   3: /****** Object:  StoredProcedure [dbo].[GetPersons]    Script Date: 05/19/2011 13:29:50 ******/
   4: SET ANSI_NULLS ON
   5: GO
   6: SET QUOTED_IDENTIFIER ON
   7: GO
   8:  
   9: -- GetPersons 3, 4, 'LastName', 'desc'
  10:  
  11: ALTER procedure [dbo].[GetPersons]
  12:  
  13: @PageSize int , 
  14: @CurrentPage int , 
  15: @SortColumn varchar(20), 
  16: @SortOrder varchar(4)
  17:  
  18: as
  19:  
  20: declare @RecordCount int
  21: declare @PageCount int
  22: declare @PageIndex int
  23:  
  24: Select @RecordCount = count(ID)
  25: from Person
  26:  
  27: set @PageCount = Ceiling(cast (@RecordCount as float) / cast (@PageSize as float))
  28:  
  29: if (@CurrentPage > @PageCount) set @CurrentPage = @PageCount
  30:  
  31: set @PageIndex = @CurrentPage - 1
  32:  
  33: Select RecordCount = @RecordCount, PageCount = @PageCount, CurrentPage = @CurrentPage
  34:  
  35: declare @Query varchar(300)
  36:  
  37: set @Query = 
  38:     'Select ID, Name, LastName, BirthDate, Weight, 
  39:      RowNumber = ROW_NUMBER() OVER (ORDER BY ' + @SortColumn + ' ' + @SortOrder + ')
  40:      from Person' 
  41:  
  42: set @Query = 
  43:     'Select ID, Name, LastName, BirthDate, Weight
  44:      from (' + @Query + ' )as result 
  45:      where RowNumber BETWEEN ' + cast(@PageSize * @PageIndex + 1 as varchar(10)) + ' 
  46:                     AND ' + cast(@PageSize * (@PageIndex + 1) as varchar(10))
  47:  
  48:  
  49: Exec (@Query)

Si observamos bien el Stored Procedure veremos que recibe algunos parámetros que estamos enviando por Json  cuando la url que carga los datos es activada.

El primer metodo que utilizaremos para cargar los datos con base en el stored procedure será

   1: internal static JQGridJsonResponse GetPersonasJSon(int pPageSize, int pPageNumber, string pSortColumn, string pSortOrder)
   2:    {
   3:        SqlConnection sqlCon = new SqlConnection(ConfigurationManager.ConnectionStrings["DataConnection"].ConnectionString);
   4:        SqlCommand command = new SqlCommand("GetPersons", sqlCon);
   5:        command.CommandType = CommandType.StoredProcedure;
   6:        command.Parameters.Add("PageSize", SqlDbType.Int).Value = pPageSize;
   7:        command.Parameters.Add("CurrentPage", SqlDbType.Int).Value = pPageNumber;
   8:        command.Parameters.Add("SortColumn", SqlDbType.VarChar, 20).Value = pSortColumn;
   9:        command.Parameters.Add("SortOrder", SqlDbType.VarChar, 4).Value = pSortOrder;
  10:  
  11:        DataSet dataSet = new DataSet();
  12:        SqlDataAdapter dataAdapter = new SqlDataAdapter(command);
  13:        dataAdapter.Fill(dataSet);
  14:  
  15:        var persons = new List<Person>();
  16:        foreach (DataRow row in dataSet.Tables[1].Rows)
  17:        {
  18:            Person person = new Person
  19:            {
  20:                ID = Convert.ToInt32(row["ID"]),
  21:                Name = row["Name"].ToString(),
  22:                LastName = row["LastName"].ToString(),
  23:                BirthDate = Convert.ToDateTime(row["BirthDate"]),
  24:                Weight = Convert.ToSingle(row["Weight"])
  25:            };
  26:            persons.Add(person);
  27:        }
  28:  
  29:        return new JQGridJsonResponse(Convert.ToInt32(dataSet.Tables[0].Rows[0]["PageCount"]), Convert.ToInt32(dataSet.Tables[0].Rows[0]["CurrentPage"]),
  30:            Convert.ToInt32(dataSet.Tables[0].Rows[0]["RecordCount"]), persons);
  31:    }

Sin embargo, este método solo llena los datos, pero aun no los devuelve, eso lo haremos a través de un webservice de la siguiente forma

   1: [WebMethod]
   2:   [ScriptMethod(ResponseFormat = ResponseFormat.Json)]
   3:   public static JQGridJsonResponse GetPersons(int pPageSize, int pCurrentPage, string pSortColumn, string pSortOrder)
   4:   {
   5:       return GetPersonasJSon(pPageSize, pCurrentPage, pSortColumn, pSortOrder);
   6:   }

Es importante que las líneas ResponseFormat=ResponseFormat.Json estén incluidas para que la información que se envié sea codificada de la forma correcta.

También debemos notar que ambos metodos hacen referencia a JQGridJsonResponse que es una clase definida para almacenar los datos y llenar cada item

   1: /// <summary>
   2: /// Respuesta JSON para JQGrid.
   3: /// </summary>
   4: public class JQGridJsonResponse
   5: {
   6:   #region Passive attributes.
   7:  
   8:   private int _pageCount;
   9:   private int _currentPage;
  10:   private int _recordCount;
  11:   private List<JQGridItem> _items;
  12:  
  13:   #endregion
  14:  
  15:   #region Properties
  16:  
  17:   /// <summary>
  18:   /// Cantidad de páginas del JQGrid.
  19:   /// </summary>
  20:   public int PageCount
  21:   {
  22:     get { return _pageCount; }
  23:     set { _pageCount = value; }
  24:   }
  25:   /// <summary>
  26:   /// Página actual del JQGrid.
  27:   /// </summary>
  28:   public int CurrentPage
  29:   {
  30:     get { return _currentPage; }
  31:     set { _currentPage = value; }
  32:   }
  33:   /// <summary>
  34:   /// Cantidad total de elementos de la lista.
  35:   /// </summary>
  36:   public int RecordCount
  37:   {
  38:     get { return _recordCount; }
  39:     set { _recordCount = value; }
  40:   }
  41:   /// <summary>
  42:   /// Lista de elementos del JQGrid.
  43:   /// </summary>
  44:   public List<JQGridItem> Items
  45:   {
  46:     get { return _items; }
  47:     set { _items = value; }
  48:   }
  49:  
  50:   #endregion
  51:  
  52:   #region Active attributes
  53:  
  54:   /// <summary>
  55:   /// Constructor.
  56:   /// </summary>
  57:   /// <param name="pItems">Lista de elementos a mostrar en el JQGrid</param>
  58:   public JQGridJsonResponse(int pPageCount, int pCurrentPage, int pRecordCount, List<Person> pPersons)
  59:   {
  60:       _pageCount = pPageCount;
  61:       _currentPage = pCurrentPage;
  62:       _recordCount = pRecordCount;
  63:       _items = new List<JQGridItem>();
  64:       foreach (Person person in pPersons)
  65:           _items.Add(new JQGridItem(person.ID, new List <string> { person.ID.ToString(),person.Name, person.LastName, person.BirthDate.ToShortDateString(), person.Weight.ToString() }));
  66:   }
  67:  
  68:   
  69:  
  70:   #endregion  
  71: }
Pero, nuevamente debemos tener cuidado, ya que esta clase llama a otra clase que contiene los atributos del grid llamada JQGridItem
   1:  
   2:   /// <summary>
   3:   /// Item del JQGrid. Elemento de la propiedad Items de la clase JsonJQGridResponse.
   4:   /// </summary>
   5:   public class JQGridItem
   6:   {
   7:     #region Passive attributes
   8:  
   9:     private long _id;
  10:     private List<string> _row;
  11:  
  12:     #endregion
  13:  
  14:     #region Properties
  15:  
  16:     /// <summary>
  17:     /// RowId de la fila.
  18:     /// </summary>
  19:     public long ID
  20:     {
  21:       get { return _id; }
  22:       set { _id = value; }
  23:     }
  24:     /// <summary>
  25:     /// Fila del JQGrid.
  26:     /// </summary>
  27:     public List<string> Row
  28:     {
  29:       get { return _row; }
  30:       set { _row = value; }
  31:     }
  32:  
  33:     #endregion
  34:  
  35:     #region Active Attributes
  36:  
  37:     /// <summary>
  38:     /// Contructor.
  39:     /// </summary>
  40:     public JQGridItem(long pId, List<string> pRow)
  41:     {
  42:       _id = pId;
  43:       _row = pRow;
  44:     }
  45:  
  46:     #endregion
  47:   }

Con esto, nuestro jqGrid sería totalmente funcional para mostrar los datos, paginar y ordenar pero aún no lo es para guardar, editar o borrar.

Estos método al igual que los anteriores, serán definidos en un webservice y serán más sencillos de utilizar pues básicamente reciben los datos mediante Json y lo demás se traduce en código que ya conocemos.

   1: [WebMethod]
   2:    [ScriptMethod(ResponseFormat = ResponseFormat.Json)]
   3:    public static void DeleteData(string id,string oper)
   4:    {
   5:               
   6:       if (String.Compare(oper, "del", StringComparison.Ordinal) == 0)
   7:        {
   8:            using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["DataConnection"].ConnectionString))
   9:            {
  10:                using (SqlCommand command = new SqlCommand("delete from person where id=@id", conn))
  11:                {
  12:                    conn.Open();
  13:                    command.Parameters.AddWithValue("@id", id);
  14:                    command.ExecuteNonQuery();
  15:                }
  16:            }
  17:  
  18:          
  19:            
  20:        }
  21:       
  22:    }
   1: [WebMethod]
   2:     [ScriptMethod(ResponseFormat = ResponseFormat.Json)]
   3:     public static void EditData(string BirthDate, string LastName, string Name, string Weight, string id, string oper)
   4:     {
   5:         if (String.Compare(id, "_empty", StringComparison.Ordinal) == 0 ||
   6:             String.Compare(oper, "add", StringComparison.Ordinal) == 0)
   7:         {
   8:  
   9:             using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["DataConnection"].ConnectionString))
  10:             {
  11:                 using (SqlCommand command = new SqlCommand("insert into person (name,lastname,birthdate,weight)values(@name,@lname,@date,@peso)", conn))
  12:                 {
  13:                     conn.Open();
  14:                     command.Parameters.AddWithValue("@id", id);
  15:                     command.Parameters.AddWithValue("@name", Name);
  16:                     command.Parameters.AddWithValue("@lname", LastName);
  17:                     command.Parameters.AddWithValue("@date", Convert.ToDateTime(BirthDate));
  18:                     command.Parameters.AddWithValue("@peso", double.Parse(Weight));
  19:                     command.ExecuteNonQuery();
  20:                 }
  21:             }
  22:  
  23:         }
  24:         else if (String.Compare(oper, "edit", StringComparison.Ordinal) == 0)
  25:         {
  26:             using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["DataConnection"].ConnectionString))
  27:             {
  28:                 using (SqlCommand command = new SqlCommand("update person set name=@name,lastname=@lname,birthdate=@date,weight=@peso where id=@id", conn))
  29:                 {
  30:                     conn.Open();
  31:                     command.Parameters.AddWithValue("@id", id);
  32:                     command.Parameters.AddWithValue("@name", Name);
  33:                     command.Parameters.AddWithValue("@lname", LastName);
  34:                     command.Parameters.AddWithValue("@date", Convert.ToDateTime(BirthDate));
  35:                     command.Parameters.AddWithValue("@peso", double.Parse(Weight));
  36:                     command.ExecuteNonQuery();
  37:                 }
  38:             }
  39:         }
  40:        
  41:  
  42:     }

 

Finalmente así se verá el trabajo

Ejemplo de descarga



Colorear celdas y filas en un Gridview

Últimamente he visto muchas consultas sobre como colorear las celdas o filas de un gridview basados en un determinado valor o condición.

Así que el objetivo de este post es mostrar como realizar este procedimiento, pero además añadiremos un elemento mas, que es como mostrar una imagen dependiendo de una condición.

Empezaremos trabajando en como cambiar el color de una fila del Gridview:

Supongamos que tenemos un Gridview que tiene un campo de tipo Literal en un TemplateField que muestra un total o cantidad y queremos con base a ese total colorear la fila completa.

   1: <asp:TemplateField HeaderText="Total">
   2:                     <ItemTemplate>
   3:                         <asp:Literal ID="ltltotal" runat="server" Text='<%# Eval("total2") %>' />
   4:                     </ItemTemplate>
   5:                 </asp:TemplateField>

Para lograr que nuestras celdas puedan cambiar de Color, utilizaremos el Evento OnRowDataBound el cual se ejecuta por cada celda del GridView , por tanto, dicho evento debe estar asociado a nuestro Gridview de lo contrario no se ejecutará

   1: <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="false" Width="524px"
   2:          OnRowDataBound="GridView1_RowDataBound">

Una vez asociado el evento y creado el ItemTeplate, es hora de programar el evento RowDataBound desde el CodeBehind

   1: protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
   2:    {
   3:        if (e.Row.RowType == DataControlRowType.DataRow)
   4:        {
   5:            Literal tot =(Literal)e.Row.FindControl("ltltotal");
   6:            Double total = Double.Parse(tot.Text);
   7:  
   8:            if (total == 500)
   9:            {
  10:                e.Row.BackColor = Color.FromName("#c6efce");
  11:                //e.Row.Cells[2].BackColor = Color.FromName("#c6efce");
  12:            }
  13:            if (total >= 600)
  14:            {
  15:                e.Row.BackColor = Color.FromName("#ffeb9c");
  16:               
  17:            }
  18:            if (total <300 )
  19:            {
  20:                e.Row.BackColor = Color.FromName("#ffc7ce");
  21:               
  22:            }
  23:        }
  24:    }

Si examinamos el código anterior, vemos dos cosas importantes:

1. Accedemos al control en el GridView mediante el FindControl

2. Convertir,os ese objeto a double para poder hacer una comparación numérica.

El color a la fila (row) lo asignamos mediante la propiedad BackColor, si observamos con detenimiento, a esa propiedad le estamos asignando la propiedad Color.FromName(“numero de color”).

Por supuesto que esta no es la única forma de asignar el color, también podríamos hacerlo utilizando Color.NombreColor.

Una vez completado esto, nuestro gridview se vería de la siguiente forma

Ahora, nuestro segundo trabajo será hacer que el gridView también se coloreo pero esta vez no toda la fila sino únicamente las celdas. De igual forma, requerimos el evento RowDataBound, solo que esta vez la forma de asignar el color varia, pues se hace directamente sobre la celda.

   1: protected void GridView2_RowDataBound(object sender, GridViewRowEventArgs e)
   2:   {
   3:       if (e.Row.RowType == DataControlRowType.DataRow)
   4:       {
   5:           Literal tot = (Literal)e.Row.FindControl("ltltotal");
   6:           Double total = Double.Parse(tot.Text);
   7:  
   8:           if (total == 500)
   9:           {
  10:              
  11:               e.Row.Cells[3].BackColor = Color.FromName("#c6efce");
  12:           }
  13:           if (total >= 600)
  14:           {
  15:               e.Row.Cells[3].BackColor = Color.FromName("#ffeb9c");
  16:               
  17:           }
  18:           if (total < 300)
  19:           {
  20:               e.Row.Cells[3].BackColor = Color.FromName("#ffc7ce");
  21:              
  22:           }
  23:       }
  24:   }

Al concluir nuestro GridView se vería así

Para terminar, haremos que nuestro gridview muestre una imagen distinta dependiendo de una condición. Esta vez haremos unos pequeños cambios:

1. Comparamos con base al valor aprobado, es decir un valor True/False para mostrar la imagen

2. Utilizamos la propiedad DataKeyNames del Gridview pues guardar el valor True/False, ya que la columna Aprobado no será mostrada sino únicamente la imagen.

3. Utilizaremos en control Image dentro de un ItemTemplate para mostrar la imagen nueva.

   1: <asp:GridView ID="GridView3" runat="server" AutoGenerateColumns="false" Width="524px"
   2:      DataKeyNames="aprovado" OnRowDataBound="GridView3_RowDataBound">
   1: <asp:TemplateField HeaderText="Aprobado">
   2:                    <ItemTemplate>
   3:                       <asp:image ID="imgestado" runat="server" />
   4:                    </ItemTemplate>
   5:                </asp:TemplateField>

El evento rowDataBound

   1: protected void GridView3_RowDataBound(object sender, GridViewRowEventArgs e)
   2:   {
   3:       if (e.Row.RowType == DataControlRowType.DataRow)
   4:       {
   5:          
   6:           string KeyID = GridView3.DataKeys[e.Row.RowIndex].Value.ToString();
   7:  
   8:           
   9:           System.Web.UI.WebControls.Image imagen = (System.Web.UI.WebControls.Image)e.Row.FindControl("imgestado");
  10:          
  11:          if (KeyID == "True")
  12:           {
  13:               imagen.ImageUrl = "GreenCircle5.jpg";
  14:              
  15:           }
  16:           if (KeyID == "False")
  17:           {
  18:               imagen.ImageUrl = "red-24.png";
  19:              
  20:           }
  21:        
  22:       }
  23:   }

En el código anterior lo mas importante, es la forma en que el valor del DataKeysNames es tomado, vemos que lo realizamos mediante el e.Row.RowIndex y lo convertimos a string para luego poder evaluarlo.

Por último les dejo el código de ejemplo



Encriptar QueryString en URL

Luego de varios días de trabajo pesado, nuevamente tengo tiempo para publicar en mi blog.

Para este post trataré de dar respuesta a una pregunta que se genera constantemente en los foros de MSDN, como encryptar los valores enviados a través del queryString en una URL.

Primero que nada he de decir que métodos de encriptación hay muchos, desde los personales hasta el MD5 y todos los conocidos. En nuestro ejemplo, trabajaremos con un código de encriptación utilizando el DESCryptoServiceProvider de asp.net más el método Left que se encuentra en la clase que publique en un post anterior

Funciones Con String

Para iniciar, debo comentar que para nuestra clase de encriptación crearemos dos métodos uno para encriptar y otro para desencriptar. Algunos se preguntaran para que el método de desencriptar. Pues sencillo, si estamos enviando valores por medio del QueryString en una URL, es porque esos valores los utilizaremos para realizar algún tipo de operación, entonces, que hacemos con unos valores encriptados? pues complicaríamos el trabajo un poco mas, así que, nuestros objetivos serán:

1. Encriptar los datos

2. Enviarlos encriptados por queryString

3.Recibirlos y desencriptarlos

4. Procesarlos.

Para empezar crearemos una nueva clase llamada Encryption, que contendrá nuestro métodos

Seguidamente, declararemos dos variables de Tipo byte que contendrán los datos que encriptemos y una de tipo string que contendrá los caracteres de encriptación

   1: static byte[] key = { };
   2:     static byte[] IV = { 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef };
   3:     static string sEncryptionKey = "!#$a54?3";

 

Ahora, agregamos un nuevo método llamado Decrypt que será el encargado de desencriptar los datos enviados

   1: public string Decrypt(string stringToDecrypt)
   2:    {
   3:        byte[] inputByteArray = new byte[stringToDecrypt.Length + 1];
   4:        try
   5:        {
   6:            key = System.Text.Encoding.UTF8.GetBytes(LeftRightMid.Left(sEncryptionKey, 8));
   7:            DESCryptoServiceProvider des = new DESCryptoServiceProvider();
   8:            inputByteArray = Convert.FromBase64String(stringToDecrypt);
   9:            MemoryStream ms = new MemoryStream();
  10:            CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(key, IV), CryptoStreamMode.Write);
  11:            cs.Write(inputByteArray, 0, inputByteArray.Length);
  12:            cs.FlushFinalBlock();
  13:            System.Text.Encoding encoding = System.Text.Encoding.UTF8;
  14:            return encoding.GetString(ms.ToArray());
  15:        }
  16:        catch (Exception e)
  17:        {
  18:            return e.Message;
  19:        }
  20:    }

 

Y por ultimo creamos el método mas importante, el de encriptación

   1: public string Encrypt(string stringToEncrypt)
   2:    {
   3:        try
   4:        {
   5:            key = System.Text.Encoding.UTF8.GetBytes(LeftRightMid.Left(sEncryptionKey, 8));
   6:            DESCryptoServiceProvider des = new DESCryptoServiceProvider();
   7:            byte[] inputByteArray = Encoding.UTF8.GetBytes(stringToEncrypt);
   8:            MemoryStream ms = new MemoryStream();
   9:            CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(key, IV), CryptoStreamMode.Write);
  10:            cs.Write(inputByteArray, 0, inputByteArray.Length);
  11:            cs.FlushFinalBlock();
  12:            return Convert.ToBase64String(ms.ToArray());
  13:        }
  14:        catch (Exception e)
  15:        {
  16:            return e.Message;
  17:        }
  18:    }

Como vemos, el proceso de encriptación puede ser algo sencillo que realmente puede ayudar mucho a la seguridad de un sitio, especialmente cuando los datos que enviamos son de extremo cuidado.

Como siempre, para terminar el código de ejemplo