logo

log de Julien Corioland (MVP)

Quand les technologies .NET deviennent passion...

archives : September 2011


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

by Julien Corioland the 9/29/2011

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.

9782815002097-mvvm

Coécrit par deux collègues et amis Jonathan Antoine et Thomas Lebrun, 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 ! Winking smile

--


read full post -

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

by Julien Corioland the 9/29/2011

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 Winking smile


read full post -

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

by Julien Corioland the 9/29/2011

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 “ActionAsync/ActionCompleted” ! Notez également que la méthode ne retourne plus un ActionResult mais un Task<ActionResult> (induit par l’utilisation de async et await).

Franchement, ça poutre !

A bientôt Winking smile


read full post -

[EN] [ASP.NET MVC] Handle HTTP errors

by Julien Corioland the 9/27/2011

This subject seems to be really basic when developing a web application but it seems that it’s very discussed on the web too. In a lot of forum threads or blog posts we can find very various methods to handle HTTP exception within an ASP.NET MVC application. This post exposes a way to do that. I’m not pretending that it’s the best way but I think that it’s an elegant one Smile (so don’t hesitate to comment on this post if you’ve remarks or suggestions).

What’s the real need ?

I think that developers should manage HttpException in an other way than other exceptions for 3 main reasons :

  1. Respect of web-standards (a not found error returns a 404 http code, that’s it !)
  2. SEO : search engines use http codes (because it’s standards) to build their indexes.
  3. The user : a user-friendly page is already better than a stack trace…

I think that the most important is that the error code is well returned by the server : for example the URL http://www.mywebsite.com/Pages/some-page-that-does-not-exists is called the server should return a 404 status code and not a 302 (found) followed by a 200 (or 404) from a specific error page like http://www.mywebsite.com/Errors/Error404).

When HttpException are thrown in an ASP.NET MVC application ?

HttpException are thrown by the ASP.NET MVC engine or by the application code, directly in the asp.net controllers of your apps :

throw new HttpException(404, "Not found");

In some cases, they can also be thrown by the server (for internal errors, for example). But all HttpException can be handled in the application.

How to handle these exceptions ?

After a lot of researches and some tests I think that http exceptions should be handled in two places :

  1. In a base controller inherited by all the controllers of the application
  2. In the Global.asax file

Handle http exception in the controller :

Exceptions that are thrown by the developer directly in the application code can be handle in a base controller. For example :

public ActionResult Product(int id)
{
    var product = _unitOfWork.GetProduct(id);
    if(product == null)
        throw new HttpException(404, "Le produit est introuvable");
 
    return View(product);
}

By inheriting the System.Web.Mvc.Controller class it’s possible to override an “OnException” method. This method is called when an exception occurs in a controller method (action) :

protected override void OnException(ExceptionContext filterContext) {
    base.OnException(filterContext);
 
    if (filterContext.Exception != null) {
        filterContext.ExceptionHandled = true;
 
        if (filterContext.Exception is HttpException) {
            if (!ControllerContext.RouteData.Values.ContainsKey("error")) {
                ControllerContext.RouteData.Values.Add("error", filterContext.Exception);
            }
 
            var httpException = (HttpException) filterContext.Exception;
 
            switch (httpException.GetHttpCode()) {
                case 404:
                    filterContext.HttpContext.Response.StatusCode = 404;
                    filterContext.HttpContext.Response.StatusDescription = httpException.Message;
                    View("Error404", null).ExecuteResult(ControllerContext);
                    break;
                case 500:
                    filterContext.HttpContext.Response.StatusCode = 500;
                    filterContext.HttpContext.Response.StatusDescription = httpException.Message;
                    View("Error500", null).ExecuteResult(ControllerContext);
                    break;
                default:
                    filterContext.HttpContext.Response.StatusDescription = httpException.Message;
                    View("GenericError", null).ExecuteResult(ControllerContext);
                    break;
            }
        }
 
        //autre traitement si pas HttpException (log par exemple...)
    }
}

As you can see in this code snippet the first step is to check that the exception is an HttpException. If yes, a redirection to an appropriate error page is done just after setting the status code and status description of the http response. The views are in the “Shared” folder of the application.

Now http exceptions that are thrown in a controller return the good status code and redirect to a user friendly page.

Handle HTTP exceptions in the Global.asax :

It’s in the Global.asax file that all other http exception are handled. To to that you should subscribe to the Error event of the mvc application (in the Init method override) :

public override void Init()
{
    base.Init();
    this.Error += new EventHandler(MvcApplication_Error);
}

In the Error event handler, initialize an errors-dedicated controller and create appropriate route data to work with the error :

var routeData = new RouteData();
routeData.Values.Add("controller", "Errors"); 
 
var lastException = Server.GetLastError();
 
if (lastException is HttpException) {
    var httpException = (HttpException) lastException;
    switch(httpException.GetHttpCode()) {
        case 404:
            routeData.Values.Add("action", "Error404");
            break;
        case 500:
            routeData.Values.Add("action", "Error500");
            break;
        default:
            routeData.Values.Add("action", "GenericError");
            break;
    }
}
else
{
    routeData.Values.Add("action", "GenericError");
}
 
routeData.Values.Add("exception", lastException);
 
Server.ClearError();
 
IController errorController = _unityContainer.Resolve<ErrorsController>();
errorController.Execute(new RequestContext(
new HttpContextWrapper(Context), routeData));

The ErrorsController :

public class ErrorsController : Controller
{
    public ActionResult Error404() {
        Response.StatusCode = 404;
        Exception exception = null;
        if(RouteData.Values.ContainsKey("exception")) {
            exception = (Exception) RouteData.Values["exception"];
        }
        return View(exception);
    }
 
    public ActionResult Error500() {
        Response.StatusCode = 500;
        Exception exception = null;
        if (RouteData.Values.ContainsKey("exception"))
        {
            exception = (Exception)RouteData.Values["exception"];
        }
        return View(exception);
    }
 
    public ActionResult GenericError() {
        Exception exception = null;
        if (RouteData.Values.ContainsKey("exception"))
        {
            exception = (Exception)RouteData.Values["exception"];
        }
        return View(exception);
    }
}

As you can see each action defines itself the http status code of the http response.

Now all http exceptions are handled in a user and SEO friendly way in your ASP.NET MVC applications !

Hope this helps Winking smile


read full post -

[ASP.NET MVC] Gestion des erreurs HTTP

by Julien Corioland the 9/27/2011

Bien que cela soit un sujet qui paraissent essentiel et surtout “basic” lorsque l’on développe une application web (avec ASP.NET MVC ou pas d’ailleurs), ce sujet semble avoir fait couler beaucoup d’encre sur le web ! (si je puis dire…). En effet, il semble qu’il règne une sorte de “flou” autour de ce sujet ou chacun utilise une méthode différente pour gérer les erreurs HTTP. Je ne sais pas si on peut parler ici de “bonne pratique” mais en tout cas, ce post présente un moyen que je juge efficace et suffisament générique pour gérer les Http Exception dans une application ASP.NET MVC ! (n’hésitez pas à me faire par de vos remarques / suggestions en commentaire Smile)

Quel est le réel besoin ?

Au final, pourquoi souhaite-t-on traiter les erreurs HTTP de manière différente des autres erreurs ? A mon humble avis, pour 3 raisons principalement :

  1. Le respect des standards (une page qui n’existe pas c’est un 404, c’est tout !)
  2. La SEO : les moteurs de recherche utilisent tous les codes HTTP (comme c’est standard) pour construire leurs index.
  3. L’utilisateur : on souhaite en général personnaliser la page d’erreur sur laquel arrive l’utilisateur !

Ce qui importe donc, c’est avant tout que le code d’erreur soit bien renvoyé : si j’appelle une URL “http://www.monsite.com/Pages/Page-qui-n-existe-pas” je souhaite que ce soit bien cette page là qui me retourne un code de status 404 et non pas un code 302 (trouvé) suivi d’un 200 (ou 404, à la limite, mais non) sur une URL de type “http://www.monsite.com/Errors/Error404”.

Quand est-ce que des HttpException sont levées en ASP.NET MVC ?

Les exceptions sont levées soit par le moteur ASP.NET MVC, soit dans votre code :

throw new HttpException(404, "Not found");

Elles peuvent également être levées au niveau du serveur (erreur interne 500, par exemple), mais dans tous les cas, il est possible de les gérer !

Comment gérer ces exceptions ?

Après pas mal de recherches et de tests, je pense qu’il y a deux endroits où il faut gérer les exceptions :

  1. Dans un contrôleur de base dérivé par tous les contrôleurs d’une application ASP.NET MVC
  2. Dans le fichier Global.asax

Gestion des erreurs dans un contrôleur de base :

Au sein du contrôleur, on gèrera les exceptions qui ont été levées intentionnellement par le développeur. Par exemple :

public ActionResult Product(int id)
{
    var product = _unitOfWork.GetProduct(id);
    if(product == null)
        throw new HttpException(404, "Le produit est introuvable");

    return View(product);
}

Lorsque l’on dérive la classe System.Web.Mvc.Controller, il est possible de surcharger une méthode “OnException” : celle-ci est appelée lorsqu’une exception est levée dans le contrôleur.

protected override void OnException(ExceptionContext filterContext) {
    base.OnException(filterContext);

    if (filterContext.Exception != null) {
        filterContext.ExceptionHandled = true;

        if (filterContext.Exception is HttpException) {
            if (!ControllerContext.RouteData.Values.ContainsKey("error")) {
                ControllerContext.RouteData.Values.Add("error", filterContext.Exception);
            }

            var httpException = (HttpException) filterContext.Exception;

            switch (httpException.GetHttpCode()) {
                case 404:
                    filterContext.HttpContext.Response.StatusCode = 404;
                    filterContext.HttpContext.Response.StatusDescription = httpException.Message;
                    View("Error404", null).ExecuteResult(ControllerContext);
                    break;
                case 500:
                    filterContext.HttpContext.Response.StatusCode = 500;
                    filterContext.HttpContext.Response.StatusDescription = httpException.Message;
                    View("Error500", null).ExecuteResult(ControllerContext);
                    break;
                default:
                    filterContext.HttpContext.Response.StatusDescription = httpException.Message;
                    View("GenericError", null).ExecuteResult(ControllerContext);
                    break;
            }
        }

        //autre traitement si pas HttpException (log par exemple...)
    }
}

Comme vous pouvez le constater, ce code vérifie si l’exception est de type HttpException, si c’est le cas, elle redirige vers une vue d’erreur après avoir renseigner un status code et un status description au niveau de la réponse.

NB : ces vues sont placées dans le dossier “Shared” des vues partagées.

Premier objectif atteind : une erreur HTTP levée dans un contrôleur se traduit par une page d’erreur “user friendly” et une réponse HTTP “SEO/Standards friendly” puisque retournant le bon code HTTP sur la bonne URL !

Gestion des erreurs HTTP dans le Global.asax :

Dans le Global.asax, on va gérer toutes les autres erreurs HTTP qui pourraient être levées dans l’application.

Pour se faire, on surcharge d’abord la méthode Init afin de s’abonner à l’événement Error :

public override void Init()
{
    base.Init();
    this.Error += new EventHandler(MvcApplication_Error);
}

Dans la méthode MvcApplication_Error, on récupère la dernière erreur du serveur, on vérifie si celle-ci est de type HttpException. Si oui, on instancie un contrôleur spécialisé dans la gestion des erreurs (ici ErrorsController, détaillé plus bas) on exécute le rendu de la vue en lui passant les bonnes RouteData :

var routeData = new RouteData();
routeData.Values.Add("controller", "Errors"); 

var lastException = Server.GetLastError();

if (lastException is HttpException) {
    var httpException = (HttpException) lastException;
    switch(httpException.GetHttpCode()) {
        case 404:
            routeData.Values.Add("action", "Error404");
            break;
        case 500:
            routeData.Values.Add("action", "Error500");
            break;
        default:
            routeData.Values.Add("action", "GenericError");
            break;
    }
}
else
{
    routeData.Values.Add("action", "GenericError");
}

routeData.Values.Add("exception", lastException);

Server.ClearError();

IController errorController = _unityContainer.Resolve<ErrorsController>();
errorController.Execute(new RequestContext(
new HttpContextWrapper(Context), routeData));

Voilà le code du contrôleur ErrorsController :

public class ErrorsController : Controller
{
    public ActionResult Error404() {
        Response.StatusCode = 404;
        Exception exception = null;
        if(RouteData.Values.ContainsKey("exception")) {
            exception = (Exception) RouteData.Values["exception"];
        }
        return View(exception);
    }

    public ActionResult Error500() {
        Response.StatusCode = 500;
        Exception exception = null;
        if (RouteData.Values.ContainsKey("exception"))
        {
            exception = (Exception)RouteData.Values["exception"];
        }
        return View(exception);
    }

    public ActionResult GenericError() {
        Exception exception = null;
        if (RouteData.Values.ContainsKey("exception"))
        {
            exception = (Exception)RouteData.Values["exception"];
        }
        return View(exception);
    }
}

NB : notez que c’est dans chaque action que l’on défini le status code de la réponse HTTP !

Je vous passe le code des vues qui n’a pas grand intérêt (mise à part que l’on peut réutiliser ici les vues du dossier “Shared” évidemment)

Deuxième objectif atteind : une erreur HTTP levée autre part que dans un contrôleur se traduit par une page d’erreur “user friendly” et une réponse HTTP “SEO/Standards friendly” puisque retournant le bon code HTTP sur la bonne URL !

A bientôt ! Winking smile


read full post -

[EN][Entity Framework] Database first approach with EF 4.1

by Julien Corioland the 9/22/2011

As you probably already know the last version of Entity Framework (4.1) supports a new development scenario called “Code first” (also called code only in some cases). It consists to develop the .NET entities (POCOs) before the SQL relational model, create the database, map these entities to table and columns at runtime according the class definitions. To do that, there is no need of an EDMX file, metadata and mapping are based on conventions and attributes.

Entity Framework 4.1 provides a lot of new functionalities that may be used even if the “code first” approach is not applied for many reasons (existing database, database created and maintained by a DBA that doesn’t know .NET or Entity Framework…). To be able to use the new features of Entity Framework 4.1 it’s possible to continue with the database first approach and use an EDMX file to do the mapping between .NET objects (POCOs) and SQL tables and columns. This post explains how to do that !

Import the Entity Framework 4.1 library

To use EF 4.1 it’s recommended to reference the related NuGet package using the NuGet management console in Visual Studio. Right click on the project that should reference the library and choose Manage NuGet packages…

In the NuGet management console, search for “Entity Framework” and click Install to download and reference the EntityFramework.dll library.

Download the T4 code generation templates for EF 4.1

To generate the DbContext and the .NET entities from the EDMX definition it’s possible to download two code generation templates from the Visual Studio Extensions manager. Open it and search for “DbContext” in the online gallery :

Install the “ADO.NET C# DbContext Generator” item (or VB.NET).

Delete the code generation tool from EDMX properties

When an ADO.NET entity data model is added to a project, it uses the default code generation template. To avoid this, you have to remove this tool in the property window of the EDMX file :

Be careful if you’re working on an existing project where generated code may be in use !

Add the templates to the project

Now you’ve to add the two generation code templates to the project. To do that add a new item of type “ADO.NET C# DbContext Generator” from the add item menu :

In these two files, find the text $edmxInputFile and replace it by the relative path to the EDMX file.

Save the files. Automatically the DbContext and the entities are generated :

Now you are ready to use the DbContext and query the database through LINQ to Entites !

using (var dbContext = new BlogContainer()) {
    var posts = dbContext.Posts.Where(p => p.IsPublished).ToList();

    return View(posts);
}

After each change on the edmx file just run the custom tool on the two T4 templates to regenerate the DbContext and the entities.

Hope this helps Winking smile


read full post -

[Entity Framework] Continuer à utiliser un EDMX avec EF 4.1

by Julien Corioland the 9/19/2011

Le titre de ce post vous a peut-être suppris puisque l’une des nouveautés la plus plébicitée de Entity Framework 4.1 est la possibilité de s’affranchir d’un modèle EDMX et d’adopter une stratégie Code First : les entités .NET permettent de générer le modèle de données SQL.

Pourtant, Entity Framework 4.1 apporte de nombreuses autres nouveautés, notamment en terme de gestion de l’état des entités, la mise à jour d’entités dans un contexte state-less (notamment), de requêtage… via des méthodes beaucoup plus simples que précédement.

Ce post à pour but d’expliquer comment utiliser Entity Framework 4.1 tout en continuant d’utiliser un EDMX et une stratégie Database First !

Importer la librairie Entity Framework 4.1

Pour pouvoir utiliser EF 4.1, le plus simple est de référencer le package NuGet associé. Faites un clic droit sur votre projet Visual Studio puis cliqué sur “Manage NuGet packages…” :

EF41_01

Dans la fenêtre qui s’affiche, recherchez Entity Framework :

EF41_02

Cliquez sur “Install”, acceptez les conditions. La librairie EntityFramework.dll va être téléchargée et référencée dans le projet.

Télécharger les templates de génération de code (T4) pour EF 4.1

Afin de générer le code propre à EF 4.1 (entités et DbContext) il est possible de télécharger un template d’item via le gestionnaire d’extension de Visual Studio. Placez-vous dans la galerie en ligne et recherchez DbContext :

EF41_03

Installez l’élément “ADO.NET C# DbContext Generator” (ou VB si vous le souhaitez Smile).

Supprimer l’outil de génération de code de votre EDMX

Afin que le code ne soit plus généré par l’EDMX, éditez les propriétés de ce dernier et supprimer l’outil personnalisé :

EF41_04

Attention : ne faites pas celà dans un projet qui utilise les éléments de code généré par l’EDMX Entity Framework “classique”.

Ajouter les templates au projet

Maintenant, ajoutez un nouvel item de type “ADO.NET C# DbContext Generator” au projet.

EF41_05

Identifiez la ligne de code suivante dans chacun des templates et modifiez la chaine $edmxInputFile$ par le chemin relatif vers votre fichier EDMX.

EF41_06

Dès lors, lorsque vous enregistrez les templates, du code devrait être généré : les entités et le DbContext.

EF41_07

Et voilà, vous pouvez utilisez le contexte généré pour attaquer votre base de données !

using (var dbContext = new BlogContainer()) {
    var posts = dbContext.Posts.Where(p => p.IsPublished).ToList();

    return View(posts);
}

Après modification de l’EDMX, il suffit de regénérer les templates T4 pour les mettre à jour et de recompiler le tout Smile

A bientôt Winking smile


read full post -

[EN] [ASP.NET MVC] Custom output cache provider

by Julien Corioland the 9/13/2011

Since ASP.NET 4 was released it’s possible to write custom output cache providers to change the way data are cached. For example, one of my customer wanted that some actions were cached on the file system to avoid loss of cache during an application pool reboot.

Be careful by using the following code because in many scenarios it can’t be used “as it is” (in a web farm, for example).

A custom output cache provider is a simple class that inherits from OutputCacheProvider and overrides these methods :

  • Add : adds an entry in the cache
  • Get : gets an entry from the cache
  • Remove : removes an entry from the cache
  • Set : updates an entry in the cache

Cached items must be serializable. In this sample, the following class will be used :

[Serializable]
public class CachedItem
{
    public object Item { get; set; }
    public DateTime UtcExpiry { get; set; }
}

In order to save an item on the file system the two following methods will be used. The first allows to get a file path from the cache key (generated by asp.net) and the second allows to save a CachedItem on the disk :

private string GetFilePathFromKey(string key)
{
    foreach (var invalidChar in Path.GetInvalidFileNameChars())
        key = key.Replace(invalidChar, '_');

    return Path.Combine(CacheDirectory, key);
}

private void SaveCachedItem(CachedItem cachedItem, string filePath)
{
    if (File.Exists(filePath))
        File.Delete(filePath);

    using (var stream = File.OpenWrite(filePath))
    {
        var binaryFormatter = new BinaryFormatter();
        binaryFormatter.Serialize(stream, cachedItem);
    }
}

The Add method :

public override object Add(string key, object entry, DateTime utcExpiry)
{
    string filePath = GetFilePathFromKey(key);
    var cachedItem = GetCachedItem(filePath);

    if (cachedItem != null && cachedItem.UtcExpiry.ToUniversalTime() <= DateTime.UtcNow)
    {
        Remove(key);
    }
    else if (cachedItem != null)
    {
        return cachedItem.Item;
    }

    SaveCachedItem(new CachedItem()
    {
        Item = entry,
        UtcExpiry = utcExpiry
    }, filePath);

    return entry;
}

The Get method :

public override object Get(string key)
{
    string filePath = GetFilePathFromKey(key);
    var cachedItem = GetCachedItem(filePath);
    if (cachedItem != null)
    {
        if (cachedItem.UtcExpiry.ToUniversalTime() <= DateTime.UtcNow)
        {
            Remove(key);
        }
        else
        {
            return cachedItem.Item;
        }
    }

    return null;
}

The Remove method :

public override void Remove(string key)
{
    string filePath = GetFilePathFromKey(key);
    if (File.Exists(filePath))
        File.Delete(filePath);
}

The Set Method :

public override void Set(string key, object entry, DateTime utcExpiry) {
    string filePath = GetFilePathFromKey(key);
    var cachedItem = new CachedItem() {Item = entry, UtcExpiry = utcExpiry};
    SaveCachedItem(cachedItem, filePath);
}

Now that the output cache provider is created, it can be registered in the web.config file of the application, in the caching section :

<system.web>
<caching>
  <outputCache>
    <providers>
      <add name="FileSystemOutputCacheProvider"
           type="Samples.FileSystemOutputCacheProvider, Samples"/>
    </providers>
  </outputCache>
</caching>

The last step consists to override the GetOutputCacheProviderName method in the Global.asax file. Actually this method is called by the ASP.NET Framework when an OutputCache attribute (or an OutputCache directive) is used. The method returns the name of the output cache provider to use in the current context. For example, if you want to use the FileSystemOutputCacheProvider for the Details action of the Products controller, you can use this code :

public override string GetOutputCacheProviderName(HttpContext context)
{
    if (context.Request.RawUrl.ToUpper().Contains("PRODUCTS/DETAILS")) {
        return "FileSystemOutputCacheProvider";
    }
    return base.GetOutputCacheProviderName(context);
}

As mentioned previously you just have to set an OutputCache attribute on the action Details of the Products controller to use the custom output cache provider :

public class ProductsController : Controller
{
    [OutputCache(Duration = 3600, VaryByParam = "id")]
    public ActionResult Details(int id)
    {
        //long operation
        Thread.Sleep(2000);

        ViewBag.ProductId = id;
        return View();
    }
}

Download the source code here.

Hope this helps Winking smile


read full post -

[ASP.NET MVC] Fournisseur de cache de sortie personnalisé

by Julien Corioland the 9/13/2011

ASP.NET 4.0 apporte de nombreuses nouveautés et notamment la possibilité de développer son propre fournisseur de cache de sortie.

Pour des besoins bien particulier, un de mes clients avait besoin de générer un grand nombre de page dynamique : le problème étant que ces pages faisaient appel à énormément de ressources et donc la génération prenait un temps fou.

Pour résoudre le problème, j’ai proposé de faire un fournisseur de cache de sortie personnalisé permettant de stocker la réponse HTTP sur le FileSystem afin de pouvoir la renvoyer plus rapidement. Cette solution est assez intéressante car le cache reste disponible même si le pool d’application ASP.NET est coupé. En revanche, elle n’est pas optimale dans un cas multi-serveur avec un répartiteur de charge en amont. Bref, à ne pas utiliser “les yeux fermés” Smile

Création du fournisseur de cache de sortie personnalisé

Pour créer un fournisseur de cache de sortie personnalisé, il suffit de dériver de la classe abstraite OutputCacheProvider et de redéfinir les méthodes suivantes :

public class FileSystemOutputCacheProvider : OutputCacheProvider
{
    public override object Add(string key, object entry, DateTime utcExpiry)
    {
        throw new NotImplementedException();
    }

    public override object Get(string key)
    {
        throw new NotImplementedException();
    }

    public override void Remove(string key)
    {
        throw new NotImplementedException();
    }

    public override void Set(string key, object entry, DateTime utcExpiry)
    {
        throw new NotImplementedException();
    }
}

Voilà un bref descriptif des méthodes :

  • Add : permet d’ajouter un élément identifié par une clé unique dans le cache
  • Get : permet de récupérer un élément dans le cache
  • Remove : permet de supprimer un élément du cache
  • Set : permet de mettre à jour un élément dans le cache

Les éléments mis en cache seront de type “CachedItem” :

[Serializable]
public class CachedItem
{
    public object Item { get; set; }
    public DateTime UtcExpiry { get; set; }
}

Nous utiliserons également deux méthodes permettant de générer un chemin de fichier à partir de la clé passé par le moteur ASP.NET MVC et de sauvegarder un CachedItem sur le disque :

private void SaveCachedItem(CachedItem cachedItem, string filePath)
{
    if (File.Exists(filePath))
        File.Delete(filePath);

    using (var stream = File.OpenWrite(filePath))
    {
        var binaryFormatter = new BinaryFormatter();
        binaryFormatter.Serialize(stream, cachedItem);
    }
}

private string GetFilePathFromKey(string key)
{
    foreach (var invalidChar in Path.GetInvalidFileNameChars())
        key = key.Replace(invalidChar, '_');

    return Path.Combine(CacheDirectory, key);
}

Méthode Add :

public override object Add(string key, object entry, DateTime utcExpiry)
{
    string filePath = GetFilePathFromKey(key);
    var cachedItem = GetCachedItem(filePath);

    if (cachedItem != null && cachedItem.UtcExpiry.ToUniversalTime() <= DateTime.UtcNow)
    {
        Remove(key);
    }
    else if (cachedItem != null)
    {
        return cachedItem.Item;
    }

    SaveCachedItem(new CachedItem()
    {
        Item = entry,
        UtcExpiry = utcExpiry
    }, filePath);

    return entry;
}

Méthode Get :

public override object Get(string key)
{
    string filePath = GetFilePathFromKey(key);
    var cachedItem = GetCachedItem(filePath);
    if (cachedItem != null)
    {
        if (cachedItem.UtcExpiry.ToUniversalTime() <= DateTime.UtcNow)
        {
            Remove(key);
        }
        else
        {
            return cachedItem.Item;
        }
    }

    return null;
}

Méthode Remove :

public override void Remove(string key)
{
    string filePath = GetFilePathFromKey(key);
    if (File.Exists(filePath))
        File.Delete(filePath);
}

Méthode Set :

public override void Set(string key, object entry, DateTime utcExpiry) {
    string filePath = GetFilePathFromKey(key);
    var cachedItem = new CachedItem() {Item = entry, UtcExpiry = utcExpiry};
    SaveCachedItem(cachedItem, filePath);
}

Enregistrement du fournisseur de cache de sortie personnalisé

L’enregistrement d’un fournisseur de cache de sortie se fait en deux étapes : dans le Web.config et dans le Global.asax.

Fichier Web.config :

<system.web>

<caching>
  <outputCache>
    <providers>
      <add name="FileSystemOutputCacheProvider" 
	type="Samples.FileSystemOutputCacheProvider, Samples"/>
    </providers>
  </outputCache>
</caching>

Global.asax :

Il faut surcharger la méthode GetOutputCacheProviderName qui est en charge de retourner le fournisseur contextuel à la requête. Par exemple, si l’on souhaite utiliser le FileSystemCacheProvider que sur le contrôleur “Products”, on écrira :

public override string GetOutputCacheProviderName(HttpContext context)
{
    if (context.Request.RawUrl.ToUpper().Contains("PRODUCTS/DETAILS")) {
        return "FileSystemOutputCacheProvider";
    }
    return base.GetOutputCacheProviderName(context);
}

Utilisation du fournisseur de cache de sortie personnalisé en ASP.NET MVC

Pour utiliser le nouveau fournisseur de cache, il suffit d’utiliser l’attribut "OutputCache” comme à son habitude. C’est le Framework ASP.NET qui se chargera d’instancier le bon fournisseur de cache au bon moment Smile

public class ProductsController : Controller
{
    [OutputCache(Duration = 3600, VaryByParam = "id")]
    public ActionResult Details(int id)
    {
        //long operation
        Thread.Sleep(2000);

        ViewBag.ProductId = id;
        return View();
    }
}

NB : le fait de préciser ici le paramètre VaryByParam = “id” permet de faire en sorte que le moteur MVC génère une clé différente par id, est donc implicitement que le FileSystemOutputCacheProvider génère un fichier par id !

Sources de l'exemple

A bientôt Winking smile


read full post -

[En] Enable or disable IE proxy using Powershell

by Julien Corioland the 9/6/2011

I’m currently working in a company where I have to set up a proxy to connect my laptop to the Internet. Because I'm fed up to enable it each morning and disable it each evening, I made two Powershell scripts to do these operations :

EnableProxy.ps1 :

set-itemproperty 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings' 
-name ProxyEnable -value 1

DisableProxy.ps1 :

set-itemproperty 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings' 
-name ProxyEnable -value 0 

Et voilà !

Hope this helps Winking smile


read full post -

Activation et désactivation du proxy IE via Powershell

by Julien Corioland the 9/6/2011

En ce moment je suis chez un client où je dois activer un proxy pour me connecter à Internet. Comme j’en avais assez de devoir reconfigurer mon IE tous les soirs chez moi et tous les matins chez mon client, voilà deux scripts Powershell permettant de faire celà en deux clics :

EnableProxy.ps1 :

set-itemproperty 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings' 
-name ProxyEnable -value 1

DisableProxy.ps1

set-itemproperty 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings' 
-name ProxyEnable -value 0

Simple et efficace !

A bientôt Winking smile


read full post -

[En][ASP.NET MVC] Remote attribute and asynchronous validation

by Julien Corioland the 9/5/2011

As you probably know ASP.NET MVC supports .NET Framework 4 Data Annotations to validate user’s inputs (Required, StringLength, Range, RegularExpression…)

Another attribute exists and allows asynchronous client side validation. It’s the Remote attribute. For example, it can be used to validate e-mail and username in a registration form and alert the user before the form is posted.

Here is a sample model for a registration form :

public class UserModel
{
    [Required]
    [Remote("CheckUsername", "RemoteValidation", ErrorMessage = "This username is already used.")]
    public string Username { get; set; }
 
    [Required]
    [Remote("CheckEmail", "RemoteValidation", ErrorMessage = "This e-mail is already used.")]
    public string Email { get; set; }
     
    [StringLength(80)]
    public string FirstName { get; set; }
 
    [StringLength(80)]
    public string LastName { get; set; }
     
    public DateTime BirthDate { get; set; }
}

As you can see in the previous code snippet, the Remote attribute takes two parameters :

  • the MVC action that should validate the input
  • the controller that defines the action

The controller :

public class RemoteValidationController : Controller 
{
    private readonly string[] _existingUsernames = {"beedoo", "julien", "jcorioland"};
    private readonly string[] _existingEmails = { "contact@juliencorioland.net" };
 
    public JsonResult CheckUsername(string username) {
        bool userIsAvailable = !_existingUsers.Contains(username);
        return Json(userIsAvailable, JsonRequestBehavior.AllowGet);
    }
 
    public JsonResult CheckEmail(string email)
    {
        bool emailIsAvailable = !_existingEmails.Contains(email);
        return Json(emailIsAvailable, JsonRequestBehavior.AllowGet);
    }
}

These actions are very simple : they take the input to validate as a string parameter and return a Json-serialized Boolean that indicates if the input is valid or not. For this article I’ve used two arrays to represent users and e-mails databases but it’s possible to execute any code here.

The view :

@using (Html.BeginForm())
{
    <div class="editor-label">@Html.LabelFor(m => m.Username)</div>
    <div class="editor-field">
        @Html.TextBoxFor(m => m.Username)
        @Html.ValidationMessageFor(m => m.Username)
    </div>
 
    <div class="editor-label">@Html.LabelFor(m => m.Email)</div>
    <div class="editor-field">
        @Html.TextBoxFor(m => m.Email)
        @Html.ValidationMessageFor(m => m.Email)
    </div>
 
    <div class="editor-label">@Html.LabelFor(m => m.FirstName)</div>
    <div class="editor-field">
        @Html.TextBoxFor(m => m.FirstName)
        @Html.ValidationMessageFor(m => m.FirstName)
    </div>
 
    <div class="editor-label">@Html.LabelFor(m => m.LastName)</div>
    <div class="editor-field">
        @Html.TextBoxFor(m => m.LastName)
        @Html.ValidationMessageFor(m => m.LastName)
    </div>
 
    <div class="editor-label">@Html.LabelFor(m => m.BirthDate)</div>
    <div class="editor-field">
        @Html.TextBoxFor(m => m.BirthDate)
        @Html.ValidationMessageFor(m => m.BirthDate)
    </div>
     
    <input type="submit" value="S'enregistrer" />
}

To activate the client side validation the following jQuery plugins should be loaded :

<head>
    <title>@ViewBag.Title</title>
    <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
    <script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery-ui-1.8.11.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
</head>

The validation actions are automatically called (asynchronously of course) when the user is completing the register form :

image

Hope this helps Winking smile


read full post -

[ASP.NET MVC] Attribut Remote et validation asynchrone

by Julien Corioland the 9/5/2011

Comme vous le savez peut-être déjà, ASP.NET MVC 3 supporte les DataAnnotations du .NET Framework 4 pour tout ce qui est validation des entrées utilisateurs : Required, StringLength, Range, RegularExpression…

Il existe un attribut un peu moins connu : Remote. Celui-ci permet de lancer une validation asynchrone côté client. Par exemple, sur un formulaire d’inscription il est fréquent de vouloir valider de manière asynchrone qu’un nom d’utilisateur / e-mail n’existe pas déjà en base utilisateur.

Voilà un exemple de modèle pour parvenir à cela :

public class UserModel
{
    [Required]
    [Remote("CheckUsername", "RemoteValidation", ErrorMessage = "Ce nom d'utilisateur est déjà utilisé")]
    public string Username { get; set; }

    [Required]
    [Remote("CheckEmail", "RemoteValidation", ErrorMessage = "Cet e-mail est déjà utilisé")]
    public string Email { get; set; }
    
    [StringLength(80)]
    public string FirstName { get; set; }

    [StringLength(80)]
    public string LastName { get; set; }
    
    public DateTime BirthDate { get; set; }
}

Comme il est possible de le constater dans l’extrait de code ci-dessus, l’attribut remote prend deux paramètres :

  • L’action MVC en charge de la validation
  • Le contrôlleur dans lequel l’action est défini (ici, RemoteValidationController)

Voilà le code de ce contrôlleur :

public class RemoteValidationController : Controller 
{
    private readonly string[] _existingUsernames = {"beedoo", "julien", "jcorioland"};
    private readonly string[] _existingEmails = { "contact@juliencorioland.net" };

    public JsonResult CheckUsername(string username) {
        bool userIsAvailable = !_existingUsers.Contains(username);
        return Json(userIsAvailable, JsonRequestBehavior.AllowGet);
    }

    public JsonResult CheckEmail(string email)
    {
        bool emailIsAvailable = !_existingEmails.Contains(email);
        return Json(emailIsAvailable, JsonRequestBehavior.AllowGet);
    }
}

Les actions sont ultra simples : elles prennent en paramètre le terme à valider et retourne un booléean, sérialisé en Json.

NB : ici la source d’emails/usernames existants a été simplifiée, mais un appel à une base de données est tout à fait possible.

La vue pour tester :

@using (Html.BeginForm())
{
    <div class="editor-label">@Html.LabelFor(m => m.Username)</div>
    <div class="editor-field">
        @Html.TextBoxFor(m => m.Username)
        @Html.ValidationMessageFor(m => m.Username)
    </div>

    <div class="editor-label">@Html.LabelFor(m => m.Email)</div>
    <div class="editor-field">
        @Html.TextBoxFor(m => m.Email)
        @Html.ValidationMessageFor(m => m.Email)
    </div>

    <div class="editor-label">@Html.LabelFor(m => m.FirstName)</div>
    <div class="editor-field">
        @Html.TextBoxFor(m => m.FirstName)
        @Html.ValidationMessageFor(m => m.FirstName)
    </div>

    <div class="editor-label">@Html.LabelFor(m => m.LastName)</div>
    <div class="editor-field">
        @Html.TextBoxFor(m => m.LastName)
        @Html.ValidationMessageFor(m => m.LastName)
    </div>

    <div class="editor-label">@Html.LabelFor(m => m.BirthDate)</div>
    <div class="editor-field">
        @Html.TextBoxFor(m => m.BirthDate)
        @Html.ValidationMessageFor(m => m.BirthDate)
    </div>
    
    <input type="submit" value="S'enregistrer" />
}

Enfin, assurez vous de charger les plugins jQuery UI et jQuery validate pour que cela fonctionne :

<head>
    <title>@ViewBag.Title</title>
    <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
    <script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery-ui-1.8.11.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
</head>

Dès lors, les actions de validation sont appelées en asynchrone lors de la saisie du formulaire, permettant ainsi une aide à la saisie avancée pour l’utilisateur !

image

NB : et comme toujours, la validation côté client ne doit absolument pas remplacer la validation côté serveur !

A bientôt Winking smile


read full post -

  • prev
  • next