Archivo del sitio

Datos en linea con WebSockets y C#


Actualmente me encuentro desarrollando un Dashboard y un sistema que trabaja con flujos de trabajo en tiempo real, lo cual me parecio un problema dado que tenia que hacer llamadas todo el tiempo al BackEnd para poder obtener los datos, generalmente usas un setInterval con llamada a Ajax asincrono, pero me daba vueltas la cabeza de la “talacha” que tendría que hacer.

Por lo tanto, me he visto obligado a hacer una pequeña investigación que maneja WebSockets, al utlizar MVC5 mi primera idea fue usar SignalR, pero luego recordé que todo lo que hago tiene que ser portable de alguna manera a otros lenguajes y se me paso, así que adiós SignalR, por lo tanto regresemos al origen de todo los Sockets, estos los puedo programar como me convenga en cualquier lenguaje, ya sea C#, Pyton, Java, C, lo cual me permite portar el servidor a donde lo necesite

Al comenzar esta tarea decidí hacer una consola, ya que después podré levantar un Servicio con el resultado, para esto tengo que crear un Socket – Asyncrono, el cuál me permitirá obtener los datos.

Para el caso del cliente, como su base es HTML en si mismo es portable a cualquier lenguaje web ya sea PHP, C# con MVC o JAVA con Groovy

Que debe hacer el programa Servidor:

  • Crear un Programa de Consola
  • Crear un SocketServer
  • Habilitar la Autenticación del SocketCliente
  • Obtener Datos de una Base de Datos
  • Enviar los datos cada determinado tiempo

Vale la pena mencionar la siguiente pagina https://developer.mozilla.org/es/docs/WebSockets-840092-dup/Escribiendo_servidores_con_WebSocket que es muy importante para comprender el concepto e implementación de los sockets server

Despues de haber definido lo que hará el programa servidor, haremos manos a la obra.

Abriremos nuestro Visual Studio y pondremos el nombre de la solución como WebSocketServer como vemos en la siguiente imagen:

Luego vamos a ir definiendo el codigo del socket, al ser un socket asincrono, es necesario utilizar solamente los metodos BeginAccept y BeginReceive, lo cual nos permitirá hacerlo completamente asincrono.

Vamos a definir las siguientes Variables:


static private string guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B13";
static List<Socket> lstSocket = new List<Socket>();
static Socket serverSocket;
static byte[] b = new byte[1024];

En el codigo de Main Crearemos el Listen de nuestro Socket:


static void Main(string[] args)
{
serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
serverSocket.Bind(new IPEndPoint(IPAddress.Any, 9090));
serverSocket.Listen(128);
serverSocket.BeginAccept(null, 0, OnAccept, null);
Console.ReadKey();
}

Con el codigo anterior, estamos poniendo a escuchar el socket debemos mirar la seccion OnAccept que es el metodo al que hará referencia y que crearemos para continuar con la programación del Socket como veremos en el siguiente codigo:


private static void OnAccept(IAsyncResult result)
{
try
{
Socket client = null;
if (serverSocket != null && serverSocket.IsBound)
{
client = serverSocket.EndAccept(result);
}
if (client != null)
{
/* Handshaking and managing ClientSocket */
if (b == null) { b = new byte[1024]; }
client.BeginReceive(b, 0, b.Length, SocketFlags.None, OnReceive, client);
serverSocket.BeginAccept(null, 0, OnAccept, null);
}
}
catch (SocketException exception)
{
serverSocket.BeginAccept(null, 0, OnAccept, null);
}
}

En el codigo anterior hacemos tambien una llamada OnReceives en el cual tenemos el codigo principal de la información, como veremos a continuación:

</pre>
private static void OnReceive(IAsyncResult AR)
{
byte[] ResponseBytes = null;
Console.WriteLine("Recieving");
Socket client = (Socket)AR.AsyncState;
int dataLen = client.EndReceive(AR);
byte[] dataBuff = new byte[dataLen];
Array.Copy(b, dataBuff, dataLen);

string szReceived = Encoding.ASCII.GetString(dataBuff, 0, dataBuff.Length);

if (!lstSocket.Contains(client))
{
Console.WriteLine(szReceived);
lstSocket.Add(client);
#region ParseHeaders
var headers = new Dictionary<string, string="">();</string,>
string[] lines = szReceived.Split('\n');
foreach (string line in lines)
{
var tokens = line.Split(new char[] { ':' }, 2);
if (!string.IsNullOrWhiteSpace(line) && tokens.Length > 1)
{
headers[tokens[0]] = tokens[1].Trim();
}
}
string Key = headers["Sec-WebSocket-Key"];
#endregion
#region CreateResponseHeaders
string ResponseKey = AcceptKey(ref Key);

var response =
"HTTP/1.1 101 WebSocket Protocol Handshake" + Environment.NewLine +
"Upgrade: WebSocket" + Environment.NewLine +
"Connection: Upgrade" + Environment.NewLine +
"Sec-WebSocket-Origin: " + headers["Origin"] + Environment.NewLine +
"Sec-WebSocket-Accept: " + ResponseKey + Environment.NewLine +
"Sec-WebSocket-Location: ws://localhost:8080/websession" + Environment.NewLine +
Environment.NewLine;
#endregion
ResponseBytes = Encoding.ASCII.GetBytes(response);
client.Send(ResponseBytes);
}
else
{
Console.Write(szReceived + " = ");
#region DecodeMessage
string Path = DecodeMessage(dataBuff);
#endregion
#region CreateTimerToSendInfo
Timer t = new Timer();
t.Enabled = true;
t.Interval = 5000;
t.Start();
t.Elapsed += (sender, args) => ExecuteQuery(sender, client, Path);
#endregion
Console.WriteLine(Path);
string Data = DataInfo(Path);
ResponseBytes = EncodeMessageToSend(Data);
client.Send(ResponseBytes);
}
if (b != null) { b = new byte[1024]; }
try
{
client.BeginReceive(b, 0, b.Length, SocketFlags.None, OnReceive, client);
}
catch (Exception ex)
{
Console.WriteLine("Error");
}
}
<pre>

Lo más relevante que podemos encontrar el en metodo OnRecieve  es que revisamos que el socket se encuentre en una lista de Sockets, esto nos permite identificar cada conexión, esto nos da dos opciones cuando nos envian Headers o Datos, los Headers siempre se mandarán en la primera conexión, todo lo demás para nosotros siempre serán Datos o Parametros o Consultas… o Algo es un Socket, siempre va a ser texto.

Headers

Lo primero es identificar las etiquetas de #region y #endregion que son las marcas que están puestas en el codigo para poder identificarlo y analizarlo parte por parte lo primero es la sección de ParseHeaders al ser un WebServer obviamente es necesario obtener los tokens y ponerlos en una lista que podamos utilizar ya que los encabezados son necesarios para obtener una única información que nos manda el socket cliente, y es Sec-WebSocket-Key para retornar la información al socket cliente como podemos ver en las etiquetas CreateResponseHeaders finalmente retornamos los encabezados de respuesta al Socket con el metodo client.Send. A todo este proceso se le llama Handshake.

Información

En el contenido del else como podemos ver se encuentran las regiones DecodeMessage que en la cual esta el método que decodificara el mensaje que nos envié el WebSocketCliente y la región CreateTimerToSendInfo la cual crea un pequeño timer que nos permite enviar la información cada cierto tiempo.

La información estará definida por el método DataInfo, que prácticamente contiene un Swith con una palabra mágica para saber que datos se van a enviar, después la información retornada en string será enviada al codificador del mensaje, para finalmente enviar la respuesta de la petición por primera vez.

Podemos ver que dentro del sistema se utilizan los siguientes Metodos:

  • AcceptKey
  • DecodeMessage
  • ExecuteQuery
  • EncodeMessageToSend
  • DataInfo

Los cuales se mostrarán a continuación:

AcceptKey es utilizado para generar la nueva llave apartir de la que el clientre nos envio:


private static string AcceptKey(ref string key)
{
string longKey = key + guid;
SHA1 sha1 = SHA1CryptoServiceProvider.Create();
byte[] hashBytes = sha1.ComputeHash(System.Text.Encoding.ASCII.GetBytes(longKey));
return Convert.ToBase64String(hashBytes);
}

ExcuteQuery es el Metodo que será utilizado como delegado para la propiedad Elapsed:


public static void ExecuteQuery(object sender, Socket client, string Path)
{
string Data = DataInfo(Path);
byte[] ResponseBytes = EncodeMessageToSend(Data);
if (client.Connected)
{
client.Send(ResponseBytes);
}
else
{
client.Disconnect(false);
lstSocket.Remove(client);
}
}

DataInfo es el metodo que nos permitirá obtener informaciónes segun haya llamadas a la base de datos, como puedes ver en el metodo, no hay conexión a la base pero esa implementación la pueden hacer ustedes ya a su gusto:


public static string DataInfo(string Path)
{
string json = string.Empty;
Random rnd = new Random();
switch (Path)
{
case "DATOS1":
json = "[{\"Key\":"+ rnd.Next(0, 100) +",\"Value\":\"" + Guid.NewGuid() + "\"},{\"Key\":"+rnd.Next(0, 100)+",\"Value\":\"" + Guid.NewGuid() + "\"}]";
break;
case "DATOS2":
json = "[{\"KeyData\":"+ rnd.Next(0, 100) +",\"ValueData\":\"" + Guid.NewGuid() + "\"},{\"KeyData\":"+rnd.Next(0, 100)+",\"ValueData\":\"" + Guid.NewGuid() + "\"}]";
break;
}
return json;
}

DecodeMessage es un metodo un tanto complicado, dado que se debe seguir la implmentación mencionada en la pagina dada al inicio de este post Especificamente Aqui.


private static String DecodeMessage(Byte[] bytes)
{
String incomingData = String.Empty;
Byte secondByte = bytes[1];
Int32 dataLength = secondByte & 127;
Int32 indexFirstMask = 2;
if (dataLength == 126)
indexFirstMask = 4;
else if (dataLength == 127)
indexFirstMask = 10;

IEnumerable<Byte> keys = bytes.Skip(indexFirstMask).Take(4);
Int32 indexFirstDataByte = indexFirstMask + 4;

Byte[] decoded = new Byte[bytes.Length - indexFirstDataByte];
for (Int32 i = indexFirstDataByte, j = 0; i < bytes.Length; i++, j++)
{
decoded[j] = (Byte)(bytes[i] ^ keys.ElementAt(j % 4));
}

return incomingData = Encoding.UTF8.GetString(decoded, 0, decoded.Length);
}

EncodeMessageToSend hace practicamente lo mismo pero de manera inversa:


private static Byte[] EncodeMessageToSend(String message)
{
Byte[] response;
Byte[] bytesRaw = Encoding.UTF8.GetBytes(message);
Byte[] frame = new Byte[10];

Int32 indexStartRawData = -1;
Int32 length = bytesRaw.Length;

frame[0] = (Byte)129;
if (length <= 125)
{
frame[1] = (Byte)length;
indexStartRawData = 2;
}
else if (length >= 126 && length <= 65535)
{
frame[1] = (Byte)126;
frame[2] = (Byte)((length >> 8) & 255);
frame[3] = (Byte)(length & 255);
indexStartRawData = 4;
}
else
{
frame[1] = (Byte)127;
frame[2] = (Byte)((length >> 56) & 255);
frame[3] = (Byte)((length >> 48) & 255);
frame[4] = (Byte)((length >> 40) & 255);
frame[5] = (Byte)((length >> 32) & 255);
frame[6] = (Byte)((length >> 24) & 255);
frame[7] = (Byte)((length >> 16) & 255);
frame[8] = (Byte)((length >> 8) & 255);
frame[9] = (Byte)(length & 255);

indexStartRawData = 10;
}

response = new Byte[indexStartRawData + length];

Int32 i, reponseIdx = 0;

//Add the frame bytes to the reponse
for (i = 0; i < indexStartRawData; i++)
{
response[reponseIdx] = frame[i];
reponseIdx++;
}

//Add the data bytes to the response
for (i = 0; i < length; i++)
{
response[reponseIdx] = bytesRaw[i];
reponseIdx++;
}

return response;
}

Con estos metodos terminamos nuestro socket server ahora vamos a compilar nuestro servidor, vamos a ejecutarlo y nos mostrará una pantalla negra, y dejemoslo esperando ahí. y crearemos un cliente creando nuestro html con el siguiente codigo:

</pre>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js">
<style>
#Data1 {
float:left;
}

#Data2 {
float:left;
background:#fc0;
}
</style>
<body>
<div id="Data1"></div>
<div id="Data2"></div>
</body>

<script>
$(document).ready(function () {

var uri = "ws://localhost:9090/api/test";

//Initialize socket
websocket = new WebSocket(uri);
//Open socket and send message
websocket.onopen = function () {
$('#messages').prepend('
<div>Connected to server.</div>
');
websocket.send("DATOS1");
websocket.send("DATOS2");
};

//Socket error handler
websocket.onerror = function (event) {
alert("Error de Conexión");
};

//Socket message handler
websocket.onmessage = function (event) {
if (event.data != "[]") {
var parse = JSON.parse(event.data)
$.each(parse, function (i, data) {
if (data.KeyData == undefined) {
$('#Data1').prepend('
<div>Key:' + data.Key + ' Value: ' + data.Value + '</div>
');
}
else {
$('#Data2').prepend('
<div>Key:' + data.KeyData + ' Value: ' + data.ValueData + '</div>
');
}
});
}
};
});

</script>
<pre>

Una vez creado lo abriremos con el navegador de chrome y veremos el siguiente resultado:

Un Resultado interesante ¿No?, con esto ya no tenemos que hacer llamadas recursivas es el server el que se encarga de enviarnos la información y la pagina solo se dedica a parsear, imaginen las cosas que podrían hacerse.
Descarga(Pendiente)

Anuncios

POCO, POJO, POPO y otras chunches… Ahhh Y MetaData tambien…


POCO es un acrónimo de Plain Old CLR Object

POJO es un acrónimo de Plain Old Java Object

Ambos acrónimos son utilizados para definir clases simples que pueden ser utilizados por frameworks para ser llenados, como muchas personas que me siguen en este blog, se darán cuenta que son muy utilizados en mis post para crear clases de paso o clases de llenado de datos que a su vez generan listas, hoy me he encontrado con algo que me da flojera a ciencia cierta y es actualizar una clase junto con todos los cambios que estas representan en las diferentes capas que utilizo en el trabajo, por lo que hoy dedique mi tiempo a realizar una herramienta que me permite crear estas clases de “dominio” o “entidad” de manera rapida utilizando la MetaData de la base de datos, veamos el código para armarla y luego podremos descargarlo.

Crearemos una solución de Windows Forms para este caso, y agregaremos los siguientes elementos

  • cbDatabaseManager: Combo que contendrá las bases de datos para conectarse
  • Server: TextBox que nos permitirá escribir el servidor a conectarnos
  • Port: TextBox que nos permitirá cambiar de puerto si es necesario
  • User: TextBox que nos permitirá escribir el Usuario
  • Password: TextBox que nos permitirá escribir el Password
  • Connect: Boton que generará la conexión
  • DirectoryPath: Path donde serán guardadas nuestras clases
  • button1: (Ya comenzamos con problemas de flojera) Boton que nos permitirá seleccionar el Path donde guardaremos nuestras clases
  • cbDataBases: Combo que se llenará con las bases de datos una vez estemos conectados
  • lstbTables: ListBox  que nos permitirá mostrar las tablas y seleccionar más de una tabla para su generación en clase
  • cbLanguage: Combo que nos dirá en que lenguaje serán creados nuestros ¿POCO’s?, ¿POJO’s? lo que ustedes quieran siguien siendo clases simples y dependen de la sintaxis del lenguaje
  • Console: TextBox Multilinea que nos dira que esta pasando en nuestro proceso
  • button2: Boton que nos permitirá Ejecutar la generación de clases
  • fbdDirectory: Control de FileBoxDialog que nos permitirá en conjunto con el Boton1 seleccionar nuestro directorio

Una vez realizado nuestro formulario debe quedar más o menos así:

Bien una vez creado nuestro formulario creemos las clases para utilizarlo, crearemos una carpeta en la raíz de nuestro proyecto llamada Entity, en el cual crearemos las siguientes clases:

Catalog


publicclassCatalog
{
public string Key { get; set; }
public string Value { get; set; }
}

La clase anteriormente mostrada es generica, se utiliza para llenar catalogos tanto estaticos como dinamicos esta se utilizará para los combos y los listbox

Connector

 public class Connector
 {
 public string DataBaseManager { get; set; }
 public string Server { get; set; }
 public int Port { get; set; }
 public string Catalog { get; set; }
 public string User { get; set; }
 public string Password { get; set; }

 public string SourceConnectorMaster { get {
 StringBuilder conn = new StringBuilder();
 conn.Append("Data Source = ")
 .Append(Server)
 .Append("; User ID = ")
 .Append(User)
 .Append("; Password = ")
 .Append(Password);
 return conn.ToString();
 } }

 public string SourceConnectorComplete
 {
 get
 {
 StringBuilder conn = new StringBuilder();
 conn.Append("Data Source = ")
 .Append(Server)
 .Append("; Initial Catalog = ")
 .Append(Catalog)
 .Append("; User ID = ")
 .Append(User)
 .Append("; Password = ")
 .Append(Password);
 return conn.ToString();
 }
 }
 }

La clase connector contendrá la información de conexión a la base de datos así como los datos que recopilemos de la entrada del formulario además de tener 2 propiedades más SourceConnectorMaster y SourceConnectorComplete, las cuales definen las cadenas de conexion que se turilizarán en este caso es .NET, más adelante con un poco de imaginación podemos agregar más cadenas de conexión de diferentes Administradores de Bases de Datos.

MetaInfo

 public class MetaInfo
 {
 public string Table { get; set; }
 public string FieldName { get; set; }
 public string Type { get; set; }
 public bool ISNullable { get; set; }
 public int MaxChar { get; set; }
 }

La clase MetaInfo contendrá la información de la Metadata de la base de datos Información de Tablas, Tamaños, Nombres de Columna, Tipos de Dato, etc…

CacheManager

 public static class CacheManager
 {
 public static List<Catalog> DataBaseManager;
 public static List<Catalog> Language;

 public static void init()
 {
 DataBaseManager = new List<Catalog>() {
 new Catalog() { Key="1", Value="SQLServer" }
 };

 Language = new List<Catalog>()
 {
 new Catalog() { Key="1", Value="C#" },
 new Catalog() { Key="2", Value="Java"}
 };
 }
 }

La clase CacheManager es una clase que se inicializará al abrir nuestro formulario y por lo tanto estatica, la cual contiene las listas estaticas dentro de nuestro programa, se utiliza para llenar los combos de Lenguaje y DataBaseManager, esto nos permite ir mostrando mientras crecemos la implementación, Estamos dieñando un programita, pero recordemos que si trabajamos en diferentes lenguajes seria un error crear una herramienta solo para uno de los lenguajes que utilizamos y peor aun si ligamos los lenguajes a bases de datos podria decirse que nativas o de paridad emmm… que quiero decir con esto, que si usas C# probablemente estes usando .NET o en su defecto si usas PHP, seguramente estas usando MySQL por esto es que voy a hacer el programa de tal manera que pueda crecer a mis necesidades.

Hora de preparar las conexiones a base de datos en este caso por prisa, solo utilizare, net; pero no obstante dejaré el proyecto preparado para crecer, así que crearé una carpeta DataAccess y dentro de ella una interface IData y una clase SQLServer, las cuales me permitirán trabajar y extender mi trabajo despues, veamos el contenido:

IData


public interface IData
{
List<Catalog> GetTables(string Connector);

List<Catalog> GetDataBases(string Connector);

List<MetaInfo> GetMetaInfo(string Connector);
}

Esta clase podemos ver que solo tiene las funciones que voy a utilizar en las demás clases esto me permitirá usar un poco de polimorfismo y algunos factorys para vivir mi experiencia un poco más ordenada (Si, a mi me va bien así…)

SQLServer


public class SQLServer : IData
{
public List GetMetaInfo(string Connector)
{
List<MetaInfo> metaInfo = new List<MetaInfo>();
SqlConnection conn = new SqlConnection(Connector);
try
{
conn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = conn;
#region Query
cmd.CommandText = @"SELECT
C.TABLE_CATALOG,C.TABLE_NAME,C.COLUMN_NAME,C.IS_NULLABLE,C.DATA_TYPE,C.CHARACTER_MAXIMUM_LENGTH
FROM INFORMATION_SCHEMA.COLUMNS C
INNER JOIN INFORMATION_SCHEMA.TABLES A ON C.TABLE_NAME=A.TABLE_NAME
WHERE A.TABLE_NAME NOT IN('SYSSCHEMAARTICLES','SYSPUBLICATIONS','MSREPL_ERRORS','TRANSACTIONS','MSMERGE_HISTORY','MSMERGE_AGENT_PARAMETERS','MSMERGE_REPLINFO',
'SYSSUBSCRIPTIONS','SYSMERGEARTICLES','VW_TRAMITES_CONCEPTOS','SYSARTICLEUPDATES','MSPUB_IDENTITY_RANGE','SYSTRANSCHEMAS','SYSMERGESCHEMAARTICLES','SYSMERGEPARTITIONINFOVIEW',
'SYSMERGEPARTITIONINFO','MSMERGE_SUPPORTABILITY_SETTINGS','MSMERGE_SESSIONS','MSMERGE_PAST_PARTITION_MAPPINGS','MSMERGE_GENERATION_PARTITION_MAPPINGS','SYSMERGESUBSCRIPTIONS',
'MSDYNAMICSNAPSHOTJOBS','MSDYNAMICSNAPSHOTVIEWS','MSMERGE_AGENT_PARAMETERS','MSMERGE_ARTICLEHISTORY','MSMERGE_ALTSYNCPARTNERS','MSMERGE_CONTENTS','MSMERGE_CURRENT_PARTITION_MAPPINGS',
'MSMERGE_DYNAMIC_SNAPSHOTS','MSMERGE_ERRORLINEAGE','MSMERGE_GENHISTORY','MSMERGE_IDENTITY_RANGE','MSMERGE_LOG_FILES','MSMERGE_METADATAACTION_REQUEST','MSMERGE_PARTITION_GROUPS',
'MSMERGE_SETTINGSHISTORY','MSMERGE_TOMBSTONE','MSPEER_CONFLICTDETECTIONCONFIGREQUEST','MSPEER_LSNS','MSPEER_ORIGINATORID_HISTORY','MSPEER_REQUEST','MSPEER_TOPOLOGYREQUEST',
'SYSARTICLECOLUMNS','SYSARTICLES','SYSEXTENDEDARTICLESVIEW','SYSMERGEEXTENDEDARTICLESVIEW','SYSMERGEPUBLICATIONS','SYSMERGESCHEMACHANGE'
)
AND A.TABLE_TYPE NOT IN('VIEW')
ORDER BY A.TABLE_NAME";
#endregion
cmd.CommandType = System.Data.CommandType.Text;
cmd.CommandTimeout = 0;
using (SqlDataReader r = cmd.ExecuteReader())
{
while (r.Read())
{
MetaInfo mi = new MetaInfo();
mi.Table = Convert.ToString(r["TABLE_NAME"]);
mi.FieldName = Convert.ToString(r["COLUMN_NAME"]);
mi.Type = Convert.ToString(r["DATA_TYPE"]);
mi.ISNullable = Convert.ToString(r["IS_NULLABLE"]) == "YES" ? true : false;
metaInfo.Add(mi);
}
}
}
conn.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return metaInfo;
}

public List<Catalog> GetDataBases(string Connector)
{
List<Catalog> catalogs = new List();
Catalog c = new Catalog();
c.Key = "0";
c.Value = "Select one";
catalogs.Add(c);
SqlConnection conn = new SqlConnection(Connector);
try
{
conn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = conn;
#region Query
cmd.CommandText = @"SELECT name, database_id FROM sys.databases";
#endregion
cmd.CommandType = System.Data.CommandType.Text;
cmd.CommandTimeout = 0;
using (SqlDataReader r = cmd.ExecuteReader())
{
while (r.Read())
{
c = new Catalog();
c.Key = Convert.ToString(r["database_id"]);
c.Value = Convert.ToString(r["name"]);
catalogs.Add(c);
}
}
}
conn.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return catalogs;
}

public List GetTables(string Connector)
{
List<Catalog> catalogs = new List<Catalog>();
SqlConnection conn = new SqlConnection(Connector);
try
{
conn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = conn;
#region Query
cmd.CommandText = @"SELECT * FROM INFORMATION_SCHEMA.TABLES ORDER BY TABLE_NAME";
#endregion
cmd.CommandType = System.Data.CommandType.Text;
cmd.CommandTimeout = 0;
using (SqlDataReader r = cmd.ExecuteReader())
{
while (r.Read())
{
Catalog c = new Catalog();
c.Key = Convert.ToString(r["TABLE_NAME"]);
c.Value = Convert.ToString(r["TABLE_NAME"]);
catalogs.Add(c);
}
}
}
conn.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return catalogs;
}
}

Como podemos ver la clase SQLServer hereda de IData, esto me permite implementar los mismos metodos en todas mis clases ahora solo veamos que es lo que debemos hacer en cuanto a los metodos, creo que si eres desarrollador en .NET lo importante no es tanto el código si no más bien los querys veamos la información y expliquemoslos de poco en poco:

Metodo GetDataBases


SELECT name, database_id FROM sys.databases

Con este query obtenemos la información de todas las bases de datos en la instancia

Metodo GetTables

SELECT * FROM INFORMATION_SCHEMA.TABLES ORDER BY TABLE_NAME

Este query a su vez nos devuelve las tablas que estan dentro de una base de datos seleccionada

Metodo MetaInfo

SELECT 
 C.TABLE_CATALOG,C.TABLE_NAME,C.COLUMN_NAME,C.IS_NULLABLE,C.DATA_TYPE,C.CHARACTER_MAXIMUM_LENGTH
 FROM INFORMATION_SCHEMA.COLUMNS C
 INNER JOIN INFORMATION_SCHEMA.TABLES A ON C.TABLE_NAME=A.TABLE_NAME
 WHERE A.TABLE_NAME NOT IN('SYSSCHEMAARTICLES','SYSPUBLICATIONS','MSREPL_ERRORS','TRANSACTIONS','MSMERGE_HISTORY','MSMERGE_AGENT_PARAMETERS','MSMERGE_REPLINFO',
 'SYSSUBSCRIPTIONS','SYSMERGEARTICLES','VW_TRAMITES_CONCEPTOS','SYSARTICLEUPDATES','MSPUB_IDENTITY_RANGE','SYSTRANSCHEMAS','SYSMERGESCHEMAARTICLES','SYSMERGEPARTITIONINFOVIEW',
 'SYSMERGEPARTITIONINFO','MSMERGE_SUPPORTABILITY_SETTINGS','MSMERGE_SESSIONS','MSMERGE_PAST_PARTITION_MAPPINGS','MSMERGE_GENERATION_PARTITION_MAPPINGS','SYSMERGESUBSCRIPTIONS',
 'MSDYNAMICSNAPSHOTJOBS','MSDYNAMICSNAPSHOTVIEWS','MSMERGE_AGENT_PARAMETERS','MSMERGE_ARTICLEHISTORY','MSMERGE_ALTSYNCPARTNERS','MSMERGE_CONTENTS','MSMERGE_CURRENT_PARTITION_MAPPINGS',
 'MSMERGE_DYNAMIC_SNAPSHOTS','MSMERGE_ERRORLINEAGE','MSMERGE_GENHISTORY','MSMERGE_IDENTITY_RANGE','MSMERGE_LOG_FILES','MSMERGE_METADATAACTION_REQUEST','MSMERGE_PARTITION_GROUPS',
 'MSMERGE_SETTINGSHISTORY','MSMERGE_TOMBSTONE','MSPEER_CONFLICTDETECTIONCONFIGREQUEST','MSPEER_LSNS','MSPEER_ORIGINATORID_HISTORY','MSPEER_REQUEST','MSPEER_TOPOLOGYREQUEST',
 'SYSARTICLECOLUMNS','SYSARTICLES','SYSEXTENDEDARTICLESVIEW','SYSMERGEEXTENDEDARTICLESVIEW','SYSMERGEPUBLICATIONS','SYSMERGESCHEMACHANGE'
 )
 AND A.TABLE_TYPE NOT IN('VIEW')
 ORDER BY A.TABLE_NAME

Y este nos da la información de tablas agrupadas por columnas con sus respectivas informaciónes por cada registro.

Una vez realizado esto tenemos la mitad de nuestro codigo para jugar, en realidad la otra mitad esta en la generación de los archivos, para hacerlo con más velocidad solo crearemos el de generacion de clases en C#; en la carpeta Entity crearemos otra carpeta llamada Servicio en la cual crearemos IFileWriter, CSharpFile y FileWriterFactory. Y veremos como nos queda:

IFileWriter

public interface IFileWriter
{
void GenerateFile(string Table, List meta);
void Save(string Path);
}

Nuevamente definimos una interfaz que nos apoyará en el camino de la extención de nuestro código.

CSharpFile

 public class CSharpFile : IFileWriter
 {
 private string Data;
 private string NameFile;
 public void GenerateFile(string Table, List<MetaInfo> meta)
 {
 this.NameFile = Table + ".cs";
 StringBuilder str = new StringBuilder();
 List<MetaInfo> TableInfo = meta.Where(MI => MI.Table == Table).ToList();
 str.Append("public class ").Append(Table).Append(" {").Append(Environment.NewLine);
 foreach (MetaInfo FieldInfo in TableInfo)
 {
 str.Append("\tpublic ");
 switch (FieldInfo.Type)
 {
 case "nchar":
 str.Append("string").Append(" ");
 break;
 case "varchar":
 str.Append("string").Append(" ");
 break;
 case "text":
 str.Append("string").Append(" ");
 break;
 case "int":
 str.Append("int").Append(FieldInfo.ISNullable ? "?" : "").Append(" ");
 break;
 case "bigint":
 str.Append("long").Append(FieldInfo.ISNullable ? "?" : "").Append(" ");
 break;
 case "smallint":
 str.Append("short").Append(FieldInfo.ISNullable ? "?" : "").Append(" ");
 break;
 case "tinyint":
 str.Append("short").Append(FieldInfo.ISNullable ? "?" : "").Append(" ");
 break;
 case "datetime":
 str.Append("DateTime").Append(FieldInfo.ISNullable ? "?" : "").Append(" ");
 break;
 case "money":
 str.Append("decimal").Append(FieldInfo.ISNullable ? "?" : "").Append(" ");
 break;
 case "bit":
 str.Append("bool").Append(FieldInfo.ISNullable ? "?" : "").Append(" ");
 break;
 }
 str.Append(FieldInfo.FieldName).Append(" { get;set; }").Append(Environment.NewLine);
 }
 str.Append(" } ");
 Data = str.ToString();
 }
 public void Save(string Path)
 {
 string FullPath = System.IO.Path.Combine(Path, NameFile);
 System.IO.StreamWriter file = new System.IO.StreamWriter(FullPath);
 file.WriteLine(Data);
 file.Close();
 }
 }

Este archivo implementa los metodos de IFileWriter y nos permitirá crear una seccion que se dedicará solamente al codigo de .NET, no creo que tenga que explicar gran cosa, simplemente se crea un string que contiene la clase parseada en base a una serie de patrones

FileWriterFactory

 public static class FileWriterFactory
 {
 public static IFileWriter GenerateWriter(string Language)
 {
 IFileWriter writer = null;
 switch (Language)
 {
 case "C#":
 writer = new CSharpFile();
 break;
 case "Java":
//...
 break;
 case "PHP":
//...
 break;
 }
 return writer;
 }
 }

Un factory que contiene un switch con la información de que tipo de lenguaje usaremos.

Una vez realizado es hora de darle funcionalidad a nuestro From inicial.

Agregaremos las siguientes lineas que definirán nuestro “repositorio” de datos (Repository) y nuestro conector


Connectorconnector = newConnector();
IData Repository;

Después agregaremos la funcion Onload del formulario, que nos permitirá ejecutar operaciones al momento de la carga del form

 private void Form1_Load(object sender, EventArgs e)
 {
 CacheManager.init();
 cbLanguage.DisplayMember = cbDatabaseManager.DisplayMember = "Value";
 cbLanguage.ValueMember = cbDatabaseManager.ValueMember = "Key";
 cbLanguage.DataSource = CacheManager.Language;
 cbDatabaseManager.DataSource = CacheManager.DataBaseManager;
 }

Con estas operaciones llenamos los combos iniciales que son cbLanguaje y cbDatabaseManager.


private void Connect_Click(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(Server.Text) && string.IsNullOrEmpty(User.Text) && string.IsNullOrEmpty(Password.Text))
{
MessageBox.Show("The Server, User and Password can't be blank");
}

Catalog databaseManager = (Catalog)cbDatabaseManager.SelectedItem;

connector.Server = Server.Text;
connector.User = User.Text;
connector.Password = Password.Text;

switch (databaseManager.Value)
{
case "SQLServer":
Repository = new SQLServer();
break;
}

List<Catalog> DataBases = Repository.GetDataBases(connector.SourceConnectorMaster);
cbDataBases.DisplayMember = "Value";
cbDataBases.ValueMember = "Key";
cbDataBases.DataSource = DataBases;
}

Con el código anteriror damos funcionalidad al boton connect podemos checar nuestra conección y crear los repositorios.


privatevoid cbDataBases_SelectedIndexChanged(object sender, EventArgs e)
{
if (cbDataBases.SelectedItem != null)
{
Catalog dataBaseSelected = (Catalog)cbDataBases.SelectedItem;
if (dataBaseSelected.Key != "0")
{
connector.Catalog = Convert.ToString(dataBaseSelected.Value);
List<Catalog> Tables = Repository.GetTables(connector.SourceConnectorComplete);
lstbTables.ValueMember = "Key";
lstbTables.DisplayMember = "Value";
lstbTables.DataSource = Tables;
}
}
}

Despues agregamos el evento SelectedIndexChanged de cbDataBases que nos permite que cuando se seleccione un nuevo item del combo automaticamente nos muestre las tablas de la base de datos que se selecciono y los despliega en lstbTables

private void button1_Click(object sender, EventArgs e)
 {
 fbdDirectory.ShowDialog();
 DirectoryPath.Text = fbdDirectory.SelectedPath;
 }

Que nos da la funcionalidad para seleccionar carpeta.

 private void button2_Click(object sender, EventArgs e)
 {
 Catalog languaje = (Catalog)cbLanguage.SelectedItem;
 IFileWriter writer = FileWriterFactory.GenerateWriter(languaje.Value);
 if (writer == null)
 {
 MessageBox.Show("must be select a language");
 return;
 }

 List<MetaInfo> data = Repository.GetMetaInfo(connector.SourceConnectorComplete);
 foreach(Catalog item in lstbTables.SelectedItems) {
 writer.GenerateFile(item.Value, data);
 writer.Save(DirectoryPath.Text);
 Console.Text += Console.Text+ "Se ha Generado el archivo " + item.Value + " en "+ DirectoryPath.Text + Environment.NewLine;
 }
 }

y finalmente el botton2 que nos permitira generar el código…

Es interesante como siguiendo ciertos patrones de programación podemos generar código de manera automatica… y evitarnos la tediosa talacha.

(Descargar)

 

Programando con MVC y WebApi en C# (Parte 4)


En el apartado anterior hemos creado nuestras vistas en nuestro MVC y como conectarla, para agregar un registro ahora veremos como presentarla y como editarla, si no has leído el apartado anterior te recomiendo que lo hagas desde aquí.

En este apartado, agregaremos una vista secundaria a nuestra Student que se encuentra dentro de la carpeta vistas, a la cual llamaremos ListStudent.cshtml algo más o menos como lo siguiente:

Una vez creado nuestro archivo agregaremos el siguiente código:


<script src="@Url.Content("~/js/DataList/Student.js")"></script>
<div class="row">
    <div class="col-lg-12">
        <h1 class="page-header">
            Lista de Estudiantes
        </h1>
        <ol class="breadcrumb">
            <li>
                <i class="fa fa-dashboard"></i>  <a href="index.html">Dashboard</a>
            </li>
            <li class="active">
                <i class="fa fa-edit"></i>Lista de Estudiantes
            </li>
        </ol>
    </div>
</div>
<div class="row">
    <div class="table-responsive">
        <table id="lstStudent" class="table table-bordered table-hover table-striped">
            <thead>
                <tr>
                    <th>NoControl</th>
                    <th>Nombre</th>
                    <th>Apellido Paterno</th>
                    <th>Apellido Materno</th>
                    <th>CURP</th>
                    <th>Semestre</th>
                    <th>Opciones</th>
                </tr>
            </thead>
        </table>
    </div>
</div>

Ahora bien, una vez agregado este codigo descargaremos unas librerías que nos permitirán hacer algunas monadas con poco código puedes descargarlas desde aquí.

A partir de esto crearemos en la carpeta js una carpeta llamada CustomControls en la cual pondremos todos los JavaScript que utilizaremos nuestra solución quedará como la siguiente:

Agregaremos los scripts para que sean cargados por nuestra pagina

 


<script src="@Url.Content("~/js/CustomControls/CustomControls.js")"></script>
<script src="@Url.Content("~/js/CustomControls/CustomFunctions.js")"></script>
<script src="@Url.Content("~/js/CustomControls/CustomSimpleGrid.js")"></script>

Una vez realizado esto podemos dedicarnos a escribir la parte del script que dará funcionalidad a nuestro código y quedará de la siguiente manera:


<script>
    var students = [];
    var gridList;

    function GetGrid() {
//Agregamos una lista con los semestres para el DropDown
        var semestre = [];
        semestre.push({ id: 1, name: "1" });
        semestre.push({ id: 1, name: "2" });
        semestre.push({ id: 1, name: "3" });
        semestre.push({ id: 1, name: "4" });
        semestre.push({ id: 1, name: "5" });
        semestre.push({ id: 1, name: "6" });
        semestre.push({ id: 1, name: "7" });
//Creamos un Grid con la librería, se define un Layout y los campos que queremos que se muestren además del tipo de campo a mostrar y sus botones
        gridList = $('#lstStudent').gridView({
            layout: { "NoControl": 0, "Name": "", "Paternal": "", "Maternal": "", "CURP": "", "Semestre": 0 },
            columns: [
                { Field: "NoControl", ControlType: "Label", Source: null },
                { Field: "Name", ControlType: "Label", Source: null },
                { Field: "Paternal", ControlType: "Label", Source: null },
                { Field: "Maternal", ControlType: "Label", Source: null },
                { Field: "CURP", ControlType: "Label", Source: null },
                { Field: "Semestre", ControlType: "Dropdown", Source: semestre },
                                {
                                    Field: "Opciones", ControlType: "Buttons",
                                    Buttons: {
                                        Edit: '<button type="button" class="btn btn-success btn-circle btn-edit-item"><i class="fa fa-pencil"></i></button>'
                                    }
                                }
            ]
        });
    }
//Esta funcion nos permite darle funcionalidad a los botones que tengamos en la tabla
    function TableFunction() {
        $('#lstStudent tbody').on('click', 'button', function () {
            var button = $(this);
            var item = $(this).parent().parent();
            var tbody = $(this).parent().parent().parent();
            var data = $(item).attr('data-row');
            if (typeof (data) == "string") {
                if (data != "") {
                    data = JSON.parse($(item).attr('data-row'));
                }
            }
            if (button.hasClass('btn-edit-item')) {
//Si existe la clase btn-edit-item se da por sentado que se ha terminado de editar el o los registros del item
//No debemos preocuparnos ya que la librería edita la variable de manera interna
                Update(data);
            }
        });
    }

    $(document).ready(function () {
        GetGrid();
//Se pide la lista de los estudiantes a nuestro webapi desde el javascript student.js
        students = getStudents();
//Creamos el Grid con los resultados de la lista
        gridList.gridView("Create", students);
//Agregamos funcionalidad al grid
        TableFunction();
    });
</script>

Este script manda a llamar a nuestro archivo CustomSimpleGrid.js el cual nos da las herramientas para crear un Grid, es muy simple de utilizar y además editable al nivel que decidamos, como podemos ver en las siguientes imagenes:

 

Y con esto hemos finalizado el tutorial de MVC y WebApi en .NET, espero que les haya gustado y que les sea de ayuda.

 
Descargar Ejemplo Completo
 

Programando con MVC y WebApi en C# (Parte 3)


En el apartado anterior se ha escrito sobre como crear nuestro webApi con MVC, como realizar las vistas y las conexiones con nuestro JavaScript si no has leído el apartado anterior te recomiendo que lo hagas desde  aquí.

En este apartado, comenzaremos creando una carpeta dentro de Views llamada Student que contendrá todas nuestras vistas. Para este ejercicio crearemos las siguientes vista Index.chtml que nos permitirá construir nuestro visualización.

Ahora bien, crearemos el controller para ordenar la visualización de ese index, nuestro controller se llamará StudentController.cs dando click con el boton derecho en la carpeta Controller>Add>Controller que nos mostrará una pantalla como la siguiente:

En dicha pantalla seleccionaremos un controller vacío, luego daremos click en el botón Add, para finalmente ponerle nombre en el siguiente cuadro de dialogo que nos mostrará:

Dentro del controller escribiremos lo siguiente:

public ActionResult Index()
{
string SourceView = "Index";
SourceView = this.Request.Params["Source"] != null ? this.Request.Params["Source"] : SourceView;
return View(SourceView);
}

¿Por que hacemos esto?

La respuesta es simple, me he encontrado que cuando manejas profundidad de controllers algunos scripts no funcionan bien, por lo tanto para mi tranquilidad, envío la pagina por método GET para que cargue el controller deseado.

Después de esto podemos ver que ya nos muestra la información de nuestro index, ahora lo siguiente que haremos será crear los componentes html dentro de nuestra vista: para esto se utilizará html simple que viene dentro de nuestro template de bootstrap, una vez en nuestra carpeta de bootstrap daremos click sobre el forms.html que trae el contenido que nos interesa, muy similar al siguiente:

Lo que nos interesa realmente son las clases con las que construyeron estos elementos así que abriremos este mismo archivo en notepad y analizaremos los valores principales en este caso solo necesitamos los controles textbox y select, y la configuración del encabezado.

Encabezado:

<div class="row">
<div class="col-lg-12">
<h1 class="page-header">
Forms
</h1>
<ol class="breadcrumb">
<li>
<i class="fa fa-dashboard"></i> <a href="index.html">Dashboard</a>
</li>
<li class="active">
<i class="fa fa-edit"></i> Forms
</li>
</ol>
</div>
</div>

TextBox y Select:

<div class="row">
<div class="col-lg-6">
<form role="form">
<div class="form-group">
<label>Text Input with Placeholder</label>
<input class="form-control" placeholder="Enter text">
</div>
<div class="form-group">
<label>Selects</label>
<select class="form-control">
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
</select>
</div>
</div>
</div>

Vamos a agregar este código a nuestro Index,cshtml para poder visualizarlo:

Ahora lo modificaremos un poco para que quede de acorde a lo que necesitamos, que en este caso debería ser similar a nuestra clase Student y lo formateraremos un poco para que quede del siguiente modo:

@{
    ViewBag.Title = "Index";
}
<div class="row">
<div class="col-lg-12">
<h1 class="page-header">
            Agregar Estudiantes
        </h1>
<ol class="breadcrumb">
            <li>
                <i class="fa fa-dashboard"></i>  <a href="index.html">Dashboard</a>
            </li>
<li class="active">
                <i class="fa fa-edit"></i> Agregar Estudiantes
            </li>
        </ol>
    </div>
</div>
<div class="row">
<form role="form">
<div class="col-lg-6">
<div class="form-group">
                <label>Nombre</label>
                <input class="form-control" placeholder="Escriba su Nombre">
            </div>
<div class="form-group">
                <label>Apellido Materno</label>
                <input class="form-control" placeholder="Escriba el Apellido">
            </div>
<div class="form-group">
                <label>Seleccione el Semestre</label>
                <select class="form-control">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="8">8</option>
<option value="9">9</option>
<option value="10">10</option>
                </select>
            </div>
        </div>
<div class="col-lg-6">
<div class="form-group">
                <label>Apellido Paterno</label>
                <input class="form-control" placeholder="Escriba el Apellido">
            </div>
<div class="form-group">
                <label>CURP</label>
                <input class="form-control" placeholder="Escriba el CURP">
            </div>
        </div>
    </form>
</div>
<div class="form-group input-group">
    <button id="button" class="btn btn-default">Registrar</button>
</div>

Y se vería así:

Nice ¿No? pues vamos a conectar esto con un javascript que utilizaremos para conectar con nuestro WebApi que ya se encuentra generado en nuestro StudentController.cs que se encuentra en la carpeta service, dicho JavaScript es un pequeño archivo que nos permitirá accessar a los metodos GET,POST,PUT, etc, los cuales han sido creado en el controller, en mi propio modo de desarrollo creo las funciones a como las necesito, en este caso solo necesitamos 3, así que en la carpeta js crearemos una carpeta llamada DataList, en la cual crearemos un archivo Studen.js que contendrá la siguiente información:

function getStudents() {
    var lst = [];
    $.ajax({
        type: "GET",
        url: "service/Student/",
        contentType: 'application/json',
        dataType: 'json',
        async: false,
        success: function (responseData, textStatus, jqXHR) {
            if (responseData.Rows.length > 0) {
                lst = responseData.Rows;
            }
        },
        error: function (responseData, textStatus, errorThrown) {
        }
    });
    return lst;
}

function Add(request) {
    var response = {};
    $.ajax({
        type: "POST",
        url: 'service/Student/',
        contentType: 'application/json',
        dataType: 'json',
        async: false,
        data: JSON.stringify(request),
        success: function (responseData, textStatus, jqXHR) {
            if (responseData.success) {
                var additionalData = JSON.parse(responseData.AdditionalData);
                MessageModal("Aceptado", "Alumno Agregado.");
            }
            else {
                MessageModal("Información", responseData.Description);
                throw responseData.Description;
            }
        },
        error: function (responseData, textStatus, errorThrown) {
            MessageModal("Error", "Error interno");
            throw "Error interno";
        }
    });
    return response;
}

function Update(request) {
    var response = {};
    $.ajax({
        type: "PUT",
        url: 'service/Student/',
        contentType: 'application/json',
        dataType: 'json',
        async: false,
        data: JSON.stringify(request),
        success: function (responseData, textStatus, jqXHR) {
            if (responseData.success) {
                MessageModal("Aceptado", "Alumno Actualizado.");
            }
            else {
                MessageModal("Información", responseData.Description);
                throw responseData.Description;
            }
        },
        error: function (responseData, textStatus, errorThrown) {
            MessageModal("Error", "Error interno");
            throw "Error interno";
        }
    });
    return response;
}

Una vez  agregado este script lo incluiremos en nuestro Index.chtml dado que es un script creado para esa vista sera agregado mediante el comando @Url.Content del siguiente modo:


<script src="@Url.Content("~/js/DataList/Student.js")"></script>

Ahora bien iremos al final del archivo y abriremos las etiquetas correspondientes a la funcionalidad de la pagina con el siguiente código:


<script>
$("#button").on("click", function () {
var Name = $("#Name").val();
var Paternal = $("#Paternal").val();
var Maternal = $("#Maternal").val();
var CURP = $("#CURP").val();
var Semestre = $("#Semestre").val();

if (Name == undefined || Name == "") {
alert("El nombre es obligatorio");
}
else if (Paternal == undefined || Paternal == "") {
alert("El apellido paterno es obligatorio");
}
else if (Maternal == undefined || Maternal == "") {
alert("El apellido Materno es obligatorio");
}
else if (CURP == undefined || CURP == "") {
alert("El CURP es obligatorio");
}
else {
var request = {};
request.Name = Name;
request.Paternal = Paternal;
request.Maternal = Maternal;
request.CURP = CURP;
request.Semestre = Semestre;

response = Add(request);
}
});
</script>

Con el código ya enlazado podemos ver que el orden del desarrollo se vuelve rapido y mejoramos el entendimiento de nuestra aplicacción, con esto terminamos el apartado 3, en el apartado siguiente veremos como crear grids (Tablas) en tiempo real, y como hacerlos editables gracias a la siguientes herramientas mostradas aquí.

Programando con MVC y WebApi en C# (Parte 2)


En el apartado anterior se ha escrito sobre como vincular Bootstrap a nuestro proyecto de MVC, en este apartado veremos como se realizará la conexión entre el WebApi y nuestra vista web si no lo has leído puedes verlo aquí.

Comenzaremos creando los controllers para nuestro servicio de WebApi dentro de la carpeta Service.

Seleccionaremos Add > Controller y aparecerá una ventana como la que se muestra a continuación:

Despues de esto nos preguntará que nombre deseamos ponerle a nuestro controller, en nuestro caso crearemos StudentController, una vez creado nuestro controller daremos manos a la obra, creando los metodos GET, POST, PUT y DELETE.

Para esto debemos crear dos Entidades, una llamada Studen y otra llamada Result , Studen será la clase utilizada para poder enviar los datos de los estudiantes de nuestra vista a nuestro WebApi, y Result será una Clase informativa que nos dirá si las inserciones, las actualizaciones y borrados se han realizado de manera correcta o si ha habido algún tipo de error, en el caso de las inserciones nos devolverá el número identificador con el cual se ha agregado.

Clase Result:

namespace StudentsWeb.Models
{
    public class Result
    {
        public bool success { get; set; }
        public string Description { get; set; }
        public string AdditionalData { get; set; }
    }
}

Clase Student:

namespace StudentsWeb.Models
{
    public class Student
    {
        public long NoControl { get; set; }
        public string Name { get; set; }
        public string Paternal { get; set; }
        public string Maternal { get; set; }
        public string CURP { get; set; }
        public short Semestre { get; set; }
    }
}

Después de Crear las clases podemos dar vida a nuestro Controller “StudenController” como mencione antes en este controller vamos a manejar los métodos que necesitamos y para hacer un mock de manera rápida, utilizaremos la sesión.

Esto queda de la siguiente manera:

using System.Web;
using System.Web.Http;

namespace StudentsWeb.service
{
    public class StudentController : ApiController
    {
        public List<Student> Get()
        {
            var session = HttpContext.Current.Session;
            List<Student> ls;
            ls = (List<Student>)session["Sudent"];
            if (ls == null)
            {
                ls = new List<Student>();
            }
            return ls;
        }

        public Student Get(long NoControl)
        {
            var session = HttpContext.Current.Session;
            List<Student> ls;
            ls = (List<Student>)session["Sudent"];
            if (ls == null)
            {
                ls = new List<Student>();
            }
            Student s = new Student();
            s = ls.FirstOrDefault(S => S.NoControl == NoControl);
            return s;
        }

        [HttpPost]
        public Result post(Student s)
        {
            Result result = new Result();
            var session = HttpContext.Current.Session;
            List<Student> ls;
            ls = (List<Student>)session["Sudent"];
            if (ls == null)
            {
                ls = new List<Student>();
            }
            try
            {
                s.NoControl = ls.Count() + 1;
                ls.Add(s);
                session["Sudent"] = ls;
                result.success = true;
                long NoControl = s.NoControl;
                result.AdditionalData = JsonConvert.SerializeObject(NoControl);
            }
            catch (Exception ex)
            {
                result.success = false;
                result.Description = ex.Message;
            }
            return result;
        }

        [HttpPut]
        public Result put(Student s)
        {
            Result result = new Result();
            var session = HttpContext.Current.Session;
            List<Student> ls;
            ls = (List<Student>)session["Sudent"];
            if (ls == null)
            {
                ls = new List<Student>();
            }
            try
            {
                ls.FirstOrDefault(S => S.NoControl == s.NoControl).CURP = s.CURP;
                ls.FirstOrDefault(S => S.NoControl == s.NoControl).Maternal = s.Maternal;
                ls.FirstOrDefault(S => S.NoControl == s.NoControl).Name = s.Name;
                ls.FirstOrDefault(S => S.NoControl == s.NoControl).Paternal = s.Paternal;
                ls.FirstOrDefault(S => S.NoControl == s.NoControl).Semestre = s.Semestre;
                session["Sudent"] = ls;
                result.success = true;
            }
            catch (Exception ex)
            {
                result.success = false;
                result.Description = ex.Message;
            }
            return result;
        }

        [HttpDelete]
        public Result delete(long NoControl)
        {
            Result result = new Result();
            var session = HttpContext.Current.Session;
            List<Student> ls;
            ls = (List<Student>)session["Sudent"];
            if (ls == null)
            {
                ls = new List<Student>();
            }
            try
            {
                ls = ls.Where(S => S.NoControl != NoControl).ToList();
                session["Sudent"] = ls;
            }
            catch (Exception ex)
            {
                result.success = false;
                result.Description = ex.Message;
            }
            return result;
        }
    }
}

Ya tenemos listo el Controller, ahora nos faltan las vistas y el JavaScript pero estoy seguro que ya saben por donde va la idea.

Para que las sesiones puedan ser utilizadas dentro de los controllers de WebApi es necesario modificar los siguientes archivos:

WebApiConfig.cs

            var appXmlType = config.Formatters.XmlFormatter.SupportedMediaTypes.FirstOrDefault(t => t.MediaType == "application/xml");
            config.Formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType);

En el código anterior, le estamos diciendo al método Register que solo permita el retorno de JSON

Global.asax

        protected void Application_PostAuthorizeRequest()
        {
            HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required);
        }

En el código anterior el método está obliga a usar las sesiones al inicio de la aplicación.

Por hoy lo dejaremos hasta aquí, la próxima entrega crearemos las vistas y conectaremos todo

 

Programando con MVC y WebApi en C# (Parte 1)


MVC (Modelo Vista Controlador) es un patrón de diseño que separa los datos de y la lógica de negocio de una Aplicación cualquiera que sea su contexto, para esto el lenguaje de .NET Ya tiene una solución que se encuentra en ASP.NET Web Application (.NET Application)

Después de seleccionar el nombre de nuestra solución nos aparecerá la siguiente ventana:

Una vez seleccionado el Web API y cambiado el modo de autenticación podemos proceder a ver nuestra solución creada, como la siguiente:

En lo personal el modo en que Microsoft Arreglo el modelo no es de mi completo agrado comenzaremos explicado los contenidos de nuestras carpetas:

Content: en la carpeta content se encuentran los contenidos de plantillas de Estilos y las imagenes
Controllers: es la carpeta que contiene los contenidos de las llamadas a servidor, en mi caso solo es utilizado para la seguridad Login, Registro y Recuperación de Usuarios
fonts: Las fuentes que serán usadas en nuestro template
Models: Las clases para serializar y hablar entre los controllers
Scripts: Los JavaScripts que serán utilizados
View: Finalmente las Vistas que serán utilizadas para presentar nuestra pagina

Como ta habia escrito anteriormente no soy partidario de los nombres que microsoft le ha puesto a las carpetas por lo tanto procederé a borrar las siguientes carpetas y a crear unas nuevas

Carpetas a borrar:

  • Content
  • Scripts

Carpetas a crear:

  • css
  • js
  • img
  • service

Una vez realizado esto nuestro proyecto quedará así:

A partir de aquí buscaremos una plantilla Bootstrap, como muchos de los lectores de este blog saben no soy muy afecto a crear diseños web, podemos descargarlo de la siguiente ruta https://startbootstrap.com/template-overviews/sb-admin/ (Es de mis favoritos), ahora veremos como integrar nuestro diseño de bootstrap a nuestro desbalijado proyecto en .NET, analicemos las carpetas que tiene nuestra plantilla:

Bien, podemos encontrar dentro de las carpetas que nos interesan css, fonts-awesome, fonts, js, estas carpetas deben ser copiadas a nuestro proyecto de MVC lo haremos de la siguiente manera:

  1. Todo el contenido de la carpeta css será copiado a la carpeta css de nuestra solucion.
  2. Todo el contenido de la carpeta js será copiado a la carpeta js de nuestra solución.
  3. En cuanto a la carpeta fonts-awesome encontraremos las siguientes carpetas css, less, scss y fonts.
    1. Tomaremos el contenido de css que esta dentro de la carpeta fonts y será copiado a nuestra carpeta css.
    2. Tomaremos el contenido de la carpeta fonts y lo pasaremos a nuestra carpeta fonts
  4.  Regresaremos a visual studio.

En visual studio ya una vez copiados todos nuestros archivos es necesario dar click al icono Show All Files, para permitirnos ver todos los archivos e incluirlos en el código, si no hiciéramos esta operación, al momento de compilar no nos agregará los archivos en el modo de publicación. Para incluir a los archivos podemos dar click derecho en el archivo o los archivos que queremos incluir, y buscamos en el menú la sección incluir en proyecto:

Y así lo haremos con todos los archivos que hayamos copiado a nuestra solución.

Con esto tenemos nuestros archivos para usar listos, ahora hay que introducirnos en la carpeta App_Star esta carpeta contiene los siguientes Archivos:

  • BundleConfig.cs: Sirve para agrupar varios archivos JavaScript y en un mismo archivo. permitiendo un mejor performance.
  • FilterConfig.cs: Nos permite crear filtros de acción que son atributos que pueden asociarse a una acción de un controlador o al controlador en su totalidad.
  • RouteConfig.cs: Es la clase que se encarga de la configuración de las rutas de los controllers por defecto.
  • WebApiConfig.cs:  Esta clase es la encargada de configuración de las rutas de cualquier servicio expuesto en el webApi.

Ahora que sabemos esto vamos a reconstruir el BundleConfig, recordemos que hemos borrado todos los recursos css y javascript con los que venia nuestra solución por defecto, si abrimos el archivo nos mostrará algo como lo siguiente:

La información que nos interesa para el Registro de Bundles es ScriptBundle y StyleBundle que son las que tenemos que registrar para tener una idea de lo que tenemos que hacer, debemos abrir nuestro archivo index de nuestra plantilla de bootstrap que contiene algo como lo siguiente:

Así pues escribiremos nuestros Bundles de css de la siguiente manera:

Y del mismo modo cambiaremos crearemos nuestros Bundles de JavaScript que quedan del siguiente modo:

Esto nos permitirá obtener los js y css de una manera simple en .NET. ahora bien, vamos a incorporar la plantilla directamente a nuestras vistas, para esto nos dirigiremos a la carpeta View y dentro de la misma la carpeta Shared y dentro de ella un _Layout.cshtml en el cual encontraremos la vista principal de nuestro html el cual muestra algo como lo siguiente:

Bien borraremos algunas cosas para que queden de la siguiente manera:

Esta es la versión menor del archivo y es la manera en como yo lo ordeno cuando voy a pasar una plantilla nueva ahora abriremos nuevamente nuestro archivo index para ir copiando de poco en poco, primero copiaremos la información del head, desde el inicio del encabezado hasta la sección del title, y poniendo la información de nuestro bundle recién, creado quedando el código como el siguiente:

Después de esta parte la cosa se complica un poco, pero créanme que vale la pena verlo terminado, nos dirigiremos de nueva cuenta a nuestra plantilla de bootstrap y buscaremos la etiqueta div siguiente

, comenzaremos a copiar de ese div hasta el div

y esta información será copiada entre y la función @RenderBody() de nuestro archivo _Layout.cshtml  y después de @RenderBody() cerraremos tres etiquetas quedando como en la siguiente imagen:

En este caso la barra de Navegación se encuentra colapsada para no tener que poner una imagen tan grande, espero se entienda. A hora no dedicaremos a la sección que no hemos copiado, pero esta se encuentra en otro archivo, que esta en la carpeta raíz, View>Home el archivo se llama index.cshtml, primero borraremos todo respetando las siguientes lineas

@{
ViewBag.Title = “Home Page”;
}

después de esas lineas copiaremos toda la sección que no copiamos que se encontraba dentro de la etiqueta <div class=”container-fluid”> quedando el archivo de la siguiente manera:

La imagen no nos muestra todo para meter una imagen más pequeña pero para el caso es lo mismo.

Despues de haber hecho todas estas modificaciones tenemos un Layout en nuestra solución lista para trabajar corremos la aplicación y veremos que queda como la siguiente:

Por hoy terminamos con este apartado, también explicado en el libro que estoy haciendo que sera puesto a la venta en Amazon en un par de meses.

Descargar la Solucion

Entity Framework y los POCO’s


La verdad, no es que me haga muy feliz el sistema de los entitys a decir verdad no es algo que me guste, lo he aprendido por que me dio mucha curiosidad, el modelo de negocio que querían utilizar en mi antiguo trabajo, hoy haremos un modelo de Base de Datos de Seguridad, que se encargará de permitir roles y operaciones a los usuarios, ya sé, ya sé, me van a decir que se puede usar membership provider, pero dado a mi poca imaginación para iniciar proyectos de ejemplo, haremos eso esta vez, comencemos con los pasos en nuestro obscuro juego de los entities:

Pasos:

Paso 1:

Abrir el Visual Studio 2012 => New Project => Class Library

Paso 2:

Creando nuevo archivo

Seleccionar ADO. NET Entity Data Model

Ahora bien nuestro cambiaremos el nombre de nuestro archivo a Seguridad.edmx

Base de Datos

En este caso yo ya tengo mi diseño de la base así que crearé los entities desde mi base de datos que es la siguiente.

Seleccionamos Generate From Data Base

Seleccionamos New Conection y nos abrirá el cuadro siguiente.

Llenamos los datos de la Conexión de SQL Server

Seleccionamos las tablas que utilizaremos

Nuestro EDMX queda como el siguiente

 

Extendiendo la Clase TextBox para agregar Funcionalidades.


Buena tarde, el día de hoy nos divertiremos extendiendo la clase TextBox, les platicaré un poco del contexto del por que sale esta publicacion, para comenzar debido a los problemas que ha habido con el AddIn actual que hay en la compañia, me ha dado vueltas por la cabesa usar los ContentControls de Word para que realize las mismas funciones que las SmartTags y así evitar los errores de las SmartTags, pero a su vez tengo que ver la manera de mezclar los controles entre sí… ahora bien, para solucionar el problema me hacerque a la POO y decidi extenter varias clases de los controles que Microsoft Visual Studio trae por defecto, y la verdad es que haciendome la vida más facil y vivida, le hecharemos un ojo al código

    public class DGTextBox : TextBox
    {
        private string _PlaceHolder;
        private bool _EsNumerico { get; set; }
        private bool _EsExpediente { get; set; }
        private bool _EsLetras { get; set; }
        private bool _EsCorreo { get; set; }

        public int CampoId { get; set; }
        public int CatTipoAsuntoId { get; set; }
        public int CatTipoOrganismoId { get; set; }
        public string Descripcion { get; set; }
        public string Ayuda { get; set; }
        public string DGID { get; set; }

        public bool EsNumerico {
            get { return _EsNumerico; }
            set
            {
                _EsNumerico = value;
                if (value == true)
                {
                    _EsExpediente = false; _EsLetras = false;
                    _EsCorreo = false;
                }
            }
        }
        public bool EsExpediente {
            get { return _EsExpediente; }
            set {
                _EsExpediente = value;
                if (value == true) {
                    _EsLetras = false; _EsNumerico = false;
                    _EsCorreo = false;
                }
            }
        }
        public bool EsLetras {
            get { return _EsLetras; }
            set
            {
                _EsLetras = value;
                if (value == true)
                {
                    _EsExpediente = false; _EsNumerico = false;
                    _EsCorreo = false;
                }
            }
        }
        public bool EsCorreo {
            get { return _EsCorreo; }
            set
            {
                _EsCorreo = value;
                if (value == true)
                {
                    _EsExpediente = false; _EsLetras = false;
                    _EsNumerico = false;
                }
            }
        }
        public string PlaceHolder
        {
            get { return _PlaceHolder; }
            set
            {
                _PlaceHolder = value;
                if (value != null)
                {
                    Text = value;
                }
            }
        }

        protected override void OnClick(EventArgs e)
        {
            if (this.Text == this.PlaceHolder)
            {
                this.Text = "";
            }
            base.OnClick(e);
        }

        protected override void OnLostFocus(EventArgs e)
        {
            if (string.IsNullOrEmpty(Text))
            {
                PlaceHolder = _PlaceHolder;
            }
            base.OnLostFocus(e);
        }

        protected override void OnKeyDown(KeyEventArgs e)
        {
            switch (e.KeyData)
            {
                case Keys.F1:
                    MessageBox.Show(Ayuda
                        , "Ayuda de " + _PlaceHolder
                        , MessageBoxButtons.OK
                        , MessageBoxIcon.Question, MessageBoxDefaultButton.Button1
                        , MessageBoxOptions.ServiceNotification);
                    break;
            }
        }

        protected override void OnKeyPress(KeyPressEventArgs e)
        {
            if (_EsNumerico)
            {
                e.Handled = PermiteNumeros(e);
            }
            else if (_EsExpediente)
            {
                e.Handled = PermiteNumeroExpediente( e);
            }
            else if (_EsLetras)
            {
                e.Handled = PermiteLetras( e);
            }
            else if (_EsCorreo)
            {
                e.Handled = PermiteCorreo( e);
            }
        }

        private bool PermiteNumeros( KeyPressEventArgs e)
        {
            bool resp;
            if (Char.IsDigit(e.KeyChar))
            {
                resp = false;
            }
            else if (Char.IsControl(e.KeyChar))
            {
                resp = false;
            }
            else if (Char.IsSeparator(e.KeyChar))
            {
                resp = false;
            }
            else if (e.KeyChar == 8)
            {
                resp = false;
            }
            else
            {
                resp = true;
            }
            return resp;
        }

        private bool PermiteNumeroExpediente( KeyPressEventArgs e)
        {
            bool resp;
            if (Char.IsDigit(e.KeyChar))
            {
                resp = false;
            }
            else if (e.KeyChar == 47)
            {
                resp = false;
            }
            else if (e.KeyChar == 8)
            {
                resp = false;
            }
            else
            {
                resp = true;
            }
            return resp;
        }

        private bool PermiteLetras(KeyPressEventArgs e)
        {
            bool resp;
            if (Char.IsLetter(e.KeyChar))
            {
                resp = false;
            }
            if (Char.IsWhiteSpace(e.KeyChar))
            {
                resp = false;
            }
            else if (e.KeyChar == 8)
            {
                resp = false;
            }
            else
            {
                resp = true;
            }
            return resp;
        }

        private bool PermiteCorreo(KeyPressEventArgs e)
        {
            bool resp;
            if (Char.IsLetterOrDigit(e.KeyChar))
            {
                resp = false;
            }
            else if (e.KeyChar == 64)
            {
                resp = false;
            }
            else if (e.KeyChar == 46)
            {
                resp = false;
            }
            else if (e.KeyChar == 8)
            {
                resp = false;
            }
            else
            {
                resp = true;
            }
            return resp;
        }

        public bool IsEmpty()
        {
            bool resp;

            if (!string.IsNullOrEmpty(Text))
            {
                if (_PlaceHolder == Text)
                {
                    resp = true;
                }
                else
                {
                    resp = false;
                }
            }
            else
            {
                resp = true;
            }

            return resp;
        }

        public void ClearPlaceHolder() {
            PlaceHolder = _PlaceHolder;
        }

        public void MarkErrorEmpty()
        {
            if (!string.IsNullOrEmpty(Text))
            {
                if (_PlaceHolder == Text)
                {
                    this.BackColor = Color.DarkSalmon;
                }
                else
                {
                    this.BackColor = Color.White;
                }
            }
            else
            {
                this.BackColor = Color.DarkSalmon;
            }
        }
    }

Las propiedades son cualquieras para mi comodidad, y para hacer el reensamblaje dentro de lo que seria el ContenControl una de las ventajas es que veremos cosas nuevas en el escritorio de diseño, lo cual nos permitirá incluso arrastrar y pegar nuestros propios controles,

        public bool EsNumerico {
            get { return _EsNumerico; }
            set
            {
                _EsNumerico = value;
                if (value == true)
                {
                    _EsExpediente = false; _EsLetras = false;
                    _EsCorreo = false;
                }
            }
        }

La propiedad EsNumerico, es tambien una propiedad como tal, teniendo en cuenta que tiene propiedades que son privadas dentro del control, este control se encarga de cambiar todas las otras propiedades que tienen que ver con las validaciones directas, así como tambien las propiedades EsExpediente,EsLetras,EsCorreo.

public string PlaceHolder
        {
            get { return _PlaceHolder; }
            set
            {
                _PlaceHolder = value;
                if (value != null)
                {
                    Text = value;
                }
            }
        }

La propiedad PlaceHolder se encarga de controlar la visualización del textbox, y a sú vez cambia esa información en la propiedad Text que forma parte de la Clase padre TextBox.

        protected override void OnClick(EventArgs e)
        {
            if (this.Text == this.PlaceHolder)
            {
                this.Text = "";
            }
            base.OnClick(e);
        }

Comenzamos a sobreescribir metodos en este caso al onClick le ponemos un evento muy muy bonito, el cual con un click en el texto se borra nuestra información dentro de nuestro Text y se ejecutará siempre que la informacion sea igual en Text y PlaceHolder.

        protected override void OnKeyDown(KeyEventArgs e)
        {
            switch (e.KeyData)
            {
                case Keys.F1:
                    MessageBox.Show(Ayuda
                        , "Ayuda de " + _PlaceHolder
                        , MessageBoxButtons.OK
                        , MessageBoxIcon.Question, MessageBoxDefaultButton.Button1
                        , MessageBoxOptions.ServiceNotification);
                    break;
            }
            //base.OnKeyDown(e);
        }

Aquí como podemos ver estamos haciendo una función específica dentro de lo que sería la tecla de F1 permitiendo así que se muestre la ayuda de nuestro control, con la sobreescritura de OnKeyDown, 10 llamadas menos…

       protected override void OnKeyPress(KeyPressEventArgs e)
        {
            if (_EsNumerico)
            {
                e.Handled = PermiteNumeros(e);
            }
            else if (_EsExpediente)
            {
                e.Handled = PermiteNumeroExpediente( e);
            }
            else if (_EsLetras)
            {
                e.Handled = PermiteLetras( e);
            }
            else if (_EsCorreo)
            {
                e.Handled = PermiteCorreo( e);
            }
        }

Ahora bien, en este metodo prepararemos los retornos de los handles en el caso de la preción de las teclas, dependiendo que le hayamos dicho al control sobre la validación que queremos el control la hará por sí misma mostraré el caso del correo gracias a la sobreeescritura de OnKeyPress

private bool PermiteCorreo(KeyPressEventArgs e)
        {
            bool resp;
            if (Char.IsLetterOrDigit(e.KeyChar))
            {
                resp = false;
            }
            else if (e.KeyChar == 64)
            {
                resp = false;
            }
            else if (e.KeyChar == 46)
            {
                resp = false;
            }
            else if (e.KeyChar == 8)
            {
                resp = false;
            }
            else
            {
                resp = true;
            }
            return resp;
        }

Como podemos ver aqui la funcion PermiteCorreo() solo se trata de checar que teclas se están precionando y ponemos la validación consecuente… quizá le ponga mejor una expreción regular…

public bool IsEmpty()
        {
            bool resp;
            if (!string.IsNullOrEmpty(Text))
            {
                if (_PlaceHolder == Text)
                {
                    resp = true;
                }
                else
                {
                    resp = false;
                }
            }
            else
            {
                resp = true;
            }

            return resp;
        }

En este caso IsEmpty() nos dirá si el control esta vacio, permitiendonos así meter ese metodo dentro de un if.

public void ClearPlaceHolder() {
PlaceHolder = _PlaceHolder;
}

es como el metodo ClearPlaceHolder, nos permite limpiar el TextBox y le pone de nueva cuenta el PlaceHolder, permitiendo que volvamos a ver la información del nombre del campo.

public void MarkErrorEmpty()
        {
            if (!string.IsNullOrEmpty(Text))
            {
                if (_PlaceHolder == Text)
                {
                    this.BackColor = Color.DarkSalmon;
                }
                else
                {
                    this.BackColor = Color.White;
                }
            }
            else
            {
                this.BackColor = Color.DarkSalmon;
            }
        }

Si el control efecitivamente esta vacio, tomando en cuenta que el PlaceHolder y Text son iguales tambien, el control siempre estará vacío, MarkErrorEmpty() en esta premisa, el control se pinta de color salmón, permitiendo así su pronta identificación.

y con esto hemos finalizado nuestro primer control propio, disfrutenlo, y si tienen dudas con todo gusto atenderé sus preguntas.

Consumiendo WCF Rest con Windows Form


Perdonad, por la mala escritura, pero creo que he bebido de más en fín hoy finalze la investigación de Windows Fom Conectandose a WCF mediante un Servicio REST como era de esperarse las cosas casi se hacen solitas, aún que sigo pensando que JavaScript es más mono! en fín vamos a divertirnos en laentrega anterior hemos creado un WCF que Diseñado mediante REST por ahí anda el ejemplo ahorita no me encuentro en condiciones de ponerlo más sin embargo, para poder utilizarlo he creado un Helper diría el purista, tú y los Helpers! me vale este es mí Blog y me mofo de quien quiero sí de por sí los hago en su cara jajajaja bien veamos el código:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;

namespace EjemploConsumoRest.Helpers
{
    public static class RESTHeper
    {
        public static string Ejecutar(string Url, string Datos, string Metodo)
        {
            byte[] data = UTF8Encoding.UTF8.GetBytes(Datos);

            HttpWebRequest request;
            request = WebRequest.Create("http://localhost/Estudiantes/Listar") as HttpWebRequest;
            request.Timeout = 10 * 1000;
            request.Method = Metodo;
            request.ContentLength = data.Length;
            request.ContentType = "application/json; charset=utf-8";
            Stream postStream = request.GetRequestStream();
            postStream.Write(data, 0, data.Length);

            HttpWebResponse response = request.GetResponse() as HttpWebResponse;
            StreamReader reader = new StreamReader(response.GetResponseStream());
            string json = reader.ReadToEnd();

            return ClearJSON(json);
        }

        private static string ClearJSON(string Json) {
            if (Json.IndexOf("{\"MuestraEstudiantesResult\":") > -1)
            {
                Json = Json.Replace("{\"MuestraEstudiantesResult\":", "");
                Json = Json.Remove(Json.Length - 1);
            }
            return Json;
        }
    }
}

Ahora bien se preguntarán por que el Metodo ClearJSON bien, como recordarán en alguna ocación hicimos la clase JSONHelper que nos permite contraer los datos de manera simple a una clase ya establecida de.Net bien está es la parte bonita de los anteriores desarrollos, esa misma clase nos sirve para devolver el JSON a una Clase que comprenda .Net podemos ver el Enlace Aquí, el problema es que .Net es muy purista, y me regresa información que no necesito por eso está el metodo ClearJSON toda la basura de más es posible limpiarla siguiendo el contexto eslse if de cualquier manera vuelvo a dejar el código:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Script.Serialization;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
namespace JSONHelper
{
public static class JsonSerializer
{
///
/// Método extensor para serializar un string a JSON
///
public static string SerializaToJson(this object objeto)
{
string jsonResult = string.Empty;
try
{
DataContractJsonSerializer jsonSerializer = new DataContractJsonSerializer(objeto.GetType());
MemoryStream ms = new MemoryStream();
jsonSerializer.WriteObject(ms, objeto);
jsonResult = Encoding.Default.GetString(ms.ToArray());
}
catch { throw; }
return jsonResult;
}
///
;
/// Método extensor para deserializar JSON cualquier objeto
///
;
public static T DeserializarJsonTo(this string jsonSerializado)
{
try
{
T obj = Activator.CreateInstance();
MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(jsonSerializado));
DataContractJsonSerializer serializer = new DataContractJsonSerializer(obj.GetType());
obj = (T)serializer.ReadObject(ms);
ms.Close();
ms.Dispose();
return obj;
}
catch { return default(T); }
}
}
}

y la clase estudiante como ya la conocen los que analizarón el anterior post que está Aquí Es la siguiente:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace EjemploConsumoRest
{
    public class Estudiante
    {
        public string Nombre { get; set; }
        public string Paterno { get; set; }
        public string Materno { get; set; }
    }
}

meteremos un DataGridView y lo enlazaremos de la siguiente manera en el From:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using RESTHeper;
using JSONHelper;

namespace EjemploConsumoRest
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            string url = "http://localhost:45597/Estudiantes.svc/Listar/";
            string Datos = "";
            string resp = RESTHeper.RESTHeper.Ejecutar(url, Datos, "POST");
            List lstEstudiante = resp.DeserializarJsonTo();
            dgvEstudiantes.DataSource = lstEstudiante;
        }
    }
}

como pueden vereso nos obligará a hacer el cambio en el metodo de IEstudiantes en el wsREST del ejemplo anterior por el siguiente:

        [OperationContract]
        [WebInvoke(Method= "POST", RequestFormat=WebMessageFormat.Json, ResponseFormat=WebMessageFormat.Json, UriTemplate = "/Listar/", BodyStyle=WebMessageBodyStyle.Wrapped)]
        List MuestraEstudiantes();

Les dejo como siempreo casisiempre elcódigo de descarga y apuro este whisky para irme a dormir :P,por cierto para los que tienen problemas con que la URL no es encontrada seria bueno que checaran el puerto.

EjemploConsumoRest (Download)

ServiciosWeb con REST y WCF


Para información muy bien documentada de REST podemos pasarnos por este blog http://www.dnmplus.net/articulos/acercandonos-a-rest-sigilosamente-(y-ii).aspx

Para comenzar abriremos un nuevo proyecto en VisualStudio 2010 y daremos clic a New Proyect nos iremos al menú que dice Installed Templates y seleccionaremos WCF Service Aplication, una vez creado el proyecto modificaremos los nombres de Service1.svc y IService1.cs po Estudiantes.svc y IEstudiantes.cs respectivamente y crearemos agregaremos al proyecto una clase nueva llamada Estudiante.cs que solo contendrá la clase para utilizar.

Código de Estudiantes.svc

<%@ <span class="hiddenSpellError">ServiceHost</span>  Language="C#"  Debug="true"  Service="wsREST.Estudiantes"  CodeBehind="Estudiantes.svc.cs"  Factory = "System.ServiceModel.Activation.WebServiceHostFactory" %>

Estudiantes.svc lo pueden editar dando clic en el botón derecho y seleccionado la opción View Markup y de toda la linea la más importante es Factory = “System.ServiceModel.Activation.WebServiceHostFactory” que de hecho es la que deben agregar.

Código de Estudiantes.svc.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace wsREST
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Service1" in code, svc and config file together.
public class Estudiantes : IEstudiantes
    {
public List MuestraEstudiantes()
        {
List respuesta = new List();
respuesta.Add(new Estudiante { Nombre = "Francisco", Paterno = "Castán", Materno = "Gutiérrez" });
return respuesta;
        }
    }
}

Código de IEstudiantes.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace wsREST
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IService1" in both code and config file together.
[ServiceContract]
public interface IEstudiantes
    {

[OperationContract]
[WebGet(RequestFormat=WebMessageFormat.Json, ResponseFormat=WebMessageFormat.Json, UriTemplate = "/Listar/", BodyStyle=WebMessageBodyStyle.Wrapped)]
List MuestraEstudiantes();
    }
}

Aquí con la siguiente linea le decimos al compilador que la información de respuesta es en JSON ResponseFormat=WebMessageFormat.Json, por defecto la regresará en XML.

Código de Estudiante.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Runtime.Serialization;

namespace wsREST
{
[DataContract]
public class Estudiante
    {
[DataMember]
public string Nombre { get; set; }
[DataMember]
public string Paterno { get; set; }
[DataMember]
public string Materno { get; set; }
    }
}

Los DataContractn y los DataMember nos permiten hacer serializable la clase, con esto el control para dispositivos móviles ya facilmente escalable a cualquier aplicación que tengamos…

Ejemplo (Download)

A %d blogueros les gusta esto: