Home - About me - Browse by categories

[Windows Phone] Bonnes pratiques dans une tâche périodique avec un background agent

Vous le savez certainement déjà, la version 7.1 du SDK Windows Phone (Mango) offre la possibilité de créer des Background Agent, c’est à dire des tâches de fond exécutées par l’OS à interval régulier (en moyenne, selon certains paramètres, cf. plus bas).

Il existe deux types de tâches : périodique et traitement long. Dans ce post, nous nous intéresserons uniquement au premier type.

Dans ces tâches, il n’est pas possible de faire tout et n’importe quoi, seules certaines API sont autorisées (cf. cette page de la MSDN). Cependant des scénarios assez sympa sont rendus possibles, notamment la mise à jour des Live Tile.

Quelques éléments importants sur les backgrounds agents :

  • ils sont exécutés toutes les 30 minutes - il est possible d’y exécuter du code pendant 25 secondes, pas plus! - si le téléphone passe en mode d’économie de batterie, ils sont succeptibles de ne pas se lancer - sur certain device, seuls 6 background agents peuvent être actif en simultanés - ils ne doivent pas utiliser plus de 6MB de mémoire - ils doivent être replannifié toutes les deux semaines - un agent qui crash 2 fois est désactivé par l’OS

Cela fait pas mal de règles assez contraignantes mais il est vraiment possible de tirer profit de ces background agents en respectant quelques bonnes pratiques.

Eviter la propagation d’exception dans un Background Agent

Comme vu plus tôt, un background agent qui crash 2 fois est automatiquement désactivé par l’OS. Vous devez à tout prix être attentif à ne pas laisser passer d’exception. Un scénario de crash assez courant et la mauvaise gestion des connexions et appels réseaux.

Par exemple, votre background agent lance une requête HTTP vers un service, la connexion est coupée, l’exception n’est pas catchée dans la callback => l’agent plante (il vous reste une vie!).

try
{
var httpWebRequest = WebRequest.CreateHttp("http://www.monsite.com/service.aspx");
httpWebRequest.BeginGetRequestStream(asyncResult =>
{
try
{
var responseStream = httpWebRequest.EndGetRequestStream(asyncResult);
//gestion de la réponse
}
catch (Exception exception)
{
OnError(exception);
}
}, null);
}
catch (Exception exception)
{
OnError(exception);
}

Comme il est possible de le constater dans l’extrait de code ci-dessus, il est important d’intercépter les exceptions lors de la création de la requête HTTP, mais également lors de la récupération de la réponse, dans la callback !

Plannifier un Background Agent

Comme vu précédemment, les background agents sont automatiquement désactivés au bout de 2 semaines. Il est donc nécessaire de les replannifier fréquemment.

Lorsque l’on plannifie un background agent, il est possible que celui-ci ait été désactivé par l’utilisateur depuis l’interface du téléphone prévue à cet effet (dans les paramètres du téléphone). Dans ce cas une InvalidOperationException est levée lors de la tentative de plannification => il faut penser à catcher cette exception et éventuellement sauvegarder le fait que le background agent soit désactivé dans un fichier de configuration, par exemple.

La première étape consiste à récupérer une tâche périodique via son nom :

var periodicTask = ScheduledActionService.Find("AgentDemo") as PeriodicTask;

Ensuite, on la supprime si elle existe (il est préférable de la supprimer et de la replannifier, afin de palier à la désactivation au bout de 2 semaines!) :

if (periodicTask != null)
{
try
{
ScheduledActionService.Remove(PeriodicTaskName);
}
catch (Exception) { }
}

Il faut ensuite recréer la tâche et lui passer une description (obligatoire!) :

periodicTask = new PeriodicTask("AgentDemo");
periodicTask.Description = "Description de l'agent";

Ensuite on tente la plannification (gare à l’InvalidOperationException si l’utilisateur a désactivé le background agent dans les paramètres de Windows Phone!) :

try
{
ScheduledActionService.Add(periodicTask);
}
catch (InvalidOperationException ioe)
{
if (ioe.Message.Contains("BNS Error: The action is disabled"))
{
//mise à jour conf
}
}

Enfin, pour déboguer, il est possible de demander le lancement de l’agent après un temps donné (lorsque l’application est quittée via le bouton démarrer, pour concerver le débogueur attaché.)

ScheduledActionService.LaunchForTest("DemoAgent", TimeSpan.FromSeconds(10));

Votre agent est prêt et fonctionnel !!

Eviter de dépasser la limite mémoire autorisée

Une des règles à respecter dans un background agent est de ne pas dépasser 6MB de mémoire ! Si vous dépassez cette limite, l’agent est intérrompu et il est considéré comme planté, donc désactivé au bout de 2 fois. Tout au long du traitement, il est possible de tester la mémoire consommée par l’agent à l’aide de l’instruction :

var memory = DeviceStatus.ApplicationMemoryUsageLimit
- DeviceStatus.ApplicationCurrentMemoryUsage;

Voilà quelques règles à respecter pour faire des background agents qui resteront actifs !

A bientôt Winking smile

read more

[Azure] Invalidation de settings ou de cache à l’aide d’une clé de configuration

Dans ce post je vais vous expliquer une technique que j’ai utilisée pour invalider le cache / recharger les settings sur toutes les instances du rôle Web qui héberge mon blog (en vrai il n’y en a qu’une seule, mais je trouve l’astuce sympa image)

Le constat est simple : mes posts et les settings du blog (email, smtp, etc…) sont persistés dans le stockage table de Windows Azure. Afin d’éviter les aller-retours vers le stockage par table, je monte tout en cache mémoire (le nombre de transaction est facturé et en plus du coup ça envoi du lourd en terme de perf).

Problème : si je change un settings dans mon storage, comment est-ce que je demande à mon service de recharger les données ? Idem si pour une raison x ou y je souhaite vider le cache de posts, je dois avoir une solution. Si on se place dans un contexte Windows Azure multi-instances (cas très fréquent) c’est une couche de complexité supplémentaire qui se rajoute car il faut notifier chaque instance !

La solution la plus simple est d’utiliser un setting Windows Azure et de faire en sorte que vos instances écoutent les changements de configuration. Dès lors, il suffit de détecter le changement de la bonne clé de settings, ConfigVersion dans mon cas (ou CléBidon si vous voulez) et de reinitialiser vos différents services (ici mon service de cache et mon service de settings).

Tout cela se passe dans le fichier WebRole.cs du webRole, via l’événement Changed de RoleEnvironment :

public class WebRole : RoleEntryPoint
{
public override bool OnStart()
{
// For information on handling configuration changes
// see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357.
RoleEnvironment.Changed += OnRoleEnvironmentChanged;
return base.OnStart();
}

private void OnRoleEnvironmentChanged(object sender, RoleEnvironmentChangedEventArgs e)
{
if (e.Changes.Any(change => change is RoleEnvironmentConfigurationSettingChange)) {
foreach (var change in e.Changes.OfType<RoleEnvironmentConfigurationSettingChange>()) {
if (change.ConfigurationSettingName == "ConfigVersion")
{
var cacheService = UnityRoot.Container.Resolve<ICacheService>();
cacheService.InvalidateCache();
var settingsManager = UnityRoot.Container.Resolve<ISettingsManager>();
settingsManager.Reload();
}
}
}
}
}

A bientôt image

Technorati Tags: [windows](http://technorati.com/tags/windows),[windows](http://technorati.com/tags/windows),[windows](http://technorati.com/tags/windows)
read more

[Livre] MVVM : De la découverte à la maîtrise

Un billet rapide pour vous présenter un ouvrage dédié au pattern MVVM et à son utilisation dans le cadre de projets WPF/Silverlight et Silverlight pour Windows Phone.

image

Coécrit par deux collègues et amis Jonathan Antoine et Jonathan Antoine, ce livre s’adresse à toutes personnes désireuses de découvrir MVVM ou d’approfondir leurs connaissances sur le sujet.

Bien que je n’ai pas encore lu tout l’ouvrage (sorti aujourd’hui même), j’ai eu l’occasion de participer à la relecture de certains chapitres de celui-ci et j’ai énormément apprécié l’approche des auteurs : du pratique, du concret et la réalité du terrain concernant MVVM !

Pour en savoir plus c’est par là : http://www.digitbooks.fr/catalogue/mvvm-antoine-lebrun.html

Bravo à Jon et Tom, et surtout, bonne lecture à tous !

A bientôt ! image

Technorati Tags: [livre](http://technorati.com/tags/livre),[livre](http://technorati.com/tags/livre),[livre](http://technorati.com/tags/livre),[livre](http://technorati.com/tags/livre),[livre](http://technorati.com/tags/livre),[livre](http://technorati.com/tags/livre),[livre](http://technorati.com/tags/livre),[livre](http://technorati.com/tags/livre),[livre](http://technorati.com/tags/livre),[livre](http://technorati.com/tags/livre)
read more

[EN] [ASP.NET MVC 4] Asynchronous controllers

Asynchronous execution is the future of Windows development : it has been largely demonstrated during the //Build conference two weeks ago.

In previous versions of ASP.NET MVC it was possible to create asynchronous controllers by inheriting the AsyncController class and using some conventions :

  • MyActionAsync : method that returns void and launches an asynchronous process
  • MyActionCompleted : method that returns an ActionResult (the result of the MVC action MyAction, in this case)

To allow the MVC engine to manage asynchronous operations and pass the result to the view engine, developers had to use the propery AsyncManager of the AsyncController. The completed method parameters was passed by the MVC engine through this object.

For example, the controller that is defined bellow allows to get a Json-serialized list of movies – asynchronously – from an OData service :

public class MoviesController : AsyncController
{
public ActionResult Index()
{
return View();
}

public void GetJsonMoviesAsync(int? page)
{
const int pageSize = 20;
int skip = pageSize * ((page ?? 1) - 1);
string url = string.Format("http://odata.netflix.com/[…]&$skip={0}&$top={1}",
skip, pageSize);

//the asynchronous operation is declared
AsyncManager.OutstandingOperations.Increment();

var webClient = new WebClient();
webClient.DownloadStringCompleted += OnWebClientDownloadStringCompleted;
webClient.DownloadStringAsync(new Uri(url));//the asynchronous process is launched
}

private void OnWebClientDownloadStringCompleted(object sender,
DownloadStringCompletedEventArgs e)
{
//the asynchronous process ends
//"movies" result is added to the parameters of the AsyncManager
//NB : it's the name of the parameter that is take by the
//GetJsonMoviesCompleted method
List<Movie> movies = null;
if (AsyncManager.Parameters.ContainsKey("movies"))
{
movies = (List<Movie>)AsyncManager.Parameters["movies"];
movies.Clear();
}
else
{
movies = new List<Movie>();
AsyncManager.Parameters["movies"] = movies;
}

movies.AddRange(Movie.FromXml(e.Result));

//the ends of the asynchronous operation (launches the call of "Action"Completed)
AsyncManager.OutstandingOperations.Decrement();
}

public ActionResult GetJsonMoviesCompleted(List<Movie> movies)
{
//on retourne le résultat Json
return Json(movies, JsonRequestBehavior.AllowGet);
}
}

It’s not really complicated to create an asynchronous controller but ASP.NET MVC 4 and C# 5 with the new async and await keywords will make it easier !

public class MoviesController : AsyncController
{
public ActionResult Index()
{
return View();
}

public async Task<ActionResult> GetJsonMovies(int? page)
{
const int pageSize = 20;
int skip = pageSize * ((page ?? 1) - 1);
string.Format("http://odata.netflix.com/[…]&$skip={0}&$top={1}",
skip, pageSize);

var webClient = new WebClient();
string xmlResult = await webClient.DownloadStringTaskAsync(url);
return Json(Movie.FromXml(xmlResult), JsonRequestBehavior.AllowGet);
}
}

As you can see in the previous code snippet, in ASP.NET MVC 4 you always should inherits from AsyncController but there is no more naming conventions, no more Async/Completed methods, no more AsyncManager and the action returns a Task instead of an ActionResult !

It’s awesome !

Hope this helps image

read more

[ASP.NET MVC 4] Contrôleurs asynchrones !

On le sait, cela a été largement abordé il y a deux semaines lors de la //Build conférence : le futur du développement est dans l’asynchrone.

Bien entendu, il était déjà possible de réaliser des contrôleur asynchrone dans les versions précédente d’ASP.NET MVC et ce en dérivant nos contrôleurs de la classe AsyncController. Le moteur MVC se basait alors sur des conventions :

  • MonActionAsync : méthode qui retourne void et qui démarre un traitement asynchrone
  • MonActionCompleted : méthode qui retourne un ActionResult (le résultat de l’action MVC MonAction, dans ce cas).

Afin de synchroniser le tout, l’AsyncController fournissait une propriété AsyncManager permettant notamment au moteur MVC de connaître le nombre d’opération asynchrone en cours, de gérer celles-ci et de passer les paramètres à la méthode de retour d’action (MonActionCompleted) à la fin du traitement asynchrone, et ce pour retourner le résultat.

Par exemple, le contrôleur ci-dessous expose une action GetJSonMovies asynchrone, qui se charge d’aller récupérer une liste de films via un flux OData :

public class MoviesController : AsyncController
{
public ActionResult Index()
{
return View();
}

public void GetJsonMoviesAsync(int? page)
{
const int pageSize = 20;
int skip = pageSize * ((page ?? 1) - 1);
string url = string.Format("http://odata.netflix.com/[…]&$skip={0}&$top={1}",
skip, pageSize);

//on "déclare" l'opération asynchrone
AsyncManager.OutstandingOperations.Increment();

var webClient = new WebClient();
webClient.DownloadStringCompleted += OnWebClientDownloadStringCompleted;
webClient.DownloadStringAsync(new Uri(url));//on lance le traitement asynchrone
}

private void OnWebClientDownloadStringCompleted(object sender,
DownloadStringCompletedEventArgs e)
{
//le traitement asynchrone se termine
//on rajoute le résultat "movies" au paramètre du AsyncManager
//NB : il s'agit du nom du paramètre de la méthode GetJsonMoviesCompleted !!
List<Movie> movies = null;
if (AsyncManager.Parameters.ContainsKey("movies"))
{
movies = (List<Movie>)AsyncManager.Parameters["movies"];
movies.Clear();
}
else
{
movies = new List<Movie>();
AsyncManager.Parameters["movies"] = movies;
}

movies.AddRange(Movie.FromXml(e.Result));

//on indique que l'opération se termine (déclenche l'appel du "Action"Completed)
AsyncManager.OutstandingOperations.Decrement();
}

public ActionResult GetJsonMoviesCompleted(List<Movie> movies)
{
//on retourne le résultat Json
return Json(movies, JsonRequestBehavior.AllowGet);
}
}

Nous sommes d’accord, ce code n’est pas extrêment compliqué, mais ASP.NET MVC 4, par le biais des nouveautés de C# 5 et de l’utilisation des mots clés async et await va grandement le simplifier, comme il est possible de le constater ci-dessous :

public class MoviesController : AsyncController
{
public ActionResult Index()
{
return View();
}

public async Task<ActionResult> GetJsonMovies(int? page)
{
const int pageSize = 20;
int skip = pageSize * ((page ?? 1) - 1);
string.Format("http://odata.netflix.com/[…]&$skip={0}&$top={1}",
skip, pageSize);

var webClient = new WebClient();
string xmlResult = await webClient.DownloadStringTaskAsync(url);
return Json(Movie.FromXml(xmlResult), JsonRequestBehavior.AllowGet);
}
}

Comme vous pouvez le voir, en ASP.NET MVC 4 on continue à dériver AsyncController, en revanche plus besoin de faire appelle à l’AsyncManager ou même d’utiliser des conventions Action**Async**/Action**Completed** ! Notez également que la méthode ne retourne plus un ActionResult mais un Task (induit par l’utilisation de async et await).

Franchement, ça poutre !

A bientôt image

read more