Archivo del sitio

Creando un Motor para guardar MetaTags


Actualmente tratando de darle un valor agregado a un campo texto, recordé el dolor de cabeza que es meter los tags en WordPress, pero claro son criterios de búsqueda y de indices en las Web, así que no lo recrimino, me quejo, pero no lo recrimino, por otra parte esta el TextIndex, que es un show, desde la instalación hasta su indexación, pasando por la parte que si lo usas con archivos extremadamente pesados, necesitas tener un Hardware potente para utilizarlo, por lo tanto lo descarte, pero que tal si combinase ambos.

Y habiendo dado el preludio de mi cabeza dando vueltas por alguna solución en este articulo veremos como generar MetaTags de forma simple y rapida, para un campo tipo texto, del tamaño que queramos.

Comenzaremos mostrando el diagrama de Base de Datos:

Como podemos ver La idea es simple MasterData es nuestra Tabla donde tendremos nuestro texto tan largo como VARCHAR(MAX), Tag es donde guardaremos las palabras clave e importantes relacionadas al texto y MasterDataTag es donde guardaremos las relaciones de Ambas tablas.

Por velocidad crearemos un motor en consola, a ustedes pueden agregarla a un servicio, o otro tipo de proceso. Esta parte ya se la saben abriremos un proyecto nuevo de VisualStudio de tipo Consola como se ve en la siguiente imagen:

Una vez realizado esto, dentro del proyeco crearemos los items con el siguiente orden:

  • Domain (Carpeta)
    • MasterData.cs
    • MasterDataTag.cs
    • Tag.cs
  • DB.cs
  • Program.cs (Editar)

En este orden comenzaremos a presentar los archivos y a Explicarlos:

MasterData.cs

Esta clase es una copia directa del modelo de Base de Datos, el código queda como el siguiente:


namespace MetaTagsTest.Domain
{
public class MasterData
{
public long CveMasterData { get; set; }
public string DataInfo { get; set; }
}
}

Tag.cs

Es el código relacionado con los Tags que se crearán dentro del modelo de MetaTags


namespace MetaTagsTest.Domain
{
public class Tag
{
public int CveTag { get; set; }
public string Name { get; set; }
}
}

 

MasterDataTag.cs

Es la clase con el código ejemplificado para la relación de Tags con MasterData


namespace MetaTagsTest.Domain
{
public class MasterDataTag
{
public long CveMasterData { get; set; }
public int CveTag { get; set; }
}
}

En esta ocación DB contendrá los repositorios que se encargarán de efectuar la interacción con la Base de Datos:


using MetaTagsTest.Domain;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;

namespace MetaTagsTest
{
public static class DB
{
#region Tags
public static List<Tag> GetAllTags()
{
List<Tag> tags = new List<Tag>();
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["Context"].ToString()))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = @"SELECT * FROM Tag";
using (IDataReader r = cmd.ExecuteReader())
{
while (r.Read())
{
Tag t = new Tag();
t.CveTag = Convert.ToInt32(r["CveTag"]);
t.Name = Convert.ToString(r["Name"]);
tags.Add(t);
}
}
}
conn.Close();
}
return tags;
}

public static void AddTag(Tag item)
{
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["Context"].ToString()))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = @"INSERT INTO Tag ([Name]) VALUES (@Name);
SELECT SCOPE_IDENTITY()";
cmd.Parameters.AddWithValue("@Name", item.Name);
item.CveTag = Convert.ToInt32(cmd.ExecuteScalar());
}
conn.Close();
}
}

#endregion

#region MasterDataTag
public static void DeleteMasterDataTag(long CveMasterData)
{
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["Context"].ToString()))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = @"DELETE FROM MasterDataTag WHERE CveMasterData=@CveMasterData";
cmd.Parameters.AddWithValue("@CveMasterData", CveMasterData);
cmd.ExecuteNonQuery();
}
conn.Close();
}
}

public static void AddMasterDataTag(MasterDataTag item)
{
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["Context"].ToString()))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = @"INSERT INTO MasterDataTag (CveMasterData,CveTag)
VALUES (@CveMasterData,@CveTag)";
cmd.Parameters.AddWithValue("@CveMasterData", item.CveMasterData);
cmd.Parameters.AddWithValue("@CveTag", item.CveTag);
cmd.ExecuteNonQuery();
}
conn.Close();
}
}

#endregion

#region MasterData
public static List<MasterData> GetMasterDataByTag(string CveTags)
{
List<MasterData> datas = new List<MasterData>();
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["Context"].ToString()))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = @"SELECT DISTINCT md.* FROM MasterData md
JOIN MasterDataTag mdt ON mdt.CveMasterData=md.CveMasterData
WHERE mdt.CveTag IN (" + CveTags + ")";
using (IDataReader r = cmd.ExecuteReader())
{
while (r.Read())
{
MasterData md = new MasterData();
md.CveMasterData = Convert.ToInt64(r["CveMasterData"]);
md.DataInfo = Convert.ToString(r["DataInfo"]);
datas.Add(md);
}
}
}
conn.Close();
}
return datas;
}

public static void AddMasterData(MasterData item)
{
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["Context"].ToString()))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = @"INSERT INTO MasterData (DataInfo) VALUES (@DataInfo);
SELECT SCOPE_IDENTITY()";
cmd.Parameters.AddWithValue("@DataInfo", item.DataInfo);
item.CveMasterData = Convert.ToInt64(cmd.ExecuteScalar());
}
conn.Close();
}
}

public static void UpdateMasterData(MasterData item)
{

using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["Context"].ToString()))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = @"UPDATE MasterData
SET DataInfo= @DataInfo
WHERE CveMasterData=@CveMasterData";
cmd.Parameters.AddWithValue("@CveMasterData", item.CveMasterData);
cmd.Parameters.AddWithValue("@DataInfo", item.DataInfo);
cmd.ExecuteNonQuery();
}
conn.Close();
}
}
#endregion
}
}

La clase anterior contiene todos elementos para agregar y actualizar metatags, basada en la inserción de la información principal, pero esto lo veremos en el siguiente código principal.

program.cs

En este  código puedes encontrar todo el negocio que se encarga de procesar y hacer búsquedas.


using MetaTagsTest.Domain;
using System;
using System.Collections.Generic;
using System.Linq;

namespace MetaTagsTest
{
class Program
{
public static List<string> notags = new List<string>()
{
" el "," la "," los "," las "," un "," una "," unos "," unas "," yo "," nosotros "," nosotras ",
" tú "," tu "," vosotros ", " vosotras "," él "," el "," ellos "," ellas ", ",", ";", " de "," antes "," no ",
" si "," sí "," esto ", " eso ", " aquello ", " aquel ", " como ", " para ", " a ", " ante ", " bajo ", " cabe ",
"desde"," por ", " segun ", " sin ", " sobre "
};
static void Main(string[] args)
{
bool salir = true;
while (salir)
{
List<Tag> dbtags = DB.GetAllTags();
Console.WriteLine("Seleccione una opción");
Console.WriteLine("1 .- Agregar una frase");
Console.WriteLine("2 .- Buscar una frase por palabras");
Console.WriteLine("3 .- Salir");
ConsoleKeyInfo opcion = Console.ReadKey();
Console.WriteLine();
switch (opcion.KeyChar)
{
case '1':
Console.WriteLine("Escriba una Frase");
string frase = Console.ReadLine();
MasterData md = new MasterData();
md.DataInfo = frase;
DB.AddMasterData(md);
foreach (string notag in notags)
{
frase = frase.Replace(notag, "");
}

string[] tags = frase.Split(' ');

foreach (string tag in tags)
{
Tag t = new Tag();
var dbtag = dbtags.FirstOrDefault(T => T.Name.ToUpper() == tag.ToUpper().Trim());
if (dbtag == null)
{

t.Name = tag.ToUpper().Trim();
DB.AddTag(t);
dbtags.Add(t);
}
else
{
t.CveTag = dbtag.CveTag;
t.Name = dbtag.Name.ToUpper();
}
MasterDataTag mdt = new MasterDataTag();
mdt.CveMasterData = md.CveMasterData;
mdt.CveTag = t.CveTag;
DB.AddMasterDataTag(mdt);
}
break;
case '2':
Console.WriteLine("Escriba una lista de palabras separadas por comas Ejem: (Hola,Bienvenido)");
string line = Console.ReadLine();
Console.WriteLine();
string[] palabras = line.Split(',');
dbtags = DB.GetAllTags();
List<string> Tags = new List<string>();
foreach (string p in palabras)
{
Tag t = dbtags.FirstOrDefault(T => T.Name.ToUpper() == p.ToUpper().Trim());
if (t != null)
{
Tags.Add(Convert.ToString(t.CveTag));
}
}
string CveTags = string.Join(",", Tags);
List<MasterData> mds = DB.GetMasterDataByTag(CveTags);
if (mds.Count == 0)
{
Console.WriteLine("No se encontraron datos...");
}

foreach (MasterData mdi in mds)
{
Console.WriteLine(mdi.DataInfo);
}

break;
case '3':
salir = false;
break;
default:
Console.WriteLine("Opción no reconocida.");
break;
}
}
}
}
}

Como podemos ver en el código de program.cs, podemos encontrar una lista notags, estos son todos los tipos de palabras que no queremos que se incluyan dentro de los tags reales, en mi caso pueden ser artículos, preposiciones, etc…

Por otra parte podemos encontrar un switch que mantendrá aislado nuestras piezas de código de consola, el cual permite agregar y buscar información dentro de las opciones están las siguientes:

  1. Agregar una frase
  2. Buscar una frase por palabras
  3. Salir

En la opción uno podemos agregar una frase, vamos a analizar el código,

  • Primero leemos la Frase de el texto escrito por el usuario
  • Segundo agregamos la Frase a la Base de Datos
  • Tercero Remplazamos en la frase por vació en los artículos
  • Cuarto Separamos las frases en palabras mediante espacios
  • Quinto  y último Creamos un ciclo en el cual por cada palabra que no exista en la base de datos se agrega una referencia o se agrega la palabra según sea el caso

El la opción dos podemos hacer una búsqueda mediante las palabras escritas, tomando en cuenta que el diccionario RAE contine un aproximando de 88,000 palabras conforme nuestras frases crezcan los indices de los metatags se quedarán estáticos y solo se agregarán referencias que nos permitirán búsquedas más rápidas, ahora veremos como funciona el sistema de busquedas

Opción dos, debemos agregar palabras separadas por {,} esto nos permitirá hacer búsquedas por los elementos solicitados.

  • Primero Leemos la Linea
  • Segundo Separamos por comas
  • Tercero Traemos todos los Tags, como ya sabemos su máximo no pasa de 90,000 (Suponiendo que a la RAE no se le ocurra meter más pseudo palabras)
  • Cuarto creamos un ciclo, donde agregamos todos los tags buscados a una cadena separada por comas
  • Quinto Hacemos la búsqueda con el parámetro de los tags.
  • Sexto Presentamos el resultado

Como se puede apreciar en el código el motor, no es nada del otro mundo eso si nos ahorra muchos problemas a la hora de hacer búsquedas, (lo cierto es que pienso usarlo para hacer búsquedas indexadas de Nombres propios, Apellidos Paternos y Maternos) ya después de usarlo les contaré como fue eso; Mientras tanto me despido y les dejo el link de la descarga, nos vemos a la próxima que haya tiempo.

Descarga (Pendiente)

Anuncios

WebHooks ¿Qué son? ¿Como hago Uno? (Parte 3)


Y bien ya estamos en la tercera parte de nuestra entrega sobre WebHooks hasta el momento, en las anteriores entregas hemos generado nuestro código principal para un manager que contiene varios webhooks, esta tercera parte nos permitirá hacer uso del WebHook como tal.

A continuación se presentan los Archivos que se van a generar dentro del Proyecto:

  • WHService
    • Application
      • AppWebHook.cs (Crear)
    • Contract
      • Data
        • WH
          • GetRequest.cs (Crear)
          • GetResponse.cs (Crear)
          • PostRequest.cs (Crear)
          • PostResponse.cs (Crear)
      • Service
        • IWebHookService.cs (Crear)
    • Service
      • WebHookService.cs (Crear)
    • Web.Config (Modificar)

Comenzamos por mostrar los códigos.

AppWebHook.cs


public class AppWebHook : IDisposable
{

public GetResponse GetData(string Application, string ApiKey, string Token, string Resource, GetRequest request)
{
GetResponse response = new GetResponse();
Domain.Application a = ApplicationRepository.GetApplicationByName(Application);
Message m = MessageRepository.GetMessagesByResource(Resource);
if (m == null)
{
throw new Exception("El Recurso no Existe");
}
if (a == null)
{
throw new Exception("La Aplicación no Existe");
}
if (a.ApiKey != ApiKey)
{
throw new Exception("La Llave Secreta no Coincide");
}
if (a.CveApplication != m.CveApplication)
{
throw new Exception("El recurso no pertenece a esta aplicación");
}
Data d = null;
if (request.Id > 0)
{
d = DataRepository.GetDataById(request.Id, a.CveApplication);
}
else
{
d = DataRepository.GetLastDatabyApplication(a.ApplicationName);
}
response.Data = d.Info;
return response;
}

public PostResponse PostData(string Application, string ApiKey, string Token, string Resource, PostRequest request)
{
PostResponse response = new PostResponse();
Domain.Application a = ApplicationRepository.GetApplicationByName(Application);
Message m = MessageRepository.GetMessagesByResource(Resource);
if (m == null)
{
throw new Exception("El Recurso no Existe");
}
if (a == null)
{
throw new Exception("La Aplicación no Existe");
}
if (a.ApiKey != ApiKey)
{
throw new Exception("La Llave Secreta no Coincide");
}
if (a.CveApplication != m.CveApplication)
{
throw new Exception("El recurso no pertenece a esta aplicación");
}
string mess = request.Message;
switch (request.Type)
{
case "JSON":
Result o = CustomCompiler.ExecuteData(m.RequestData, mess);
break;
default: //Marcaremos todo Como String
break;
}

Data d = new Data();
d.CveApplication = a.CveApplication;
d.CveMessage = m.CveMessage;
d.DateInsert = DateTime.Now;
d.IsRead = false;
d.Info = mess;
DataRepository.Add(d);
return response;
}

public void Dispose()
{

}
}

GetRequest.cs


publicclassGetRequest
{
public string Resource { get; set; }
public int Id { get; set; }
}

GetResponse.cs


publicclassGetResponse
{
public string Data { get; set; }
}

PostRequest.cs


publicclassPostRequest
{
public string Type { get; set; }
public string Message { get; set; }
}

PostResponse.cs


publicclassPostResponse
{
}

IWebHookService.cs


[ServiceContract]
public interface IWebHookService : IDisposable
{
[OperationContract]
[WebInvoke(UriTemplate = "{Resource}/{id=0}/", Method = "GET", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json),
Description("Obtiene un Recurso Generado")]
GetResponse GetData(string Resource, string id);

[OperationContract]
[WebInvoke(UriTemplate = "{Resource}/", Method = "POST", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json),
Description("Recurso Generado")]
PostResponse PostData(string Resource, PostRequest request);
}

WebHookService.cs


private readonly AppWebHook appwh;
public WebHookService()
{
appwh = new AppWebHook();
}

public GetResponse GetData(string Resource, string id)
{
GetResponse response = new GetResponse();
try
{
WebHeaderCollection headers = ValidateHeaders();
GetRequest request = new GetRequest();
request.Resource = Resource;
request.Id = string.IsNullOrEmpty(id) ? 0 : Convert.ToInt32(id);
response = appwh.GetData(headers.Get("Application"), headers.Get("ApiKey"), headers.Get("Token"), Resource, request);
}
catch (Exception ex)
{
throw new WebFaultException(ex.Message, HttpStatusCode.BadRequest);
}
return response;
}

public PostResponse PostData(string Resource, PostRequest request)
{
PostResponse response = new PostResponse();
try
{
WebHeaderCollection headers = ValidateHeaders();
appwh.PostData(headers.Get("Application"), headers.Get("ApiKey"), headers.Get("Token"), Resource, request);
}
catch (Exception ex)
{
throw new WebFaultException(ex.Message, HttpStatusCode.BadRequest);
}
return response;
}

private static WebHeaderCollection ValidateHeaders()
{
if (WebOperationContext.Current != null)
{
WebHeaderCollection headers = WebOperationContext.Current.IncomingRequest.Headers;
if (string.IsNullOrEmpty(headers.Get("Content-Type")))
{
throw new Exception("Error al obtener Encabezados");
}
else if (string.IsNullOrEmpty(headers.Get("Application")))
{
throw new Exception("Error al obtener Encabezados");
}
else if (string.IsNullOrEmpty(headers.Get("ApiKey")))
{
throw new Exception("Error al obtener Encabezados");
}
else if (string.IsNullOrEmpty(headers.Get("Token")))
{
throw new Exception("Error al obtener Encabezados");
}
return headers;
}
else
{
throw new Exception("Error al obtener Encabezados");
}
}
}

Web.Config

En este caso, agregamos la configuración que nos lleva a exponer el Api


<service behaviorConfiguration="BehaviorConfigurationService" name="WHService.Service.WebHookService">
<endpoint address="" binding="basicHttpBinding" name="WHEndPoint" contract="WHService.Contract.Service.IWebHookService"/>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
<endpoint address="/api/" binding="customBinding" bindingConfiguration="RawReceiveCapable" behaviorConfiguration="RestServiceEndPointBehaviors" contract="WHService.Contract.Service.IWebHookService"/>
</service>

Vamos a mostrar, las ejecuciones con un proyecto de SOAPUI y consumir nuestro servicio como un servicio REST, vamos a probarlo:

En la imagen anterior podemos ver como hemos asignado los recursos al SOAPUI y realizaremos la operación Hello en los métodos GET y POST, a continuación vemos el método que usaremos para insertar

Como podemos ver el metodo get recibe los Headers ApiKey, Application, Token, Content-Type, estos encabezados nos permitirán buscar y hacer validaciones dentro del sistema, cabe mencionar que el metodo Token, debería llevar un Digestivo con las llaves que creamos, esto para poder verificar que la persona que esta agregando información a nuestro WebHook es alguien que tiene la Llave, por eso es que cuando se crea una aplicación se dan llaves de Cifrado y el ApiKey, con esto, el sistema ya tiene los datos dentro de la Base de datos.

A continuación veremos el metodo GET

Como podemos ver nuestros datos tal cual han sido agregados al sistema. finalmente se tienen que hacer más acciones de seguridad, pero como siempre los invito a implementar y re implementar sus propias mejoras, esto es una idea, para que creen ideas más grandes.

(Descarga)

WebHooks ¿Qué son? ¿Como hago Uno? (Parte 1)


YDesde hace algun tiempo vengo viendo que se agregan nuevas terminologías al diccionario informatico, desde hace algun tiempo la palabra WebHook ha tomado popularidad, por ahí en wikipedia dice que este termino fue inventado apartir de la programación Hook o funcionalidad Hook (En mis tiempos se llamaban Callbacks), si, si sabes JavaScript Ajax o Jquery Asincrono, si, has usado programación Hook, la diferencia aqui radica en que la llamada se hará hacia un WebService, en mi caso, me toco conectarme a uno para intercambio de información entre transacciones bancarias, lo cual es por demás interesante; Pero… Quiero hacer algo generico, que me soporte cualquier tipo de información y yo despues haga lo que quiera con ella, imaginate un repositorio de BigData con diferentes informaciónes en un mismo usuario, donde puedas programar tus entradas y salidas, para un webhook esto seria divertido y el truco está en como crear una configuración

Para este ejemplo crearemos lo siguiente:

  • Diseño de Base de Datos
  • WCF Configurado como REST (Este servicio contendrá)
    • Metodo Generador de Llaves Publica y Privada
    • Metodo Generador de ApiKey
    • Metodo Al que se enviará la Información
    • Metodo de Creación de Clases de Trasnporte
    • Metodo de Asignación del Tipo de Mensaje
    • Metodo de Obtención de Mensaje
  • Pagina de Configuración Tipo MVC (Este contendrá)
    • Pagina de Generación de ApiKey y Llave Publica
    • Configuración para crear Objetos y Reservar el Tipo de Objeto dentro del WebHook
    • Asignación de Tipo de Mensaje
    • Ver Lista de Mensajes Enviados

Comenzaremos definiendo el Diseño de Base de Datos que queda como en la siguiente imagen:

Como podemos ver en la imagen anterior el diseño no es nada complicado, aqui jugaremos con pedasos de codigo que ya han sido expuestos con anterioridad y algunos nuevos que crearemos gracias a algunas investigaciones que se han realizado a lo largo de estos meses.

Continuaremos creando una solución nueva a la cual llamaremos WHService, ya saben abrimos nuestro Visual Studio como en la siguiente imagen:

Y una vez creado, crearemos el siguiente arbol de archivos:

Los lectores que llevan tiempo visitando este blog y ha seguido los modos de construcción del diseño orientado a dominio, ya estarán familiarizados con este modelo, comenzaremos poniendo las clases de la Carpeta Domain con las siguientes clases:

  • Application.cs
  • Data.cs
  • Result.cs
  • Message.cs

Contenido de Application.cs


public class Application
{
public int CveApplication { get; set; }
public string ApplicationName { get; set; }
public string ApiKey { get; set; }
public string PrivateKey { get; set; }
public string PublicKey { get; set; }
}

Contenido de Data.cs


public class Data
{
public int CveData { get; set; }
public int CveApplication { get; set; }
public int CveMessage { get; set; }
public string Info { get; set; }
public DateTime DateInsert { get; set; }
public bool IsRead { get; set; }
}

Contenido de Result.cs


public class Result
{
public bool Success { get; set; }
public string Erros { get; set; }
public int ErrorNumber { get; set; }
}

Codigo de Message.cs


public class Message
{
public int CveMessage { get; set; }
public string MessageName { get; set; }
public string ResourceName { get; set; }
public string RequestData { get; set; }
public string ResponseData { get; set; }
public int CveApplication { get; set; }
}

Después de haber realizado la creación de las clases de dominio, pasaremos a crear las clases de Acceso a Datos que se encuentran en la carpeta DataAccess

  • ApplicationRepository.cs
  • DataRepository.cs
  • MessageRepository.cs

A continuación el Código de cada una:

Coddigo de ApplicationRepository.cs


public class ApplicationRepository
{
public static List<Domain.Application> GetApplications()
{
List<Domain.Application> ass = new List<Domain.Application>();
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["Context"].ToString()))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = @"SELECT [CveApplication],[ApplicationName],[ApiKey],[PrivateKey],[PublicKey]
FROM [Application]";
using (IDataReader r = cmd.ExecuteReader())
{
while (r.Read())
{
ass.Add(PopulateData(r));
}
}
}
}
return ass;
}

public static Domain.Application GetApplicationByName(string ApplicationName)
{
Domain.Application a = null;
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["Context"].ToString()))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = @"SELECT [CveApplication],[ApplicationName],[ApiKey],[PrivateKey],[PublicKey]
FROM [Application]
WHERE ApplicationName=@ApplicationName";
cmd.Parameters.AddWithValue("@ApplicationName", ApplicationName);
using (IDataReader r = cmd.ExecuteReader())
{
while (r.Read())
{
a = PopulateData(r);
}
}
}
}
return a;
}

public static Domain.Application GetApplicationById(int CveApplication)
{
Domain.Application a = null;
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["Context"].ToString()))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = @"SELECT [CveApplication],[ApplicationName],[ApiKey],[PrivateKey],[PublicKey]
FROM [Application]
WHERE CveApplication=@CveApplication";
cmd.Parameters.AddWithValue("@CveApplication", CveApplication);
using (IDataReader r = cmd.ExecuteReader())
{
while (r.Read())
{
a = PopulateData(r);
}
}
}
}
return a;
}

public static Domain.Application PopulateData(IDataReader r)
{
Domain.Application a = new Domain.Application();
a.CveApplication = Convert.ToInt32(r["CveApplication"]);
a.ApplicationName = Convert.ToString(r["ApplicationName"]);
a.ApiKey = Convert.ToString(r["ApiKey"]);
a.PrivateKey = Convert.ToString(r["PrivateKey"]);
a.PublicKey = Convert.ToString(r["PublicKey"]);
return a;
}

public static void Add(Domain.Application a)
{
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["Context"].ToString()))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = @"INSERT INTO [dbo].[Application] ([ApplicationName],[ApiKey],[PrivateKey],[PublicKey])
VALUES (@ApplicationName,@ApiKey,@PrivateKey,@PublicKey);
SELECT SCOPE_IDENTITY() AS CveApplication";
cmd.Parameters.AddWithValue("@ApplicationName", a.ApplicationName);
cmd.Parameters.AddWithValue("@ApiKey", a.ApiKey);
cmd.Parameters.AddWithValue("@PrivateKey", a.PrivateKey);
cmd.Parameters.AddWithValue("@PublicKey", a.PublicKey);
a.CveApplication = Convert.ToInt32(cmd.ExecuteScalar());
}
}
}

public static void Update(Domain.Application a)
{
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["Context"].ToString()))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = @"UPDATE [dbo].[Application]
SET [ApplicationName] = @ApplicationName
,[ApiKey] = @ApiKey
,[PrivateKey] = @PrivateKey
,[PublicKey] = @PublicKey
WHERE CveApplication=@CveApplication";
cmd.Parameters.AddWithValue("@ApplicationName", a.ApplicationName);
cmd.Parameters.AddWithValue("@ApiKey", a.ApiKey);
cmd.Parameters.AddWithValue("@PrivateKey", a.PrivateKey);
cmd.Parameters.AddWithValue("@PublicKey", a.PublicKey);
cmd.Parameters.AddWithValue("@CveApplication", a.CveApplication);
cmd.ExecuteNonQuery();
}
}
}

public static long Delete(int a)
{
long RecordId = 0;
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["Context"].ToString()))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = @"DELETE FROM [dbo].[Application] WHERE CveApplication=@CveApplication";
cmd.Parameters.AddWithValue("@CveApplication", a);
cmd.ExecuteNonQuery();
}
}
return RecordId;
}
}

Código de Data.cs


public class DataRepository
{
public static Domain.Data GetLastDatabyApplication(string ApplicationName)
{
Domain.Data ds = new Domain.Data();
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["Context"].ToString()))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = @"SELECT TOP 1 d.[CveData],d.[CveApplication],d.[CveMessage],d.[Info],d.[DateInsert],d.[IsRead]
FROM [Data] d
JOIN [Application] a ON a.CveApplication=d.CveApplication
JOIN [Message] m ON m.CveMessage = d.CveMessage
WHERE a.ApplicationName=@ApplicationName
ORDER BY DateInsert";
cmd.Parameters.AddWithValue("@ApplicationName", ApplicationName);
using (IDataReader r = cmd.ExecuteReader())
{
while (r.Read())
{
ds = Populate(r);
}
}
}
}
return ds;
}

public static List<Domain.Data> GetDataByApplicationName(string ApplicationName)
{
List<Domain.Data> ds = new List<Domain.Data>();
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["Context"].ToString()))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = @"SELECT d.[CveData],d.[CveApplication],d.[CveMessage],d.[Info],d.[DateInsert],d.[IsRead]
FROM [Data] d
JOIN [Application] a ON a.CveApplication=d.CveApplication
JOIN [Message] m ON m.CveMessage = d.CveMessage
WHERE a.ApplicationName=@ApplicationName
ORDER BY DateInsert";
cmd.Parameters.AddWithValue("@ApplicationName", ApplicationName);
using (IDataReader r = cmd.ExecuteReader())
{
while (r.Read())
{
ds.Add(Populate(r));
}
}
}
}
return ds;
}

public static Domain.Data GetDataById(int CveData)
{
Domain.Data ds = new Domain.Data();
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["Context"].ToString()))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = @"SELECT d.[CveData],d.[CveApplication],d.[CveMessage],d.[Info],d.[DateInsert],d.[IsRead]
FROM [Data] d
JOIN [Application] a ON a.CveApplication=d.CveApplication
JOIN [Message] m ON m.CveMessage = d.CveMessage
WHERE d.CveData=@CveData";
cmd.Parameters.AddWithValue("@CveData", CveData);
using (IDataReader r = cmd.ExecuteReader())
{
while (r.Read())
{
ds = Populate(r);
}
}
}
}
return ds;
}

public static Domain.Data Populate(IDataReader r)
{
Domain.Data d = new Domain.Data();
d.CveApplication = Convert.ToInt32(r["CveApplication"]);
d.CveData = Convert.ToInt32(r["CveData"]);
d.CveMessage = Convert.ToInt32(r["CveMessage"]);
d.DateInsert = Convert.ToDateTime(r["DateInsert"]);
d.Info = Convert.ToString(r["Info"]);
d.IsRead = Convert.ToBoolean(r["IsRead"]);
return d;
}

public static void Add(Domain.Data d)
{
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["Context"].ToString()))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = @"INSERT INTO [dbo].[Data] ([CveApplication],[CveMessage],[Info],[DateInsert],[IsRead])
VALUES (@CveApplication,@CveMessage,@Info,@DateInsert,@IsRead);
SELECT SCOPE_IDENTITY() AS CveData";
cmd.Parameters.AddWithValue("@CveApplication", d.CveApplication);
cmd.Parameters.AddWithValue("@CveMessage", d.CveMessage);
cmd.Parameters.AddWithValue("@Info", d.Info);
cmd.Parameters.AddWithValue("@DateInsert", d.DateInsert);
cmd.Parameters.AddWithValue("@IsRead", d.IsRead);
d.CveData = Convert.ToInt32(cmd.ExecuteScalar());
}
}
}

public static void Update(Domain.Data d)
{
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["Context"].ToString()))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = @"UPDATE [dbo].[Data]
SET [CveApplication] = @CveApplication
,[CveMessage] = @CveMessage
,[Info] = @Info
,[DateInsert] = @DateInsert
,[IsRead] = @IsRead
WHERE CveData=@CveData";
cmd.Parameters.AddWithValue("@CveApplication", d.CveApplication);
cmd.Parameters.AddWithValue("@CveMessage", d.CveMessage);
cmd.Parameters.AddWithValue("@Info", d.Info);
cmd.Parameters.AddWithValue("@DateInsert", d.DateInsert);
cmd.Parameters.AddWithValue("@IsRead", d.IsRead);
cmd.Parameters.AddWithValue("@CveData", d.CveData);
cmd.ExecuteNonQuery();
}
}
}

public static long Delete(int d)
{
long RecordId = 0;
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["Context"].ToString()))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = @"DELETE FROM [dbo].[Data] WHERE CveData=@CveData";
cmd.Parameters.AddWithValue("@CveData", d);
cmd.ExecuteNonQuery();
}
}
return RecordId;
}
}

Codigo de MessageRepository.cs


public static class MessageRepository
{
public static List<Domain.Message> GetMessagesByApplicationName(string ApplicationName)
{
List<Domain.Message> ms = new List<Domain.Message>();
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["Context"].ToString()))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = @"SELECT m.[CveMessage],m.[MessageName],m.[ResourceName],m.[RequestData],m.[ResponseData],m.[CveApplication]
FROM [Message] m
JOIN [Application] a ON a.CveApplication=m.CveApplication
WHERE a.ApplicationName=@ApplicationName";
cmd.Parameters.AddWithValue("@AUTHORIZATION_ID", ApplicationName);
using (IDataReader r = cmd.ExecuteReader())
{
while (r.Read())
{
ms.Add(Populate(r));
}
}
}
}
return ms;
}

public static Domain.Message GetMessagesById(int CveMessage)
{
Domain.Message m = null;
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["Context"].ToString()))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = @"SELECT m.[CveMessage],m.[MessageName],m.[ResourceName],m.[RequestData],m.[ResponseData],m.[CveApplication]
FROM [Message] m WHERE CveMessage=@CveMessage";
cmd.Parameters.AddWithValue("@CveMessage", CveMessage);
using (IDataReader r = cmd.ExecuteReader())
{
while (r.Read())
{
m = Populate(r);
}
}
}
}
return m;
}

public static Domain.Message GetMessagesByResource(string ResourceName)
{
Domain.Message m = null;
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["Context"].ToString()))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = @"SELECT m.[CveMessage],m.[MessageName],m.[ResourceName],m.[RequestData],m.[ResponseData],m.[CveApplication]
FROM [Message] m WHERE ResourceName=@ResourceName";
cmd.Parameters.AddWithValue("@ResourceName", ResourceName);
using (IDataReader r = cmd.ExecuteReader())
{
while (r.Read())
{
m = Populate(r);
}
}
}
}
return m;
}

public static Domain.Message Populate(IDataReader r)
{
Domain.Message m = new Domain.Message();
m.CveApplication = Convert.ToInt32(r["CveApplication"]);
m.CveMessage = Convert.ToInt32(r["CveMessage"]);
m.MessageName = Convert.ToString(r["MessageName"]);
m.RequestData = Convert.ToString(r["RequestData"]);
m.ResourceName = Convert.ToString(r["ResourceName"]);
m.ResponseData = Convert.ToString(r["ResponseData"]);
return m;
}

public static void Add(Domain.Message m)
{
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["Context"].ToString()))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = @"INSERT INTO [dbo].[Message] ([MessageName],[ResourceName],[RequestData],[ResponseData],[CveApplication])
VALUES (@MessageName,@ResourceName,@RequestData,@ResponseData,@CveApplication);
SELECT SCOPE_IDENTITY() AS RECORD_ID";
cmd.Parameters.AddWithValue("@MessageName", m.MessageName);
cmd.Parameters.AddWithValue("@ResourceName", m.ResourceName);
cmd.Parameters.AddWithValue("@RequestData", m.RequestData);
cmd.Parameters.AddWithValue("@ResponseData", m.ResponseData);
cmd.Parameters.AddWithValue("@CveApplication", m.CveApplication);
m.CveMessage = Convert.ToInt32(cmd.ExecuteScalar());
}
}
}

public static void Update(Domain.Message m)
{
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["Context"].ToString()))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = @"UPDATE [dbo].[Message]
SET [MessageName] = @MessageName
,[ResourceName] = @ResourceName
,[RequestData] = @RequestData
,[ResponseData] = @ResponseData
,[CveApplication] = @CveApplication
WHERE CveMessage=@CveMessage";
cmd.Parameters.AddWithValue("@MessageName", m.MessageName);
cmd.Parameters.AddWithValue("@ResourceName", m.ResourceName);
cmd.Parameters.AddWithValue("@RequestData", m.RequestData);
cmd.Parameters.AddWithValue("@ResponseData", m.ResponseData);
cmd.Parameters.AddWithValue("@CveApplication", m.CveApplication);
cmd.Parameters.AddWithValue("@CveMessage", m.CveMessage);
cmd.ExecuteNonQuery();
}
}
}

public static long Delete(int m)
{
long RecordId = 0;
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["Context"].ToString()))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = @"DELETE FROM [dbo].[Message] WHERE CveMessage=@CveMessage";
cmd.Parameters.AddWithValue("@CveMessage", m);
cmd.ExecuteNonQuery();
}
}
return RecordId;
}
}

Una vez realizado esta sección comenzaremos con la parte interesante que es la creación de nuestra aplicación, como les dije antes este será un webhook en el que podremos agregar cualquier tipo de dato, lo cierto es que vamos a crear un manager para poder registrar nuestros webhooks y nuestros tipos de mensajes independientemente de que estos se manejen como json del tipo string, lo correcto es que validemos esta porción de código, lo cual nos llevará al mundo oscuro del CodeDom, pero veamos que tan divertido nos sale esto, para estos efectos se supone que tendremos llaves publicas y privadas que se generarán a partir de una aplicación, asi como una secret key que no es otra cosa que un bonito guid asi que comencemos en la carpeta Helpers dentro de la Carpeta Application, crearemos los siguinetes archivos

CryptoHelper.cs y CustomCompiler.cs


public partial class CryptoHelper
{
public RSACryptoServiceProvider RSAService { get; set; }

public CryptoHelper()
{
this.RSAService = new RSACryptoServiceProvider(2048);
}

public byte[] CreatePublicKey()
{
string xmlPublicKey = this.RSAService.ToXmlString(false);
return Encoding.ASCII.GetBytes(xmlPublicKey);
}
public byte[] CreatePrivateKey()
{
string xmlPrivateKey = this.RSAService.ToXmlString(true);
return Encoding.ASCII.GetBytes(xmlPrivateKey);
}

public static byte[] EncryptText(string text, string xmlPublicKey)
{
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(2048);
RSA.FromXmlString(xmlPublicKey);
byte[] encryptedData = RSA.Encrypt(Encoding.ASCII.GetBytes(text), false);
return encryptedData;
}

public static byte[] DecryptText(string encrypttext, string xmlPrivateKey)
{
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(2048);
RSA.FromXmlString(xmlPrivateKey);
byte[] decryptData = RSA.Decrypt(Convert.FromBase64String(encrypttext), false);
return decryptData;
}
}

Como podemos ver CryptoHelper es una clase que esta dedicada a crear las llaves publicas y privadas, además tiene los metodos de cifrado y descifrado, estas que ocuparan la parte de creación de llaves para el Manager y previamente para cifrar y des cifrar en el webhook.

Veamos el código de CustomCompiler.cs


public static class CustomCompiler
{
public static Result EvalCode(string sCSCode)
{
Result r = new Result();
CodeDomProvider c = CodeDomProvider.CreateProvider("CSharp");
CompilerParameters cp = new CompilerParameters();

cp.ReferencedAssemblies.Add("system.dll");
cp.ReferencedAssemblies.Add("system.xml.dll");
cp.ReferencedAssemblies.Add("system.data.dll");
cp.ReferencedAssemblies.Add("system.windows.forms.dll");
cp.ReferencedAssemblies.Add("system.drawing.dll");

cp.CompilerOptions = "/t:library";
cp.GenerateInMemory = true;

StringBuilder sb = new StringBuilder(""); //Texto del codigo
sb.Append("using System;\n");
sb.Append("using System.Xml;\n");
sb.Append("using System.Data;\n");
sb.Append("using System.Data.SqlClient;\n");
sb.Append("using System.Windows.Forms;\n");
sb.Append("using System.Drawing;\n");

sb.Append("namespace CustomSpace{ \n");
sb.Append(sCSCode);
sb.Append("}\n");

CompilerResults cr = c.CompileAssemblyFromSource(cp, sb.ToString()); //icc.CompileAssemblyFromSource(cp, sb.ToString());
if (cr.Errors.Count > 0)
{
r.Erros = cr.Errors[0].ErrorText;
r.ErrorNumber = cr.Errors.Count;
r.Success = false;
return r;
}

r.Success = true;
return r;

}

public static Result ExecuteData(string sCSCode)
{
Result r = new Result();
CodeDomProvider c = CodeDomProvider.CreateProvider("CSharp");
CompilerParameters cp = new CompilerParameters();

cp.ReferencedAssemblies.Add("system.dll");
cp.ReferencedAssemblies.Add("system.xml.dll");
cp.ReferencedAssemblies.Add("system.data.dll");
cp.ReferencedAssemblies.Add("system.windows.forms.dll");
cp.ReferencedAssemblies.Add("system.drawing.dll");

cp.CompilerOptions = "/t:library";
cp.GenerateInMemory = true;

StringBuilder sb = new StringBuilder(""); //Texto del codigo
sb.Append("using System;\n");
sb.Append("using System.Xml;\n");
sb.Append("using System.Data;\n");
sb.Append("using System.Data.SqlClient;\n");
sb.Append("using System.Windows.Forms;\n");
sb.Append("using System.Drawing;\n");

sb.Append("namespace CustomSpace{ \n");
sb.Append(sCSCode);
sb.Append("}\n");

CompilerResults cr = c.CompileAssemblyFromSource(cp, sb.ToString()); //icc.CompileAssemblyFromSource(cp, sb.ToString());
if (cr.Errors.Count > 0)
{
r.Erros = cr.Errors[0].ErrorText;
r.ErrorNumber = cr.Errors.Count;
r.Success = false;
return r;
}

System.Reflection.Assembly a = cr.CompiledAssembly;
object o = a.CreateInstance("CSCodeEvaler.CSCodeEvaler");

Type t = o.GetType();
#region Preparación de Implementación y Validación del Codigo
//Generar Region despues
#endregion
r.Success = true;
return r;

}
}

Estas clases son las principales, CustomCompiler permite crear y validar código en tiempo real, aun falta la región de como lo haremos pero creo que veremos esa opción en el próximo capitulo, por ahora terminaremos el servicio del Manager, así que a continuación crearemos el archivo AppManager.cs que será el encargado de generar los métodos que se expondrán en el servicio:


public class AppManager : IDisposable
{
private readonly IMapper _mapper;
public AppManager()
{
this._mapper = AppProfile.ConfigMaps();
}

#region Application
public GetApplicationsResponse GetApplications(GetApplicationRequest request)
{
GetApplicationsResponse response = new GetApplicationsResponse();
List<Domain.Application> a = ApplicationRepository.GetApplications();
response.Applications = this._mapper.Map<List<Domain.Application>, List<GetApplicationResponse>>(a);
return response;
}

public GetApplicationResponse GetApplication(GetApplicationRequest request)
{
GetApplicationResponse response = new GetApplicationResponse();
Domain.Application a = ApplicationRepository.GetApplicationByName(request.ApplicationName);
response = this._mapper.Map<Domain.Application, GetApplicationResponse>(a);
return response;
}

public AddApplicationResponse AddApplication(AddAplicationRequest request)
{
AddApplicationResponse response = new AddApplicationResponse();
Domain.Application a = new Domain.Application();
a.ApplicationName = request.ApplicationName;
a.ApiKey = Guid.NewGuid().ToString();
CryptoHelper cipher = new CryptoHelper();
a.PrivateKey = Convert.ToBase64String(cipher.CreatePrivateKey());
a.PublicKey = Convert.ToBase64String(cipher.CreatePublicKey());
ApplicationRepository.Add(a);
return response;
}

public UpdateApplicationResponse UpdateApplication(UpdateApplicationRequest request)
{
UpdateApplicationResponse response = new UpdateApplicationResponse();
Domain.Application a = ApplicationRepository.GetApplicationById(request.CveApplication);
a.ApplicationName = request.ApplicationName;
ApplicationRepository.Update(a);
return response;
}

public DeleteApplicationResponse DeleteApplication(DeleteApplicationRequest request)
{
DeleteApplicationResponse response = new DeleteApplicationResponse();
ApplicationRepository.Delete(request.CveApplication);
return response;
}

#endregion

#region Message
public GetMessagesResponse GetMessages(GetMessageRequest request)
{
GetMessagesResponse response = new GetMessagesResponse();
List<Domain.Message> m = MessageRepository.GetMessagesByApplicationName(request.ApplicationName);
response.Messages = this._mapper.Map<List<Domain.Message>, List<GetMessageResponse>>(m);
return response;
}

public GetMessageResponse GetMessage(GetMessageRequest request)
{
GetMessageResponse response = new GetMessageResponse();
Domain.Message m = MessageRepository.GetMessagesById(request.CveMessage);
response = this._mapper.Map<Domain.Message, GetMessageResponse>(m);
return response;
}

public AddMessageResponse AddMessage(AddMessageRequest request)
{
AddMessageResponse response = new AddMessageResponse();
Domain.Message m = new Domain.Message();
m.CveApplication = request.CveApplication;
m.MessageName = request.MessageName;
CustomCompiler.EvalCode(request.RequestData);
CustomCompiler.EvalCode(request.ResponseData);
m.RequestData = request.RequestData;
m.ResponseData = request.ResponseData;
m.ResourceName = request.ResourceName;
MessageRepository.Add(m);
return response;
}

public UpdateMessageResponse UpdateMessage(UpdateMessageRequest request)
{
UpdateMessageResponse response = new UpdateMessageResponse();
Domain.Message m = new Domain.Message();
m.CveApplication = request.CveApplication;
m.CveMessage = request.CveMessage;
m.MessageName = request.MessageName;
CustomCompiler.EvalCode(request.RequestData);
CustomCompiler.EvalCode(request.ResponseData);
m.RequestData = request.RequestData;
m.ResponseData = request.ResponseData;
m.ResourceName = request.ResourceName;
MessageRepository.Update(m);
return response;
}

public DeleteMessageResponse DeleteMessage(DeleteMessageRequest request)
{
DeleteMessageResponse response = new DeleteMessageResponse();
MessageRepository.Delete(request.CveMessage);
return response;
}

#endregion

#region Data

public GetDatasResponse GetDatas(GetDataRequest request)
{
GetDatasResponse response = new GetDatasResponse();
List<Domain.Data> d = DataRepository.GetDataByApplicationName(request.ApplicationName);
response.Datas = this._mapper.Map<List<Domain.Data>, List<GetDataResponse>>(d);
return response;
}

public GetDataResponse GetData(GetDataRequest request)
{
GetDataResponse response = new GetDataResponse();
Domain.Data d = DataRepository.GetDataById(request.CveData);
response = this._mapper.Map<Domain.Data, GetDataResponse>(d);
return response;
}

#endregion
public void Dispose()
{

}
}

Existen dos métodos que vale la pena mencionar, como lo son AddApplication donde podemos ver la creación del GUID y las Llaves Publica y Privada, y AddMessage en el cual request.RequestData y request.ResponseData contendrán clases de entrada y salida, (Si ya se complique de más la situación pero es divertido) que servirán para validar la información, OJO esto es un ejemplo, no recomiendo usar esto para exponer en sistemas dado que esto es permitir al usuario ejecutar cualquier código dentro de nuestro programa, apartir de esto crearemos los Accesos a los Repositorios o Contratos, lo cual es muy pero muy largo así que mejor dejo el ejemplo para que lo puedan descargar y pasaré a la ejecución del Servicio Manager veamos como queda:

Vamos a ver como quedan los Métodos AddApplication y GetApplication:

Y despues de insertado nuestro registro lo podemos obtener con el mismo nombre:

Con esto terminamos por hoy, a continuación les muestro el enlace para que puedan ver el proyecto completo

(Descargar)

 

En la próxima parte, crearemos el la parte visual para jugar con nuestro sistema y terminar de crear la validación de código, ya verán que será divertido.

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)

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

 

Comentarios sobre ActiveDirectory o Directorio Activo


Bien hoy les vengo a platicar lo que haré en uno de mis proyectos, SAMUS 5.3 hará una de las cosas más interesantes y que por cierto comentan que no tiene ninguna gracia, será conectarse al DirectorioActivo de Windows para poder permitir al usuario conectarse desde el portal cautivo o no, lo cual implicará, algunas cosas como lo son migrar la base de datos y crear un wsSAMUS el cual a estas alturas tendrá el core de samus para conectarse a la base de datos y a directorio activo y por lo tanto este pequeño sistema cambiará en SAMUS para ser creado como un framework, prometo que dejando lista la  versión 5 dejaré de jugar con esto, por cierto, veremos algunos ejemplos de consumo de SOAP desde C++ así que los siguientes ejemplos serán divertidos, este elemento estará en todas las categorías que se utilizará para ensamblar SAMUS

A %d blogueros les gusta esto: