Archivo de la categoría: .Net

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 2)


Bien, en la anterior entrega, vimos como crear el servicio para nuestro WebHook, en esta ocasión vamos a crear el Manager con el cual podremos Agregar y Editar tanto Aplicaciones y Mensajes. Vamos a comenzar Agregando un nuevo Proyecto a nuestra solución WHService, y lo llamaremos WHManager, hago un paracentesis en esta parte dado que me la llevaré rápido con el MVC, si quieren saber como integrar bootstrap a MVC puedes pasarte por esta liga en ella podrás ver a profundidad como utilizo el MVC y WebApi.

Comenzamos agregando un proyecto de MVC al cual nombraremos WHManager como en la siguiente Imagen:

Luego de poner el nombre y dar click en aceptar vamos a seleccionar Empty Proyect,  y nos muestra una solución base como la siguiente:

 

Apartir de Aqui vamos a crear y modificar Archivos en las diferentes secciones:

  • App_Start
    • WebApiConfig.cs (Modificar)
  •  Controllers
    • HomeController.cs (Crear)
  • css (Copiar)
  • Fonts (Copiar)
  • img (Copiar)
  • js (Copiar)
  • Models
    • Result.cs (Crear)
  • service
    • ApplicationController.cs (Crear)
    • ResourceController.cs (Crear)
  • Views
    • Home
      • AddApplication.cshtml (Crear)
      • AddResource.cshtml (Crear)
      • Index.cshtml (Crear)
    • Shared
      • _Layout.cshtml (Crear)

Haciendo los cambios y creaciones de archivo podremos hacer las pruebas correspondientes de nuestro Administrador de WebHook, así que vamos a dar una vista rápida de los archivos que vamos a copiar para estructurar el proyecto y después veremos los códigos de los archivos que vamos a crear.

Para comenzar descargaremos un template de bootstrap que ya se me está haciendo costumbre de aquí. A continuación muestro los archivos que encontramos una vez descargado nuestro template:

A continuación vamos a mover el contenido de font-awesome dentro de ella encontraremos la carpeta css y fonts, de las cuales sacaremos los contenidos y moveremos lo del css a la carpeta raiz css y lo de la carpeta fonts a la carpeta fonts, quedando de la siguiente manera en nuestra solución:

Después de esto agregaremos a la solución las carpetas, para no hacer más largo este post, me dedicaré a la muestra de los archivos que se deben crear, como ya dije antes si quieren saber más de como se usa el MVC pueden hacerlo de esta liga.

WebApiConfig.cs


public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
var appXmlType = config.Formatters.XmlFormatter.SupportedMediaTypes.FirstOrDefault(t => t.MediaType == "application/xml");
config.Formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType);

config.MapHttpAttributeRoutes();

config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "service/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}

Como podrás darte cuenta agregamos 2 lineas de configuración para poder trabajar exclusivamente en JSON.

HomeController.cs


public class HomeController : Controller
{
// GET: Home
public ActionResult Index()
{
string SourceView = "Index";
if (this.Request.Params["Source"] != null)
{
SourceView = this.Request.Params["Source"];
}
return View(SourceView);
}
}

En el código anterior podemos ver nuestro controller Home, el cual nos permite acceder a todas las vistas que serán utilizados más adelante.

Result.cs


public class Result
{
public bool success { get; set; }
public string AdditionalData { get; set; }
}

Esta clase, nos permite los retornos que usaremos en los servicios del WebApi

ApplicationController.cs


public class ApplicationController : ApiController
{

public GetApplicationsResponse get()
{
GetApplicationsResponse response = new GetApplicationsResponse();
GetApplicationRequest request = new GetApplicationRequest();
WebHookManagerClient servicio = new WebHookManagerClient();
response = servicio.GetApplications(request);
return response;
}

public GetApplicationResponse get(string ApplicationName)
{
GetApplicationResponse response = new GetApplicationResponse();
GetApplicationRequest request = new GetApplicationRequest();
request.ApplicationName = ApplicationName;
WebHookManagerClient servicio = new WebHookManagerClient();
response = servicio.GetApplication(request);
return response;
}

[HttpPost]
public Result post(AddAplicationRequest request)
{
Result result = new Result();
AddApplicationResponse response = new AddApplicationResponse();
WebHookManagerClient servicio = new WebHookManagerClient();
response = servicio.AddApplication(request);
result.success = true;
result.AdditionalData = JsonConvert.SerializeObject(response);
return result;
}

[HttpPut]
public Result put(UpdateApplicationRequest request)
{
Result result = new Result();
UpdateApplicationResponse response = new UpdateApplicationResponse();
WebHookManagerClient servicio = new WebHookManagerClient();
response = servicio.UpdateApplication(request);
result.success = true;
result.AdditionalData = JsonConvert.SerializeObject(response);
return result;
}

[HttpDelete]
public Result Delete(int CveApplication)
{
Result result = new Result();
DeleteApplicationResponse response = new DeleteApplicationResponse();
DeleteApplicationRequest request = new DeleteApplicationRequest();
request.CveApplication = CveApplication;
WebHookManagerClient servicio = new WebHookManagerClient();
response = servicio.DeleteApplication(request);
result.success = true;
result.AdditionalData = JsonConvert.SerializeObject(response);
return result;
}
}

Como podemos ver el codigo anterior es parte del servicio del WebApi, que nos permite crear servicios GET y POST, PUT que nos retornan directamente JSON, en este caso es para el servicio de aplicaciones

ResourceController.cs


public class ResourceController : ApiController
{
public GetMessagesResponse get(string ApplicationName)
{
GetMessagesResponse response = new GetMessagesResponse();
GetMessageRequest request = new GetMessageRequest();
request.ApplicationName = ApplicationName;
WebHookManagerClient servicio = new WebHookManagerClient();
response = servicio.GetMessages(request);
return response;
}

[HttpPost]
public Result post(AddMessageRequest request)
{
Result result = new Result();
AddMessageResponse response = new AddMessageResponse();
WebHookManagerClient servicio = new WebHookManagerClient();
response = servicio.AddMessage(request);
result.success = true;
result.AdditionalData = JsonConvert.SerializeObject(response);
return result;
}

[HttpPut]
public Result put(UpdateMessageRequest request)
{
Result result = new Result();
UpdateMessageResponse response = new UpdateMessageResponse();
WebHookManagerClient servicio = new WebHookManagerClient();
response = servicio.UpdateMessage(request);
result.success = true;
result.AdditionalData = JsonConvert.SerializeObject(response);
return result;
}

[HttpDelete]
public Result Delete(DeleteMessageRequest request)
{
Result result = new Result();
DeleteMessageResponse response = new DeleteMessageResponse();
WebHookManagerClient servicio = new WebHookManagerClient();
response = servicio.DeleteMessage(request);
result.success = true;
result.AdditionalData = JsonConvert.SerializeObject(response);
return result;
}
}

Mismo caso pero para la sección de Recursos o mensajes. aquí van a encontrar un detalle, necesitan las clases del servicio esto lo podemos hacer agregando una referencia como vemos en la siguiente imagen:

Despues de esto nos mostrará una ventana como la siguiente:

En mi caso se la dirección por que anteriormente copie la dirección en la que me compila el servicio, que se realizo en la parte 1

A continuación vamos a presentar las vistas

AddApplication.cshtml


@{
ViewBag.Title = "Application";
}
<script src="~/js/DataList/Application.js"></script>
<script src="~/js/Controls/CustomControls.js"></script>
<div class="container-fluid">
<!-- Page Heading -->
<div class="row">
<div class="col-lg-12">
<h1 class="page-header">
@ViewBag.Title <small>Add/Edit Applications</small>
</h1>
<ol class="breadcrumb">
<li class="active">
<i class="fa fa-dashboard"></i> @ViewBag.Title
</li>
</ol>
</div>
</div>
<div class="row">
<div class="panel-body">
<ul class="nav nav-tabs">
<li class="active"><a href="#add" data-toggle="tab">Agregar Aplicación</a></li>
<li><a href="#edit" data-toggle="tab">Editar Aplicación</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane fade in active" id="add">
<div class="col-lg-12">
<br />
<div class="form-group col-lg-6">
<label class="col-md-5 control-label">Nombre Aplicación:</label>
<input id="tbxiAppName" type="text" class="form-control input-sm">
</div>
<div class="col-lg-12">
<button type="button" class="btn btn-sm" data-dismiss="modal" id="addApp">Agregar</button>
</div>
</div>
</div>
<div class="tab-pane fade in" id="edit">
<br />
<div class="col-lg-12">
<div class="form-group col-lg-6">
<label class="col-md-5 control-label">Seleccionar Aplicación:</label>
<div id="controlApp">
<select class="form-control input-sm" id="AppsInput">
<option>Seleccione una Opcion</option>
</select>
</div>
</div>
<div class="form-group col-lg-6">
<label class="col-md-5 control-label">Nombre Aplicación:</label>
<input id="tbxAppName" type="text" class="form-control input-sm">
</div>
<div class="form-group col-lg-6">
<label class="col-md-5 control-label">Llave de Api:</label>
<input id="tbxAppKey" type="text" class="form-control input-sm copyApiKey">
<button type="button" class="btn btn-sm" id="copyApiKey">Copiar LLave de Api</button>
</div>
<div class="form-group col-lg-6">
<label class="col-md-5 control-label">Llave Publica:</label>
<input id="tbxPublicKey" type="text" class="form-control input-sm copyPublicKey">
<button type="button" class="btn btn-sm" id="copyPublicKey">Copiar LLave Publica</button>
</div>
<div class="col-lg-12">
<button type="button" class="btn btn-sm" data-dismiss="modal" id="editApp">Editar</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
var applications = [];

applications = getApplications();
$('#controlApp').html(DropDownForm("Apps", parseApplications(applications), ""));

$('#addApp').on('click', function () {
request = {};
request.ApplicationName = $('#tbxiAppName').val();
addApplication(request);
applications = getApplications();
$('#controlApp').html(DropDownForm("Apps", parseApplications(applications), ""));
$('#AppsInput').on('change', function () {
SetData(applications, $('#AppsInput').val());
});
});

$('#editApp').on('click', function () {
request = {};
request.ApplicationName = $('#tbxAppName').val();
request.CveApplication = $('#AppsInput').val();
udpApplication(request);
});

$('#AppsInput').on('change', function () {
SetData(applications, $('#AppsInput').val());
});

function parseApplications(data) {
var NewData = [];
NewData.push({ "id": 0, "name": "Selecciona una Opcion" });
$.each(data, function (key, value) {
NewData.push({ "id": value.CveApplication, "name": value.ApplicationName });
});
return NewData;
}

function SetData(data, valueSelect) {
$.each(data, function (key, value) {
if (parseInt(valueSelect) == value.CveApplication) {
$('#tbxAppName').val(value.ApplicationName);
$('#tbxAppKey').val(value.ApiKey);
$('#tbxAppKey').prop("disabled", true);
$('#tbxPublicKey').val(value.PublicKey);
$('#tbxPublicKey').prop("disabled", true);
}
});
}

$('#copyApiKey').on('click', function () {
$('#tbxAppKey').prop("disabled", false);
copy('.copyApiKey');
$('#tbxAppKey').prop("disabled", true);
});

$('#copyPublicKey').on('click', function () {
$('#tbxPublicKey').prop("disabled", false);
copy('.copyPublicKey');
$('#tbxPublicKey').prop("disabled", true);
});

function copy(id) {
var copyTextarea = document.querySelector(id);
copyTextarea.select();

try {
var successful = document.execCommand('copy');
var msg = successful ? 'successful' : 'unsuccessful';
console.log('Copying text command was ' + msg);
} catch (err) {
console.log('Oops, unable to copy');
}
}
</script>

AddResource.cshtml


@{
ViewBag.Title = "Resource";
}
<script src="~/js/DataList/Application.js"></script>
<script src="~/js/DataList/Resource.js"></script>
<script src="~/js/Controls/CustomControls.js"></script>
<div class="container-fluid">
<!-- Page Heading -->
<div class="row">
<div class="col-lg-12">
<h1 class="page-header">
@ViewBag.Title <small>Add/Edit Resources</small>
</h1>
<ol class="breadcrumb">
<li class="active">
<i class="fa fa-dashboard"></i> @ViewBag.Title
</li>
</ol>
</div>
</div>

<div class="row">
<div class="panel-body">
<ul class="nav nav-tabs">
<li class="active"><a href="#add" data-toggle="tab">Agregar Recurso</a></li>
<li><a href="#edit" data-toggle="tab">Editar Recurso</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane fade in active" id="add">
<div class="col-lg-12">
<br />
<div class="form-group col-lg-6">
<label class="col-md-5 control-label">Seleccionar Aplicación:</label>
<div id="controlApp">
<select class="form-control input-sm" id="AppsInput">
<option>Seleccione una Opcion</option>
</select>
</div>
</div>
<div class="form-group col-lg-6">
<label class="col-md-5 control-label">Nombre de Mensaje:</label>
<input id="tbxMessName" type="text" class="form-control input-sm">
</div>
<div class="form-group col-lg-6">
<label class="col-md-5 control-label">Datos de Entrada:</label>
<textarea id="tbxRequest" type="text" class="form-control input-sm" style="height:200px;"></textarea>
</div>
<div class="form-group col-lg-6">
<label class="col-md-5 control-label">Datos de Salida:</label>
<textarea id="tbxResponse" type="text" class="form-control input-sm" style="height:200px;"></textarea>
</div>
<div class="form-group col-lg-6">
<label class="col-md-5 control-label">Nombre Recurso:</label>
<input id="tbxResourceName" type="text" class="form-control input-sm">
</div>
<div class="col-lg-12">
<button type="button" class="btn btn-sm" data-dismiss="modal" id="addMess">Agregar</button>
</div>
</div>
</div>
<div class="tab-pane fade in" id="edit">
<br />
<div class="form-group col-lg-6">
<label class="col-md-5 control-label">Seleccionar Aplicación:</label>
<div id="controlIApp">
<select class="form-control input-sm" id="IAppsInput">
<option>Seleccione una Opcion</option>
</select>
</div>
</div>
<div class="form-group col-lg-6">
<label class="col-md-5 control-label">Seleccionar Mensaje:</label>
<div id="controlMess">
<select class="form-control input-sm" id="IMessInput">
<option>Seleccione una Opcion</option>
</select>
</div>
</div>
<div class="form-group col-lg-6">
<label class="col-md-5 control-label">Nombre de Mensaje:</label>
<input id="tbxIMessName" type="text" class="form-control input-sm">
</div>
<div class="form-group col-lg-6">
<label class="col-md-5 control-label">Nombre Recurso:</label>
<input id="tbxIResourceName" type="text" class="form-control input-sm">
</div>
<div class="form-group col-lg-6">
<label class="col-md-5 control-label">Datos de Entrada:</label>
<textarea id="tbxIRequest" type="text" class="form-control input-sm" style="height:200px;"></textarea>
</div>
<div class="form-group col-lg-6">
<label class="col-md-5 control-label">Datos de Salida:</label>
<textarea id="tbxIResponse" type="text" class="form-control input-sm" style="height:200px;"></textarea>
</div>
<div class="col-lg-12">
<button type="button" class="btn btn-sm" data-dismiss="modal" id="EditMess">Editar</button>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
var applications = [];
var messages = [];

applications = getApplications();
$('#controlApp').html(DropDownForm("Apps", parseApplications(applications), ""));
$('#controlIApp').html(DropDownForm("IApps", parseApplications(applications), ""));

$('#IAppsInput').on('change', function () {
messages = getResources($('#IAppsInput option:selected').text());
$('#controlMess').html(DropDownForm("IMess", parseMessages(messages), ""))
$('#IMessInput').on('change', function () {
SetData(messages, $('#IMessInput').val());
});
});

$('#addMess').on('click', function () {
request = {};
request.CveApplication = $('#AppsInput').val();
request.MessageName = $('#tbxMessName').val();
request.ResourceName = $('#tbxResourceName').val();
request.RequestData = $('#tbxRequest').val();
request.ResponseData = $('#tbxResponse').val();
addResource(request);
clearFormAddMess();
});

$('#EditMess').on('click', function () {
request = {};
request.CveApplication = $('#IAppsInput').val();
request.CveMessage = $('#IMessInput').val();
request.MessageName = $('#tbxIMessName').val();
request.ResourceName = $('#tbxIResourceName').val();
request.RequestData = $('#tbxIRequest').val();
request.ResponseData = $('#tbxIResponse').val();
udpResource(request);
});

function clearFormAddMess() {
$('#AppsInput').val('0');
$('#tbxMessName').val('');
$('#tbxResourceName').val('');
$('#tbxRequest').val('');
$('#tbxResponse').val('');
}

function SetData(data, valueSelect) {
$.each(data, function (key, value) {
if (parseInt(valueSelect) == value.CveMessage) {
$('#tbxIMessName').val(value.MessageName);
$('#tbxIResourceName').val(value.ResourceName);
$('#tbxIRequest').val(value.RequestData);
$('#tbxIResponse').val(value.ResponseData);
}
});
}

function parseMessages(data) {
var NewData = [];
NewData.push({ "id": 0, "name": "Selecciona una Opcion" });
$.each(data, function (key, value) {
NewData.push({ "id": value.CveMessage, "name": value.MessageName });
});
return NewData;
}

function parseApplications(data) {
var NewData = [];
NewData.push({ "id": 0, "name": "Selecciona una Opcion" });
$.each(data, function (key, value) {
NewData.push({ "id": value.CveApplication, "name": value.ApplicationName });
});
return NewData;
}
</script>

_Layout.cshtml


<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<title>@ViewBag.Title</title>
<link rel="stylesheet" href="~/css/bootstrap.css" type="text/css" />
<link rel="stylesheet" href="~/css/sb-admin.css" type="text/css" />
<link rel="stylesheet" href="~/css/font-awesome.css" type="text/css" />
<script src="~/js/jquery.js"></script>
<script src="~/js/bootstrap.js"></script>

<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
<div id="wrapper">
<!-- Navigation -->
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="index.html">SB Admin</a>
</div>
<!-- Top Menu Items -->
<ul class="nav navbar-right top-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-envelope"></i> <b class="caret"></b></a>
<ul class="dropdown-menu message-dropdown">
<li class="message-preview">
<a href="#">
<div class="media">
<span class="pull-left">
<img class="media-object" src="http://placehold.it/50x50" alt="">
</span>
<div class="media-body">
<h5 class="media-heading">
<strong>John Smith</strong>
</h5>
<p class="small text-muted"><i class="fa fa-clock-o"></i> Yesterday at 4:32 PM</p>
<p>Lorem ipsum dolor sit amet, consectetur...</p>
</div>
</div>
</a>
</li>
<li class="message-preview">
<a href="#">
<div class="media">
<span class="pull-left">
<img class="media-object" src="http://placehold.it/50x50" alt="">
</span>
<div class="media-body">
<h5 class="media-heading">
<strong>John Smith</strong>
</h5>
<p class="small text-muted"><i class="fa fa-clock-o"></i> Yesterday at 4:32 PM</p>
<p>Lorem ipsum dolor sit amet, consectetur...</p>
</div>
</div>
</a>
</li>
<li class="message-preview">
<a href="#">
<div class="media">
<span class="pull-left">
<img class="media-object" src="http://placehold.it/50x50" alt="">
</span>
<div class="media-body">
<h5 class="media-heading">
<strong>John Smith</strong>
</h5>
<p class="small text-muted"><i class="fa fa-clock-o"></i> Yesterday at 4:32 PM</p>
<p>Lorem ipsum dolor sit amet, consectetur...</p>
</div>
</div>
</a>
</li>
<li class="message-footer">
<a href="#">Read All New Messages</a>
</li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-bell"></i> <b class="caret"></b></a>
<ul class="dropdown-menu alert-dropdown">
<li>
<a href="#">Alert Name <span class="label label-default">Alert Badge</span></a>
</li>
<li>
<a href="#">Alert Name <span class="label label-primary">Alert Badge</span></a>
</li>
<li>
<a href="#">Alert Name <span class="label label-success">Alert Badge</span></a>
</li>
<li>
<a href="#">Alert Name <span class="label label-info">Alert Badge</span></a>
</li>
<li>
<a href="#">Alert Name <span class="label label-warning">Alert Badge</span></a>
</li>
<li>
<a href="#">Alert Name <span class="label label-danger">Alert Badge</span></a>
</li>
<li class="divider"></li>
<li>
<a href="#">View All</a>
</li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-user"></i> John Smith <b class="caret"></b></a>
<ul class="dropdown-menu">
<li>
<a href="#"><i class="fa fa-fw fa-user"></i> Profile</a>
</li>
<li>
<a href="#"><i class="fa fa-fw fa-envelope"></i> Inbox</a>
</li>
<li>
<a href="#"><i class="fa fa-fw fa-gear"></i> Settings</a>
</li>
<li class="divider"></li>
<li>
<a href="#"><i class="fa fa-fw fa-power-off"></i> Log Out</a>
</li>
</ul>
</li>
</ul>
<!-- Sidebar Menu Items - These collapse to the responsive navigation menu on small screens -->
<div class="collapse navbar-collapse navbar-ex1-collapse">
<ul class="nav navbar-nav side-nav">
<li class="active">
<a href="@Url.Content("~/Home")"><i class="fa fa-fw fa-dashboard"></i> Dashboard</a>
</li>
<li>
<a href="@Url.Content("~/Home?Source=AddApplication")"><i class="fa fa-fw fa-bar-chart-o"></i> Agregar Aplicación</a>
</li>
<li>
<a href="@Url.Content("~/Home?Source=AddResource")"><i class="fa fa-fw fa-table"></i> Agregar Recurso</a>
</li>
</ul>
</div>
<!-- /.navbar-collapse -->
</nav>
<div id="page-wrapper">
@RenderBody()
</div>
<!-- /#page-wrapper -->
</div>
<a data-toggle="modal" href="#modalDefault" style="display: none;" id="WindowModal">Modal - Default</a>
<div class="modal fade" id="modalDefault" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="windowTitle"></h4>
</div>
<div class="modal-body" id="windowMessage">
</div>
<div class="modal-footer">
<button type="button" style="display: none;" class="btn btn-sm" data-dismiss="modal" id="messageclose">Aceptar</button>
<button type="button" class="btn btn-sm" data-dismiss="modal">Cerrar</button>
</div>
</div>
</div>
</div>
<!-- /#wrapper -->
</body>
</html>

Luego del monton de codigo que se ha escrito vamos a ver como quedaron nuestras vistas, ya funcionales, si seguimos todos los pasos del código anterior:

En la imagen anterior podemos ver como quedo nuestra pagina en la pestaña de Agregar Aplicación.

En la imagen anterior vemos como al seleccionar una aplicación, nos muestra el Nombre de la Aplicación, nos da una Llave de Api y una Llave Publica para cifrar algún dato ya veremos como lo usaremos en el WebHook en la Parte 3 de este articulo, veamos como se ven las secciones de Recursos.

Esta parte, es la más divertida e interesante, dado que creamos un mensaje que se ancla a una aplicación, este mensaje tiene Información de entrada e Información de Salida, aquí quiero despejar un poco esta información, la información de entrada y de salida no es otra cosa que una clase en C# que va a permitir validar la estructura del JSON que sea enviado al WebHook, así como la información de Salida, como podemos ver en la siguiente imagen de la pagina de edición:

Con esto tenemos el Manager Finalizado para efectos de jugar con nuestro WebHook, con esto se finaliza la segunda parte de esta entrega les dejo la liga de descarga y nos vemos en la próxima entrega donde crearemos el WebHook la parte que se va a dedicar a recibir datos y a obtenerlos para conectar otras aplicaciones.

(Download)

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)

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

A %d blogueros les gusta esto: