[IT] Outlook.com con dominio propio

Hola,

Hace unos días q llevo intentando migrar a Outlook.com para probar que tal va.

He leído algunos POST sobre cómo conectar GMAIL a Outlook y así estar al día sobre lo que por allí recibimos desde una única interfaz. Si aún no sabes cómo hacerlo, puedes  ver este post de Pilar con video incluido.

El problema que algunos nos encontramos es que en todos los casos se usa nuestra dirección de Outlook como dirección de re-envío. Para los que usamos cuentas personales (dominios propios: odelvalle.com) para acceder a live, hacer esto  implica re-enviar todo el tráfico de nuestra cuenta GMail a nuestro servidor de correo.

Para evitar esto, intenté cambiar mi cuenta outlook pero me avisó que perdería la conexión de todos los dispositivos asociados a esa cuenta live (malo malo). Busqué si se podía crear una nueva cuenta pero eso implicaría tener 2 accesos a live, una con mi correo personal y la otra con la nueva cuenta outlook (esto tampoco me convence)

Dedicando un ratico cada mañana a buscar posibles soluciones, hoy encuentro una: :

Untitled

Crear un alias en outlook.com es la manera que tenemos de unir/asociar varias direcciones de correo electrónico a una sola. 

Untitled1

Al crear el Alias, Outlook.com te enviará un correo de verificación a tu cuenta personal (live) para asegurarse que el Alias está correctamente asociado.

Untitled2

y ya estamos listos. Podemos seguir los pasos que nos cuenta Pilar en su post y utilizar el alias de outlook como dirección de re-envío, de esta forma los correos de GMAIL no pasarán por nuestro pequeño e indefenso servidor e irán directamente al grande de Outlook.com. Sonrisa

Salu2

Publicado en General | 1 comentario

POO–Responsabilidades

“Hay una diferencia entre programar orientado a objetos y pensar orientado a objetos”

CNX-B4WDebatiendo hoy en el trabajo sobre la funcionalidad que debería tener una toolsbox, salió uno de esos temas que me encantan. ¿Dónde debe estar el código asociado a un clic de un botón?

Responsabilidades

Mucho más allá de si la pregunta se responde aplicando un Chain of Responsibility Pattern, dentro se esconde uno de los pilares de la POO, la Especialización. Los objetos mientras mayor sea su especialización, mejor podremos definir su comportamiento.

¿Qué quiere decir esto? Pues que la responsabilidad de cada objeto debe ser solo, la que permita cumplir los objetivos por el cual fue creado.

Un avión vuela (por suerte). Nosotros desde fuera vemos un conjunto, pero dentro del avión hay miles de objetos que tienen una responsabilidad específica y que entre todos, hace que el avión vuele. Creo que excepto en las teles de la antigua URSS (le quitabas piezas y seguían funcionando), todo objeto tiene una responsabilidad que permite lograr una funcionalidad superior.

¿Que es una toolsbox? pues un panel de botones y nada más. Cuando compramos un panel de botones este no incluye para qué se usa cada botón o qué hace cada botón. Si compramos un interruptor eléctrico, este por si solo no apaga ni enciende la luz, incluso, puede llegar a realizar funciones explosivas si conectas incorrectamente los cables (que me pregunten a mi con 11 años la que lié).

Nuestra toolsbox, tiene botones (como casi todas) y el objetivo que cumplirá cada botón debería ser independiente a la misma. Un botón “dentro de la toolsbox” tiene estados cuyos cambios son notificados al exterior para quien quiera hacer algo que lo haga.

Lo mismo pasa con nuestro avión, mueves los mandos de vuelo y giras, esto no ocurre porque las aletas y el mando sean un único e inseparable objeto, sino porque el mando cambia de estado y notifica a todo aquel que pueda estar interesado para que haga algo. Las aletas y seguramente muchos objetos más dentro de un avión, están escuchando continuamente para saber si los mandos de vuelo han cambiado de posición(estado).

Vean que para definir cual debe ser la responsabilidad de mi  toolsbox, me he basado en todo momento en ejemplos de la vida real.

En el evento que di sobre POO en SecondNug (aquí tenéis los apuntes) cuando se analizaba la crisis del software, se definió como objetivo de la programación orienta a objetos el acercamiento del ordenador al problema y no del problema al ordenador, lo cual significa que debemos intentar en todo momento modelar los problemas existentes en la vida real analizándolo como lo que son, cosas que ocurren en la realidad. Esto era y seguramente aún es, el paradigma de la POO.

El otro día leía un artículo de Eduard sobre knockout y había una línea de texto que decía: “el código javascript es totalmente ignorante del DOM y trabaja tan solo con el viewmodel. Separación de responsabilidades.” ¡Atentos! que en este artículo se habla de separar la responsabilidad entre el HTML y el Javascript… (si digo yo esto en una entrevista de trabajo hace unos años, ya me dirán ustedes que hubieran pensado de mi)

Seguramente hoy por hoy aún queda mucho por hacer, pero da gusto como cada vez más intentamos llevar a la más mínima expresión la separación de responsabilidades. Mi opinión es que mientras menor sea la responsabilidad de un objeto, siempre que cumpla su objetivo, mejor y más fácil será de mantener.

Por cierto, para no dejarles sin saber, la toolsbox terminó con N botones que no saben lo que hacen pero que notifican a quien pueda interesar los estados de onBeforeClick y onClick. (espero no ser yo quien tenga que conectarla) Sonrisa

Salu2

Publicado en General | Deja un comentario

Mensajes de browser a browser (Web Socket)

ws_tornadoHe estado trabajando últimamente en un proyecto en el que necesitaba comunicación entre instancias distintas de browsers en tiempos lo más real posible.

La solución se desprende, Web Socket con HTML5 Sonrisa

La lógica que necesitamos en el servidor es simple, transmitir un mensaje que viene de un cliente, al resto de los clientes conectados.

En la parte del servidor quiero la menor lógica posible, así podré reutilizarlo en distintos proyectos. Los mensajes van de clientes a clientes, por lo que la responsabilidad del servidor en este caso debe ser solamente la de comunicación.

Para implementar el servidor he usado Fleck, un Web Socket que ya existe para NET y el cual recomiendo muchísimo, un código claro y fácil de entender si te pica la curiosidad y quieres conocer internamente cómo funciona.

Server

De momento vamos a crear una aplicación de consola para hospedar a nuestro Socket Server.

class Program
{
    static void Main()
    {
        FleckLog.Level = LogLevel.Debug;

        var socketManager = new MessagesWebSocket();
        socketManager.Start();

        Console.ReadLine();

        socketManager.Close();
    }
}

La clase MessagesWebSocket tendría:

class MessagesWebSocket
{
    private readonly List<IWebSocketConnection> _allSockets;
    private readonly WebSocketServer _server;


    public MessagesWebSocket()
    {
        _allSockets = new List<IWebSocketConnection>();
        _server = new WebSocketServer(ConfigurationManager.AppSettings["socketServer"]);
    }

    public void Start()
    {
        _server.Start(socket =>
        {
            socket.OnOpen = () =>
            {
                Console.WriteLine("Open client connection!");
                _allSockets.Add(socket);
            };

            socket.OnClose = () => _allSockets.Remove(socket);
            socket.OnMessage = message => _allSockets.Where(s=> s != socket).ToList().ForEach(s => s.Send(message));
        });
    }

    public void Close()
    {
        _allSockets.ForEach(s => s.Close());
        _server.ListenerSocket.Close();
    }
}

Si has trabajado con Socket verás que no hay mucha diferencia a lo que se hacía hasta ahora, incluso, la clase WebSocketServer de Fleck usa internamente System.Net.Sockets.

Nuestra clase MessagesWebSocket tiene dos métodos, Start & Stop.

Start nos permite abrir la conexión y quedarnos a la escucha de cualquier petición de conexión de algún cliente. Cuando un cliente previamente conectado envía un mensaje al servidor, este simplemente toma el mensaje tal cual y lo envía al resto de clientes conectados.

El servidor no interviene en absoluto en la estructura del mensaje que se envía, justamente lo que necesitamos. Entender los mensajes que se envían los clientes será siempre responsabilidad de ellos.

Client

Si hablamos de clientes WEB, la moda en comunicación se llama JSON! Sonrisa Así que vamos a hacer que este sea el formato que usen los mensajes enviados entre clientes.

Para que todo el tema de web socket y json nos quede fuera de la lógica de cualquier página o proyecto, vamos a encapsular toda la funcionalidad dentro de un objeto en Javascript que nos permita enviar y recibir mensajes sin importarnos cómo.

var WebSocketHelper = function (server, onMessage, onError)
{
    var w = window;
    var connection;

    var reciveData = onMessage || false;
    var error = onError || false;

    // if user is running mozilla then use it's built-in WebSocket
    w.WebSocket = w.WebSocket || w.MozWebSocket;

    // open connection
    try
    {
        connection = new w.WebSocket(server);
    }
    catch (connErr)
    {
        if (error) error("Open connection error: " + connErr);
        return;
    }

    // only for debug propose
    connection.onopen = function ()
    {
        //alert('Connected.');
    };

    connection.onmessage = function (d)
    {
        var result = jQuery.parseJSON(d.data);
        if (reciveData) reciveData(result);

        return false;
    };

    connection.onerror = function (wsErr)
    {
        if (error) error("Connection error: " + wsErr);
        connection = null;
    };

    this.SendCommand = function (obj)
    {
        try
        {
            var jsonString = JSON.stringify(obj);
            connection.send(jsonString);
        }
        catch (sendErr)
        {
            if (error) error("Send message error: " + sendErr);
        }
    };
};

Vamos por parte.

1- Al objeto WebSocketHelper le pasamos tres parámetros: la dirección del servidor, una función (opcional) que se ejecutará cuando nos llegue un nuevo mensaje y otra función (también opcional) que se ejecutará si encontramos algún error.

2- Instanciamos un nuevo objeto WebSocket, y aquí entramos en las diferencias entre browsers de toda la vida. Firefox tiene un objeto MozWebSocket mientras Chrome e Internet Explorer (v10) usan WebSocket.

3- Luego de instanciado el socket, intentamos abrir la conexión con el servidor, si todo sale bien, estamos listos para enviar y recibir datos mediante el socket.

4- El evento onmessage es quien me permite capturar los mensajes que llegan desde el servidor. La función asociada a este evento toma el mensaje recibido y lo transforma a un objeto javascript usando jQuery.parseJSON. Al tener la información recibida como objeto javscript, se ejecuta la función que hemos pasado al WebSocketHelper pasando como parámetro el objeto recibido.

5- Para enviar un objeto al servidor el proceso es parecido. Se usa la función pública SendCommand y se pasa como parámetros el objeto a enviar. Esta función internamente convierte el objeto en string y lo envía mediante el socket al servidor.

¿Cómo funciona esto desde una página?

Primero que todo hay que llegar a un acuerdo entre clientes, podemos enviar cualquier objeto javascript mediante el servidor pero es necesario que los clientes sepan de que va el asunto, porque de lo contrario sería como pedir una cerveza y que de pronto te lleguen melones, quesos, tomates, gambas y sandias ( y lo peor es que intentes algo porque creas que son cervezas).

En mi caso he usado una estructura de objetos muy simple. Internamente cada objeto que envío tiene una propiedad llamada command que define el tipo de mensaje que es.

var commands = { stop: "stop", start: "start", game: "game", reset: "reset", gameover: "gameover" };

Inicializando el socket server y recibiendo datos desde el servidor:

var socket = new WebSocketHelper("ws://192.168.100.64:8181", function (cmd)
{
    switch (cmd.command)
    {
        case commands.gameover:

            // GAME Over
            break;

        case commands.start:
            
            alert("Welcome {0}. Are you ready?".format(cmd.player));
            break;

        default: throw new Error("Invalid Command on socket server.");

    }
});

Enviando mensajes:

socket.SendCommand(
{
    command: commands.start,
    player: 'Pepe el loco',
    device: 1,
    language: 'es'
});

y con esto ya tenemos una comunicación simple de browser a browser.

Más adelante veremos otro ejemplo en el que vamos a darle más responsabilidad al SocketServer enviando comandos recibidos desde una Kinect a clientes Web y ya de paso, convertiremos nuestro WebSocket en un servicio Windows.

Salu2

Publicado en General | Deja un comentario

Navigator para WP7 en MVVM

Otro tema que estaba pendiente…

Cuando empecé a estudiar y testear cosas de WP7 con MVVM, una de los detalles que más me molestaban era tener que navegar entre páginas usando el code behind.

Es verdad que desde el ViewModel puedes tener acceso al NavigationService, pero esto sería una muy mala práctica de cara a los test unitarios. Por otra parte, desde dónde puedo capturar los parámetros pasados a una vista es siempre el método OnNavigatedTo que tenemos en el Code Behind.

Una tarde escuchando una charla de Josue sobre WP7, comentó que se encontraba desarrollando una aplicación llamada WPControla. Soy de los que piensa que una de las mejores maneras de tomar buenas prácticas en los desarrollos es mirar mucho código, cualquiera, no importa de quien sea porque siempre te ayudan a comparar y sacar conclusiones sobre lo que se debe o no hacer. 

En la propia charla le pregunté a Josue si podríamos descargarnos el código de la aplicación y como siempre, nos dio acceso al repositorio. Winking smile

Mirando el código de esta aplicación descubrí algo que me llamó mucho la atención, dentro del namespace WPControla.Client.Services había una clase llamada NavigationService. Me imaginé que esta clase, al implementar una interfaz sería una solución a lo que tanto me había molestado hasta el momento, poder navegar entre Views desde el ViewModel sin afectar los test.

Me costó entender cómo funcionaba porque en el repo no está toda la funcionalidad aún implementada, así que le pregunté directamente a Josue si esto era lo que me imaginaba y cómo había pensado en resolver esto, su respuesta no se hizo esperar. Gracias Josue! Smile

Con permiso del Master, vamos a intentar explicar la solución que implementa  WPControla.

Todo parte de una clase llamada NavController en la cual se empieza definiendo un singleton para el acceso a una única instancia. También contamos con un Dictionary que nos permite registrar y referenciar las vistas mediante “alias”.

registeredViews.Add("Start", new Uri(@"/MainPage.xaml", UriKind.Relative));

En mi caso, cambié el Dictionary utilizado:

static Dictionary<String, Uri> registeredViews = new Dictionary<String, Uri>();

por:

private static readonly Dictionary<Pages, Uri> _registeredViews = new Dictionary<Pages, Uri>();

Pages es un enumerado, esto no implica ninguna mejora a la solución planteada por Josue, más bien es un gusto personal, no me gusta trabajar con string porque soy muy despistado Sad smile y siempre los escribo mal, así que me ahorro errores de ejecución usando enumerados.

La clase define  tres sobrecargas sobre el método NavigateTo. Una sobrecarga nos permite navegar a una vista sin pasar parámetros, en la segunda, podemos incorporar parámetros pasados por QueryString y en la tercera nos vamos a detener:

public void NavigateTo(String navigationTarget, Action<NavigationEventArgs> onNavigated)

Esta sobrecarga incluye un Action<NavigationEventArgs> como parámetro, el objetivo es poder tomar una acción en el momento en que se navegue de una vista a otra. Internamente la clase navController captura el evento Navigated de PhoneApplicationFrame y cuando este ocurre, ejecuta nuestra acción.

/// <summary>
/// Este evento se lanza cuando hemos navegado a una página.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void root_Navigated(object sender, NavigationEventArgs e)
{
    if (NavigationMethod != null)
    {
        NavigationMethod(e);
        NavigationMethod = null;
    }
}

Este método de la forma que está escrito, no nos permite capturar el Navigated cuando el NavigationMode es Back, o sea, cuando vamos de regreso. Para solucionarlo solo necesitamos eliminar la línea de código que asigna null al NavigationMethod y no olvidar desasignar el evento cuando regresemos, porque de lo contrario navegando hacia atrás y hacia adelante multiplicará la cantidad de veces que se ejecuta nuestra acción.

void RootNavigated(object sender, NavigationEventArgs e)
{
    if (_navigationMethod == null) return;

    _navigationMethod(e);

    if (e.NavigationMode != NavigationMode.Back) return;
    var rootFrame = (PhoneApplicationFrame)Application.Current.RootVisual;
        
    rootFrame.Navigated -= RootNavigated;
}

Otra particularidad de esta sobrecarga es que mediante el parámetro NavigationEventArgs tenemos acceso a la View que estamos navegando mediante la propiedad Content, a su vez, mediante la View tenemos acceso al DataContext que en una arquitectura MVVM sería el ViewModel. Esta estructura pudiera ser utilizada para inicializar objetos complejos cuando navegamos de una vista a otra (luego veremos un ejemplo).

Ya que estamos creando métodos de navegación, adicioné a esta clase un método que nos permita hacer Back por código.

public void NavigateToBack()
{
    var rootFrame = Application.Current.RootVisual as PhoneApplicationFrame;
    if (rootFrame == null || !rootFrame.CanGoBack) return;

    rootFrame.GoBack();
}

La magia para que todo esto se integre con nuestros ViewModels llega ahora:

Definimos una interfaz con los distintos métodos para la navegación que necesitamos en nuestra aplicación, hacemos que un servicio implemente dicha interfaz y lo usamos en el constructor de los distintos ViewModels.

Un servicio de navegación que utilice NavController quedaría como el siguiente:

public interface INavigatorService
{
    void GotoMvvmView1();
    void GotoMvvmView1(DataItem param);

    void GoBack();
}

public class NavigatorService : INavigatorService
{
    public void GotoMvvmView1()
    {
        NavController.Current.NavigateTo(NavController.Pages.MvvmView1);
    }

    public void GotoMvvmView1(DataItem param)
    {
        NavController.Current.NavigateTo(NavController.Pages.MvvmView1, 
            args =>
            {
                if (args.NavigationMode != NavigationMode.New) return;

                var view1Model = ((FrameworkElement) (args.Content)).DataContext as View1ViewModel;
                if (view1Model != null) view1Model.Initialize(param);
            });
    }

    public void GoBack()
    {
        NavController.Current.NavigateToBack();
    }
}

Observen como estamos ejecutando una acción en el momento en que llegamos a la vista a la que deseamos navegar. Esta acción accede al ViewModel e inicializa parámetros necesarios con los datos que se pasan al método GotoMvvmView1.

A mi este mecanismo no me gusta  (cuestión personal), no sé si será buena o mala práctica, pero me cuesta tener que acceder a métodos del ViewModel desde este punto.

Para solucionar este problema he incorporado a la clase NavController un Dicctionary para pasar datos entre vistas.

/// <summary>
/// Navigation Parameters 
/// </summary>
public static IDictionary<string, object> Parameters { get; set; }

Como NavNavigator es independiente de nuestros ViewModels mediante una interfaz, tenemos que adicionar a la misma la utilización del Dictionary. La interfaz vista anteriormente ahora nos quedaría así:

public interface INavigatorService
{
    IDictionary<string, object> Parameters { get; set; }

    void GotoMvvmView1();
    void GotoMvvmView1(DataItem param);

    void GoBack();
}

y su implementación sería…

public class NavigatorService : INavigatorService
{
    public IDictionary<string, object> Parameters
    {
        get { return NavController.Parameters; }
        set { NavController.Parameters = value; }
    }

    public void GotoMvvmView1()
    {
        NavController.Current.NavigateTo(NavController.Pages.MvvmView1);
    }

    public void GotoMvvmView1(DataItem param)
    {
        NavController.Current.NavigateTo(NavController.Pages.MvvmView1, 
            args =>
            {
                if (args.NavigationMode == NavigationMode.Back) return;

                NavController.Parameters = new Dictionary<string, object> { { "item", param} };
            });
    }

    public void GoBack()
    {
        NavController.Current.NavigateToBack();
    }
}

Después de tener esto solo nos queda pasar el servicio de navegación en el constructor de nuestros ViewModels y acceder a los distintos métodos para navegar.

/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel(IDataService dataService, INavigatorService navigator)
private void GotoView1Command()
{
    _navigator.GotoMvvmView1(_item);
}

Quizás pienses que a esta altura, cuando ya WP7 solo tendrá una próxima actualización y las cosas cambiarán de cara a WP8 y a la nueva interfaz de W8 (antes Metro), ya no tienen mucho sentido. Puedes echarle una ojeada a este post sobre W8 del propio Josue y seguramente habrá cosas que te resulten familiar. Winking smile

Salu2

Código del ejemplo

Publicado en General | Deja un comentario

AsyncWorkManagerHelper para WP7

Hace mucho que tenía este artículo y este código en el tintero por repasar y publicar. Se trata de un Helper para ejecutar tareas en un agente de WP7 de manera asíncrona.

Hace 1 año aproximadamente, Peter Torr publicaba un artículo en tres partes explicando el trabajo con los agentes en background para WP7. Los enlaces a las tres partes son:

1- Background Agents – Part 1 of 3
2- Background Agents – Part 2 of 3
3- Background Agents – Part 3 of 3

En la segunda parte de la seria se discute un Helper para realizar tareas en segundo plano y esperar que estas terminen para poder notificar al agente el resultado de la ejecución.

El helper da una limpieza a nuestro código muy buena y nos permite olvidarnos de todo el proceso asíncrono. Sin embargo, si queremos extender el uso del helper más allá de un agente y utilizarlo en nuestra aplicación, encontramos que carece de alguna funcionalidad.

1- No se tiene control de lo que va sucediendo con las tareas que se van ejecutando hasta que no termina todo el proceso.
2- Si yo necesito que las tareas se ejecuten de 1 en 1 o de 2 en 2, o de N en N, tampoco tendría el control sobre ello.

Entiéndase que el helper fue escrito para ayudar con las tareas en background de un agente y en estos casos pocas veces importa lo que está sucediendo hasta que no termina todo el proceso.

Dispuesto a poder utilizar el código del Helper en cualquier tarea asíncrona que necesite ejecutar mi aplicación de WP7, le hice algunas modificaciones al código.

FIFO para las tareas.

First In, First Out. El código de Peter ejecuta directamente cada tarea cuando se adiciona la misma al Helper. En realidad estas ejecuciones son bloqueadas hasta que un ManualResetEvent es seteado cuando se ejecuta el Start o el WaitAll

/// <summary>
/// Schedules a work item to begin
/// </summary>
/// <param name="work">The work to perform</param>
/// <remarks>The worker thread is blocked on the startEvent so that the work
/// doesn't complete before the client is ready to handle the completion event
/// </remarks>
void BeginWorkItem(WorkloadInfo<TParam, TResult> work)
{
  lock (this)
  {
    outstandingItems++;
  }

  ThreadPool.QueueUserWorkItem(delegate
  {
    // Wait until it's OK to start
    startEvent.WaitOne();
    try
    {
      // Method is responsible for completing itself if it didn't fail
      work.Method(work.Parameter, work);
    }
    catch (Exception ex)
    {
      // Complete with a failure case
      work.NotifyFailure(ex);
    }
  });
}

Si queremos controlar la cantidad de tareas ejecutándose a la vez, este mecanismo no nos vale, por lo que he creado un Queue (FIFO en NET) para almacenar las tareas y poder controlar su ejecución.

/// <summary>
/// FIFO to Tasks
/// </summary>
private readonly Queue<WorkloadInfo<TParam, TResult>> _queue;

El la clase AsyncWorkManager ahora tiene 2 constructores, en uno de ellos podemos indicar el límite de tareas que se ejecutan simultáneamente.

Para poder conocer el estado de cada tarea en el momento en que se ejecutan, se ha adicionado un evento (OnCompleteWorkItem) a la clase WorkLoadInfo que es disparado justo al concluir la ejecución de la misma.

/// <summary>
/// Completes the work item with a successful result
/// </summary>
/// <param name="result">The result of the operation</param>
/// <remarks>This method is called by the worker Method once it has completed its task</remarks>
public void NotifySuccess(TResult result)
{
    MarkAsComplete();
    Result = result;

    if (OnCompleteWorkItem != null) OnCompleteWorkItem(this, EventArgs.Empty);
    _parent.CompleteWorkItem();
}

/// <summary>
/// Completes the work item with an error
/// </summary>
/// <param name="error">The error to report</param>
/// <remarks>This method is called by the worker Method if it fails its task</remarks>
public void NotifyFailure(Exception error)
{
    MarkAsComplete();
    Error = error;

    if (OnCompleteWorkItem != null) OnCompleteWorkItem(this, EventArgs.Empty);
    _parent.CompleteWorkItem();
}

La clase AsyncWorkManager tiene dos métodos por los cuales se puede iniciar el proceso. Start y WaitAll.

Start

El método inicia la ejecución de todas las tareas y no espera por su finalización. En la versión original del Helper el Start se ejecuta en el mismo hilo desde el cual es llamado y, si en nuestro caso queremos controlar la cantidad de tareas que se van ejecutando esto tampoco nos vale, así que también se ha modificado para que se ejecute en un ThreadPool diferente.

/// <summary>
/// Starts performing work, if not already happening.
/// </summary>
/// <remarks>This method is implicitly called by the WaitAll methods</remarks>
public void Start()
{
    _startNewEvent.Set();

    lock (this)
    {
        if (_queue.Count == 0)
        {
            CompleteWorkload();
            return;
        }

        // Work has already been completed
        if (_completionEvent.WaitOne(0)) return;

        // Reset the completion event to be waited on
        _completionEvent.Reset();

        _outstandingItems = _queue.Count;
    }

    ThreadPool.QueueUserWorkItem(delegate
    {
        // Release the threads waiting to do work
        while (true)
        {
            if (_executingOperations == _concurrentOperations && _concurrentOperations != 0) _startNewEvent.Reset();

            _startNewEvent.WaitOne();

            var work = _queue.Dequeue();
            BeginWorkItem(work);

            if (_queue.Count == 0) break;
        }
    });
}

Una particularidad del cambio realizado es que si usamos el constructor sin parámetros de la clase AsyncWorkManager, no se controla la concurrencia y todas las tareas son ejecutadas a la vez.

WaitAll

Este método tiene dos sobrecargas, una sin parámetros que espera infinitamente a que todas las tareas se hayan ejecutado (ojo que esto detiene el hilo en el que se haga la llamada al método) y otro que espera un tiempo (timeout) y aborta en caso de que todas las tareas no se hayan finalizado.

Después de los cambios, un ejemplo de cómo se podría usar el Helper sería este:

var calendarsWorkManager = new AsyncWorkManager<Account, IEnumerable<Calendar>>(concurrentsTask:1); 

GetAccounts(delegate(object o, AccountsEventArgs args)
{
    foreach (var account in args.EntityList)
    {
        switch (account.Source)
        {
            case Enums.SourceProvider.Google:

                var rGoogle = calendarsWorkManager.AddWorkItem(SyncGoogleCalendars, account);
                rGoogle.OnCompleteWorkItem += delegate
                {
                    if (rGoogle.Error == null) calendarList.AddRange(rGoogle.Result);
                };

                break;
            case Enums.SourceProvider.Live:

                var rLive = calendarsWorkManager.AddWorkItem(SyncLiveCalendars, account);
                rLive.OnCompleteWorkItem += delegate
                {
                    if (rLive.Error == null) calendarList.AddRange(rLive.Result);
                };

                break;
            case Enums.SourceProvider.Exchange:

                calendarList.Add(ExchangeCalendar(account));

                break;
        }
    }

    calendarsWorkManager.WorkComplete += (sender, a) => onCalendarList(this, new CalendarsEventArgs(calendarList));
    calendarsWorkManager.Start();
});

En principio la funcionalidad es la misma, excepto que podemos conocer y hacer algo cuando cada tarea se termina de ejecutar (onCompleteWorkItem), podemos indicar la cantidad de tareas ejecutándose a la vez (concurrentsTask:1) y ejecutar el inicio del proceso mediante Start sin que el hilo quede bloqueado.

Adjunto el código del Helper modificado por si a alguien le vale.

Un salu2

Publicado en WP7 | Deja un comentario

#region en C#

Acabo de leer en twitter un comentario que dice:

#region in C# only has 2 purposes: to add unnecessary noise, or when you think it helps, it’s actually telling you how much your class sucks because if you feel the need to ‘group’ things into regions, you generally need to separate the code into multiple classes”

El comentario se refiere a dos formas diferentes de usar #region, uno sería quien usa las regiones para identificar zonas privadas o públicas, o para identificar constructores del resto de la lógica en una clase y, el otro grupo se refiere a quienes lo usan para agrupar funcionalidad o lo que es lo mismo, lógicas diferentes dentro de una misma clase.

Al primer grupo, le define el uso de regiones como “ruido”, un calificativo desde mi punto de vista que pudiera ser discutible Winking smile 

Sobre el comentario hacia el segundo grupo no se le puede quitar una pizca de razón. Si una clase es lo suficientemente grande como para que te sea “molesta a la vista”, puedes estar seguro que necesitas un refactoring antes que varios #region

… aquí tienes un claro ejemplo de lo que nunca se debería hacer:

Untitled

cu…

Publicado en General | Deja un comentario

[OT] Return to live…

Hace ya bastante tiempo que un día sí y otro también, solo sabemos escuchar hablar sobre las nuevas tecnologías que nos vienen desde Microsoft: Metro, W8, WP8, nueva versión de NET, de MVC, de EF…

Si a eso le sumamos andaduras por WebGL con three.js, Kinect con javascript y HTML 5 que últimamente me tocan porque sí… poco tiempo queda para escribir algo.

Aprovechando que agosto parece que viene más “relajado” y que finalmente, después de cambiar a WP, pienso empezar a publicar cosas en mi blog (www.odelvalle.com) sería buen momento para retornar a la vida… Smile

Para no dejar abandonado (no podría) a geeks.ms, este primer post viene siendo algo así como un [TestMethod] desde Live Writer hacia el resto del mundo

Pronto más…
salu2

Publicado en General | Deja un comentario