C# WinServiceBasic1 v1.1.0: Servicio con servidor HTTP

El servicio que he programado hasta ahora no ofrece ninguna funcionalidad, uno de los usos más habituales es ofrecer un puerto abierto a la escucha para comunicarse con otras aplicaciones en red que ejercen de clientes. En vez de inventar un nuevo protocolo vamos a usar uno reconocido, usando la clase HttpListener vamos a crear un servidor HTTP muy sencillo (espacio de nombres “using System.Net”);

Proyecto en GitHub (v1.1.0)

Clase WebServer

Vamos a crear una clase WebServer que agrupa todas las funcionalidades de nuestro servidor Web. Solo contiene dos atributos privados:

  • _listener: Una instancia de la clase HttpListener.
  • _responderMethod: El capturador de evento cuando un cliente se conecta.

Los métodos que ofrece son tres:

  • WebServer: El constructor de la clase, recibe la URL donde se pondrá a la escucha (por ejemplo “http://localhost:8080/test/” y la función externa a la clase que genera la respuesta al cliente (_responderMethod).  La parte más interesante del constructor es la llamada a _listener.Start().
  • Run: Este método no acepta parámetros. Emplea el método IsListening de HttpListener para determinar si se ha iniciado. La clase  HttpListenerContext proporciona acceso a los objetos de solicitud y respuesta utilizados por la clase HttpListener.  Llama al método interno que apunta a la función que captura la solicitud cliente “_responderMethod(ctx.Request);” y con la respuesta la redirecciona al cliente HTTP.
  • Stop: Este método es muy simple, llama a la Stop y Close de HttpListener.

Ejecución

Ejecutamos e introducimos la siguiente URL en nuestro navegador para ver el resultado: http://localhost:8080/test/.

Posts relacionados

Referencias Externas

 

 

C# WinServiceBasic1 v1.0.1: Añadimos algunas mejoras al servicio

Después de experimentar un poco con el servicio (C# WinServiceBasic1: Crear un servicio básico en Windows) he prescindido del control de VS que permite instalar el servicio y hacerlo yo mismo a mano con scripts PS (PowerShell), los he guardado en la carpeta Debug y permiten instalar / desinstalar / arrancar / parar / ver estado del servicio.

Proyecto WinServiceBasic1 en GitHub.

WinServiceBasic1_install.ps1

Utilizo el comando New-Service para instalar el servicio, el script previamente copia el ejecutable en C:\bin y borra el servicio si ya existiese previamente.

WinServiceBasic1_delete.ps1

WinServiceBasic1_start.ps1

Start-Service -Name "MyNewService"

WinServiceBasic1_stop.ps1

Stop-Service -Name "MyNewService"

WinServiceBasic1_view.ps1

Get-WmiObject win32_service -Filter "name='MyNewService'"

También he realizado modificaciones en las propiedades del objeto eventLog1 (clase EventLog para escribir logs que se visualizan en el visor de eventos de Windows), es necesario especificar la propiedad Source, normalmente se define como el nombre de la aplicación (“MyNewService” en este caso).

Ajusto algunos parámetros del objeto “MyNewService” (heredado de la clase ServiceBase) para modificar el comportamiento del servicio. La propiedad AutoLog a “False” para que la aplicación no genere eventos por su cuenta.

Además he sobrecargado el método WriteEntry para ilustrar como generar diferentes tipos de eventos (información, error,…):

Ahora los eventos del visor de eventos de Windows proporcionan más información:

Después de probar un rato con EventLog he instalado NLog para generar logs con más flexibilidad (ver “NLog: Sistema de logging gratuito y de código abierto para .Net“)

Referencias externas

 

NLog1 C#: Configuración versión de la aplicación con App.Config

Voy a continuar con  NLog: Sistema de logging gratuito y de código abierto para .Net.

Proyecto en GitHub NLog1.

He añadido algunas mejoras sin relación con el propio NLog,  Algo esencial para el control del proyecto es la gestión de versiones para controlar los cambios realizados (yo sigo este esquema de versionado siempre Semantic Versioning 2.0.0). El fichero App.config del proyecto en VS2012 con sintaxis XML es el candidato ideal para alojar la información de la versión del proyecto añadiendo a mano un nuevo bloque “<appSettings>” y dentro una etiqueta con clave valor :

Otra forma de definir la versión si queremos tratarla posteriormente en nuestra aplicación es definir 3 parámetros clave valor para X.Y.Z de la versión (MAJOR.MINOR.PATCH).

Poder acceder a la información de la versión desde el código de nuestra aplicación es esencial, sobre todo si queremos condicionar la ejecución de un bloque de código especial dependiendo de la versión por ejemplo. Para ello debemos añadir la referencia “System.Configuration” al proyecto y el espacio de nombres a nuestra aplicación:

using System.Configuration;

Para acceder a la variable desde nuestro programa es muy sencillo:

Como conclusión, podemos añadir la información que necesitemos, algunos usos muy comunes es incluir información de conexión a la base de datos o configuración del usuario.

Referencias externas:

 

 

 

C# WinServiceBasic1: Crear un servicio básico en Windows

Los servicios (o demonio en Linux) son normalmente aplicaciones que corren de fondo (sin interfaz) y ofrecen como su nombre lo dice un servicio a otras aplicaciones, es muy común por ejemplo para implementar  protocolos de comunicaciones en red con otras máquinas mediante sockets TCP/UDP actuando como un servidor.  Otro uso muy común es como servidor de acceso a datos alojados en una máquina central.

Código del ejercicio en GitHub

Crear un servicio en VS 2012

En Visual Studio ahora te facilita crear aplicaciones de este tipo ya que ofrece un tipo de proyecto especifico “Windows Service”:

Visual Studio añade automáticamente una clase nueva Service1.cs heredada de System.ServiceProcess.ServiceBase.

Cambiamos el nombre de “Service1” por “MyNewService” buscando y reemplazando en todo el proyecto,

Añadir eventos de Windows

En la vista de diseño de Service1.cs arrastramos y añadimos un control “EventLog

VS le asigna un nombre eventLog1 a la nueva clase, podemos cambiar el nombre de la clase en las propiedades.

Ahora ya podemos añadir eventos para notificar de arranque / parada del servicio.

Instalar el servicio

Para crear un instalador usamos el botón derecho del ratón en la vista diseño Service1.cs sobre el fondo y el menú contextual seleccionamos “Add Installer”, revisamos las propiedades de “serviceInstaller1”, ServiceName debe estar definido como “MyNewService”, es importante elegir el tipo de arranque (lo dejamos en manual por el momento).

Para instalar el servicio hay que hacerlo mediante el interprete de comandos “Developer Command Prompt” incluido en VS.

Ahora vamos a la carpeta de salida donde está WinServiceBasic1.exe y ejecutamos:

installutil.exe WinServiceBasic1.exe

 

MVVMBasicDemo1: Patrón de diseño MVVM con aplicaciones basadas en WPF Resultado de imagen de WPF (Windows Presentation Foundation)

WPF (Windows Presentation Foundation) es una tecnología de MS para diseñar interfaces de usuario tomando ideas de técnicas Web y aplicaciones Windows. WPF permite definir las ventanas en ficheros de texto plano que contienen un lenguaje de etiquetado llamado XAML (basado en XML).

El proyecto completo se puede descargar en GitHub: https://github.com/ikerlandajuela/MVVMBasicDemo1.

 

De está forma separa las capas con la lógica de la aplicación y la capa de presentación donde interactuamos con el usuario. Está separación tan nítida propicia adoptar patrones de diseño como MVVM (Model-View-ViewModel ) para desarrollar aplicaciones complejas.

WPF emplea data-binding para conectar  la UI (User Interface) con la lógica de la aplicación, gracias a ello cuando un origen de dato cambia su valor  el cambio se refleja de forma automática en la UI, también si a la inversa la presentación del dato cambia en la UI los cambios tienen reflejo en la lógica de negocio interna (el contenido de un TextBox modificado por el usuario por ejemplo) .

El modelo MVVM estructura la aplicación en 3 capas:

  • Model: Mantiene los orígenes de datos.
  • ViewModel: Actúa de conector entre las otras dos capas.
  • View: Mantiene la información de la UI.

MVVMBasicDemo1: Primera aplicación

Este es el ejemplo mas sencillo que he encontrado para entender los fundamentos básicos de MVVM con WPF.  La única ventana de la aplicación muestra el nombre y apellido (cada uno en su respectivo TextBox editable) de una lista de estudiantes, junto a los dos TexBox (FirstName y LastName) hay un TextBlock con el nombre y apellidos unidos (FullName), cuando el usuario modifica el nombre o apellido de un estudiante inmediatamente la modificación se actualiza de forma automática en el TexBox (de lectura). Los controles con la lista de usuarios se crean de forma dinámica dependiendo de la lista de estudiantes que proporcionemos en la capa ViewModel.

 

Creamos un nuevo proyecto de tipo “WPF Application” en VS (Visual Studio). Creamos 3 carpetas (Model, ViewModel, Views)

Model

Añadimos una nueva clase “StudentModel” a la carpeta “Model”.

Para poder usar las técnicas de programación que se describen a continuación debemos añadir al comienzo del fichero un nuevo espacio de nombres System.ComponentModel.

using System.ComponentModel;

La clase base ‘INotifyPropertyChanged‘ notifica que el valor de una propiedad ha cambiado, genera un evento PropertyChanged cuando cambia el valor de un miembro de la clase. Debemos definir un atributo de tipo PropertyChangedEventHandler como manejador del evento para notificar a las clases cuando cambia una propiedad en alguno de los atributos editables (firstName,lastName).

Los atributos privados de la clase son el nombre y apellido, controlamos el acceso a sus propiedades con los descriptores de acceso get/set (ver más sobre este tema en “C# class LibroCalificaciones: Acceso a propiedades con descriptores de acceso get / set“), de esta forma controlamos cuando cambia el valor y lanzamos la función RaisePropertyChanged que a su vez llama a al manejador PropertyChanged. El resto de código y comprobaciones que realiza con fáciles de comprender. Si por ejemplo cambia el nombre genera un evento adicional para que en coherencia cambie el FullName.


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

using System.ComponentModel;

namespace MVVMBasicDemo1.Model
{
public class StudentModel { }

public class Student : INotifyPropertyChanged
{
private string firstName;
private string lastName;

public string FirstName
{
get
{
return firstName;
}

set
{
if (firstName != value)
{
firstName = value;
RaisePropertyChanged("FirstName");
RaisePropertyChanged("FullName");
}
}
}

public string LastName
{
get { return lastName; }

set
{
if (lastName != value)
{
lastName = value;
RaisePropertyChanged("LastName");
RaisePropertyChanged("FullName");
}
}
}

public string FullName
{
get
{
return firstName + " " + lastName;
}
}

public event PropertyChangedEventHandler PropertyChanged;

private void RaisePropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
}

 

ViewModel

En la capa intermedia ViewModel añadimos la clase StudentViewModel.cs. La clase representa el origen de datos de donde se nutre con la lista de alumnos.

La clase StudentViewModel contiene un solo atributo público de tipo Clase ObservableCollection<T>  esta clase representa una colección de datos dinámicos de la clase Student definida en el modelo de datos. El método público LoadStudents() carga la colección de datos con la información de los estudiantes.


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

using MVVMBasicDemo1.Model;
using System.Collections.ObjectModel;

namespace MVVMBasicDemo1.ViewModel
{
class StudentViewModel
{
public ObservableCollection<Student> Students
{
get;
set;
}

public void LoadStudents()
{
ObservableCollection<Student> students = new ObservableCollection<Student>();

students.Add(new Student { FirstName = "Mark", LastName = "Allain" });
students.Add(new Student { FirstName = "Allen", LastName = "Brown" });
students.Add(new Student { FirstName = "Linda", LastName = "Hamerski" });
students.Add(new Student { FirstName = "Iker", LastName = "Landajuela" });

Students = students;
}
}
}

Views

Lo primero hemos movido el archivo MainWindow.xaml a la carpeta Views donde debería estar.

App.xaml es el punto de entrada cuando arranca la aplicación, VS lo genera de forma automática cuando creamos el proyecto de tipo WPF.

En este fichero debemos modificar el atributo StartupUri para que apunte a  MainWindow.xaml dentro de la carpeta Views. Si quisiéramos mostrar otra ventana en el inicio de la aplicación la definimos en este campo.

App.xaml incluye también por detrás código en el fichero App.xaml.cs (clase Application).

 

MainWindow.xaml

Este fichero contiene la definición de la ventana de nuestra aplicación usando XAML y la etiqueta Window. Definimos algunas propiedades básicas como el tamaño de ventana y el título.

Dentro de Window definimos un control Grid, este control define una rejilla donde alojamos controles en las casillas (Ver The Grid Control). En nuestro caso sólo contiene una vista con la etiqueta views que se encarga de cargar nuestra vista StudentView.xaml.

StudentView.xaml

He añadido un nuevo elemento de tipo “User Control (WPF)” al proyecto en la carpeta Views.  El archivo StudentView.xaml contiene los controles para visualizar la lista de estudiantes, la etiqueta principal UserControl contiene un Grid, dentro un control StackPanel (Ver “WPF StackPanel“).

El control ItemsControl es donde opera la magia del data-binding, se asocia a la propiedad Students (colección de clases Student) definida en la clase StudentViewModel. Dentro de ItemsControl se define ItemsControl.ItemTemplate que contiene el modelo de visualización de un estudiante.

Cada clase TextBox se asocia a un atributo de la clase student usando la propiedad Text.


<UserControl x:Class = "MVVMBasicDemo1.Views.StudentView"
xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc = "http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d = "http://schemas.microsoft.com/expression/blend/2008"
xmlns:local = "clr-namespace:MVVMBasicDemo1.Views"
mc:Ignorable = "d"
d:DesignHeight = "300" d:DesignWidth = "300">

<Grid>
<StackPanel HorizontalAlignment = "Left">

<ItemsControl ItemsSource = "{Binding Path = Students}">

<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation = "Horizontal">
<TextBox Text = "{Binding Path = FirstName, Mode = TwoWay}"
Width = "100" Margin = "3 5 3 5"/>

<TextBox Text = "{Binding Path = LastName, Mode = TwoWay}"
Width = "100" Margin = "0 5 3 5"/>

<TextBlock Text = "{Binding Path = FullName, Mode = OneWay}"
Margin = "0 5 3 5"/>

</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>

</ItemsControl>

</StackPanel>
</Grid>

</UserControl>

 

Enlaces externos

NLog: Sistema de logging gratuito y de código abierto para .Net

Siempre que comienzo proyectos de envergadura antes me pongo al día de las utilidades básicas que necesita mi aplicación, un sistema de logging para mi es esencial para seguir la ejecución del código, en lenguaje C otras veces he usado liblogger, es bastante básico pero cumple con mis propósitos, se integra fácilmente con el código principal y es capaz de escribir los logs en ficheros de texto plano en disco.

Para C# uso NLog, es muy flexible en su configuración, puede enviar los logs al clásico fichero, con innumerables opciones de configuración para dar nombre al fichero destino, rotar ficheros cuando alcanzan determinado tamaño y todo lo que nos ocurra. Pero a parte de trabajar con ficheros como toda la vida también puede enviar los logs a una base de datos, al sistema de eventos de windows o usando la red local a un servidor en otra máquina por ejemplo (ver apartado “Targets” en GitHub).

La instalación de NLog es muy sencilla usando el gestor de paquetes de Visual Studio, escribimos las siguientes líneas (previamente creamos un proyecto de consola):

PM> Install-Package NLog -Version 4.4.11

PM> Install-Package NLog.Config

NLog.Config es para poder configurar NLog para nuestra aplicación en un fichero XML, cuando lo instalamos añade automáticamente un fichero NLog.config al proyecto, borramos su contenido y copiamos las siguientes líneas:


<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true" > <!-- permite cambiar configuración sin parar ejecución! -->

<!-- crea subcarpeta por día por ejemplo logs/2017-07-23/ -->
<variable name="logDirectory" value="${basedir}/logs/${shortdate}"/>
<!-- Fichero con fecha Ejemplo logs/2010-06-08.log -->
<!-- <variable name="logDirectory" value="${basedir}/${shortdate}.log"/> -->

<targets>
<target xsi:type="NLogViewer"
name="viewer"
includeSourceInfo="true"
address="udp://127.0.0.1:9999"/>
<target xsi:type="ColoredConsole" name="colored_console"/>

<target
layout="${longdate} ${logger} ${message}"
name="file1" xsi:type="File" fileName="${logDirectory}/log.log"/>

</targets>

<rules>
<logger name="*"
minlevel="Trace"
writeTo="viewer,colored_console,file1" />
</rules>
</nlog>

No voy a entrar a describir en detalle todos los campos (para eso está la documentación oficial), lo que hacemos es definir 3 destinos para los logs.

El primero NLogViewer envía los logs a un visor externo en una dirección IP y un puerto determinado, yo lo he usado para enviar los logs a una aplicación gráfica llamada Sentinel. Las primeras pruebas que he realizado funciona de maravilla.

El siguiente destino es la consola coloreada el nivel de logs:

El tercero es el clásico fichero, he usado algunas propiedades para que se guarde en una carpeta con la fecha actual.

Por último la sección “rules” permite filtrar los logs de salida por tipo y que destinos se usan, en un momento dado tal vez queramos desactivar la salida por consola por ejemplo.

El parámetro “autoReload” a True permite que cambiemos el fichero de configuración sin necesidad de detener la aplicación.

Para realizar unas pruebas muy sencillas editamos Program.cs y lo dejamos así:

Un ejemplo del fichero NLog.config que me he topado por Internet incluso pemite enviar los logs a GMail. Me parece extremadamente útil para enviar errores fatales a los administradores de la aplicación que normalmente no estarán todo el día leyendo los logs esperando a que pase algo.

 

Por último debemos tomar en cuenta si queremos que el procesamiento sea asíncrono (para evitar bloqueos de nuestra aplicación  principal ,