Affichage des articles dont le libellé est ASP.NET MVC. Afficher tous les articles
Affichage des articles dont le libellé est ASP.NET MVC. Afficher tous les articles

Live charting avec SignalR

Publié par Fabrice Michellonet sous le(s) label(s) , , , le 26 septembre 2011

SignalR is awesome ! Oui il va falloir vous habituer à entendre dire que cette librairie est un petit bijou.


tweet signalr

Mais qu'est-ce qu'elle a de si bien cette librairie SignalR ?

SignalR fournit une couche d'abstraction au-dessus des WebSocket et des long polling connections pour les développeurs .NET et fonctionne sous IIS sans avoir à installer un autre service à côté.

Jusqu'alors il existait bien des solutions commerciales de ces technos dans l'écosystème .NET mais aucune n'avait vraiment convaincu, souvent par manque d'intégration forte entre le client et le serveur; rendant la communication entre les deux fastidieuse.
Avec SignalR c'est tout le contraire, le code serveur et le code client ne semble faire qu'un. Les lignes de code glissent du serveur vers le navigateur, sans aucune lourdeur. C'est SIMPLE.

Comme d'hab, rien de mieux qu'un petit bout de code pour démontrer ça.

Bon, si vous avez lu le titre vous avez une petite idée de ce que l'on va coder...

Coté composants techniques, évidement SignalR pour la communication client/serveur et Highchart coté client pour dessiner le chart.

Après avoir créé un nouveau projet WebForm ou MVC ajoutez le package SignalR via nuget.
Intéressons-nous au code javascript. (A noter que le code lié à la configuration du chart à été omis a des fins de clarté.)

$(function () {
     var stockExchangeServer = $.connection.stockExchange;

    stockExchangeServer.drawShareValue = function (id, content) {

        if (id != this.Id) {
            return;
        }

        // set up the updating of the chart.
        var series = chart.series[0];
        var x = new Date(parseInt(content[0].Date.substr(6))).getTime(),
                y = content[0].Price;

        series.addPoint([x, y], true, true);
    };

    $.connection.hub.start(function () {
        stockExchangeServer.connect();
            .done(function (success) {
                if (success === false) {
                    console.log(":(");
                }
                console.log("connected");
            });
    });
});

Pour initier la communication avec le serveur, il suffit comme vous pouvez le voir d'utiliser la méthode $.connection.hub.start();
On se contente alors de faire appel à la méthode connect; nous implémenterons cette dernière côté serveur.... oui vous avez bien lu, côté serveur.

Remarquez également la définition de la méthode drawShareValue() qui se charge de dessiner un nouveau point dans le chart, nous ferons appel à elle dans le code serveur.


Côté serveur, nous allons définir un hub, la classe de base qui abstrait la communication client/serveur dans l'api SignalR.

public class StockExchange : Hub
    {
        private Timer _timer;

        private double _fowlerPrice = 10.2;

        private double VaryPrice()
        {
            Random rnd = new Random();
            _fowlerPrice = _fowlerPrice + (rnd.Next(-1, 2) * rnd.NextDouble());
            return _fowlerPrice;
        }

        public bool Connect()
        {
            // Set unique id for client.
            Caller.Id = Context.ClientId;

            _timer = new Timer { Interval = 1000 };

            _timer.Elapsed += (sender, e) => Send(
                new List<SharePrice>(new[]
                                         {
                                             new SharePrice
                                                 {
                                                     Date = DateTime.Now,
                                                     Price = VaryPrice(),
                                                     Share = new Share {Name = "Fowler-Corp"}
                                                 }
                                         }));
            _timer.Start();

            return true;
        }

        public void Send(IEnumerable<SharePrice> sp)
        {
            Clients.drawShareValue(Context.ClientId, sp);
        }
    }

Avez-vous remarqué la méthode Connect()? On y a fait appel à partir du code JS.
Et l'appel à drawShareValue(), fait bien référence à la méthode que l'on à définit dans le javascript. Enorme, non?

Tout cela est possible parce que SignalR utilise dans ses fondements le typage dynamic à laquelle vient s'ajouter une résolution de Propriété/Méthode "simple" par convention, évitant ainsi d'avoir à écrire du code verbeux.

Au final, voici ce que l'on peut obtenir...

chart

Bon ok, l'image ne bouge pas, il faut imaginer qu'un point vient s'ajouter chaque seconde dans le graph :)

Pour ceux qui voudrait jeter un coup d’œil à la solution entière, vous pouvez la récupérer sur Github

Avez-vous utilisé d'autres technos/framework pour faciliter les communications "temps réel" entre le serveur et le browser?

Custom ASP.NET MVC Project Template

Publié par Fabrice Michellonet sous le(s) label(s) , , le 21 juin 2011

Récemment, Phil Haack nous présentait dans un très bon post comment ajouter un template MVC3 personnalisé.

Dans ce post il lève le voile sur l'intégration de nuget dans Visual Studio. On y apprend que malheureusement par manque de temps l'intégration n'est que minimaliste et que seul les packages présent sur la machine (%ProgramFiles%\Microsoft ASP.NET\ASP.NET MVC 3\Packages) ne peuvent être installés.

Après avoir fait un peu joujou avec, voici quelques points complémentaires :

  • Pour télécharger un package nuget (extension nupkg), vous pouvez utiliser nuget package explorer

  • Il n'y a pas de résolution de dépendance entre package. Vous devez donc les ordonner dans la section WizardData :
    <WizardData>
        <packages>
            <package id="jQuery" version="1.5.1" />
            <package id="jQuery.Validation" version="1.8.0" />
            <package id="jQuery.UI.Combined" version="1.8.11" />
        </packages>
    </WizardData>
    

  • Les template de quickstart (plusieurs projets) fonctionnent dans cette configuration et peuvent tirer parti de l'installation de packages via nuget.

  • Il m'est arrivé a plusieurs reprise de noter que la commande
    devenv /installvstemplates
    
    ne suffisait a rafraichir le cache de template de Visual Studio. Un reboot de la machine remet tout dans l'ordre.

Happy Nuget!

Quickstart Telerik Extensions for ASP.NET MVC via Nuget

Publié par Fabrice Michellonet sous le(s) label(s) , le 31 mars 2011

Sans nul doute Nuget a grandement amélioré le process de déploiement/configuration/utilisation de bibliothèques tierces dans l'écosystème .NET.

Malheureusement, certain package ne sont pas parfait, et nécessite que l'on trifouille encore un peu dans la config pour que tout soit fonctionnel.
J'en ai personnellement fait l'expérience lorsque j'ai tenté d'utiliser les MVC Extensions de Telerik via Nuget, une excellente librairie de composants graphiques soit dit en passant.

Vous l'aurez compris, l'idée de ce post est de présenter les manipulations à faire pour pouvoir finaliser l'installation de la librairie de Telerik.

nuget  telerik mvc extensions

On commence par demander l'installation du package :

Install-Package TelerikMvcExtensions

Pas d'inquiétude, si cela prend un peu de temps, c'est normal; il y a un paquet de fichier a rapatrier puis à ajouter dans la solution.

Une fois que c'est fait, si vous ouvrez une vue, vous vous rendrez compte que malheureusement, l'intellisense ne vous propose rien de nouveau. Pire, s'il vous prenait l'envie de copier coller un exemple de code issue du site de Telerik, vous auriez un joli plantage.

Pour corriger tout ça, on va trifouiller dans le web.config. Ci-dessous les élèments à ajouter :


<configuration>
  <configSections>
    <sectionGroup name="telerik">
      <section name="webAssets" type="Telerik.Web.Mvc.Configuration.WebAssetConfigurationSection, Telerik.Web.Mvc" requirePermission="false" />
    </sectionGroup>

    <sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
      <section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
      <section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
    </sectionGroup>
  </configSections>

  <system.web>
    <pages>
      <namespaces>
        <add namespace="Telerik.Web.Mvc.UI" />
      </namespaces>
    </pages>

    <httpHandlers>
      <add verb="GET,HEAD" path="asset.axd" validate="false" type="Telerik.Web.Mvc.WebAssetHttpHandler, Telerik.Web.Mvc" />
    </httpHandlers>
  </system.web>

  <system.webServer>

    <handlers>
      <remove name="asset" />
      <add name="asset" preCondition="integratedMode" verb="GET,HEAD" path="asset.axd" type="Telerik.Web.Mvc.WebAssetHttpHandler, Telerik.Web.Mvc" />
    </handlers>

  </system.webServer>

  <telerik>
    <webAssets useTelerikContentDeliveryNetwork="false" />
  </telerik>

  <system.web.webPages.razor>
    <host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
    <pages pageBaseType="System.Web.Mvc.WebViewPage">
      <namespaces>
        <add namespace="System.Web.Mvc" />
        <add namespace="System.Web.Mvc.Ajax" />
        <add namespace="System.Web.Mvc.Html" />
        <add namespace="System.Web.Routing" />
        <add namespace="Telerik.Web.Mvc.UI" />
      </namespaces>
    </pages>
  </system.web.webPages.razor>

</configuration>

Ouf! ca y est on a rajouté tout ce qu'il nous manquait.

En ouvrant de nouveau une vue, l'intellisense devrait se mettre a vous proposer de nouvelles choses dans le namespace Telerik. Parfois, l'intellisense reste muet et je n'ai pas trouvé d'autres alternatives que de redemarrer mon Visual Studio 2010.

Edit -- 18/04
Evidemment, il nous faut également modifier la master page afin d'y ajouter les styles et les scripts de Telerik.

<head>
    ...
    @(Html.Telerik().StyleSheetRegistrar().DefaultGroup(group => group.Add("telerik.common.css").Add("telerik.windows7.css").Add("telerik.rtl.css").Combined(true).Compress(true)))
</head>

</body>
@(Html.Telerik().ScriptRegistrar().Globalization(true).DefaultGroup(group => group.Combined(true).Compress(true)))
</html>

Ok, ok m'sieur Michellonet c'est bien beau tout ça, mais pourquoi j'irais utiliser la librairie de Telerik... en deux mots!

Tout simplement parce que la grille est géniale, elle a toutes les fonctionnalités que l'on peut attendre d'une grille Web 3.0 :p

Razor plugin pour SyntaxHighlighter

Publié par Fabrice Michellonet sous le(s) label(s) , le 28 février 2011

Comme beaucoup d'entre vous j'utilise l'excellent SyntaxHighlighter d'Alex Gorbatchev, pour mettre en forme les snippets de code sur mon blog.

Bien qu'il dispose de nombreux plugin de prise en charge de language, parfois très ésotérique, je n'ai encore rien vu pour le language préféré de tous ceux qui font du MVC 3 ou du WebMatrix, j'ai nommé razor.

razor

Du coup, je me suis lancé et voici une toute première version téléchargeable ici

et voici un exemple de mise en forme

@model CustomMVCScaffolder.Models.Employee

@* This partial view defines form fields that will appear when creating and editing entities *@
@Html.LabelFor(model => model.NationalIdNumber)
@Html.EditorFor(model => model.NationalIdNumber) @Html.ValidationMessageFor(model => model.NationalIdNumber)
@foreach (var ourItem in ourList) { if (ourItem != ourList.First()) { outItem.Name } } @Html.ActionLink("Edit", "Edit", new { id=item.UserName }) | @Html.ActionLink("Details", "Details", new { id=item.UserName }) | @Html.ActionLink("Delete", "Delete", new { id=item.UserName }) @Html.Hidden("UserName", Model.UserName)

Voici comment l'utiliser :

  <pre style="brush:razor">
    @Html.Hidden("UserName", Model.UserName)
  </pre>

J'espère que cette petite contribution à la communauté viendra enjoliver les snippets de code que l'on voit ça et là.

Ho... j'oubliais, si vous avez des propositions pour améliorer la mise en forme, je suis preneur.

MVC3 - Utiliser facilement le Datepicker de JQuery.

Publié par Fabrice Michellonet sous le(s) label(s) le 18 février 2011

Un des points apportant le plus de productivité dans le framework ASP.NET MVC est surement l'avènement des Display & Editor Templates.

Je m'explique si vous suivez les best practices et que vos vues ressemblent plus ou moins à cela :

@model CustomMVCScaffolder.Models.Employee

@* This partial view defines form fields that will appear when creating and editing entities *@
@Html.LabelFor(model => model.NationalIdNumber)
@Html.EditorFor(model => model.NationalIdNumber) @Html.ValidationMessageFor(model => model.NationalIdNumber)
@Html.LabelFor(model => model.Login)
@Html.EditorFor(model => model.Login) @Html.ValidationMessageFor(model => model.Login)
@Html.LabelFor(model => model.JobTitle)
@Html.EditorFor(model => model.JobTitle) @Html.ValidationMessageFor(model => model.JobTitle)
@Html.LabelFor(model => model.BirthDate)
@Html.EditorFor(model => model.BirthDate) @Html.ValidationMessageFor(model => model.BirthDate)
@Html.LabelFor(model => model.Maried)
@Html.EditorFor(model => model.Maried) @Html.ValidationMessageFor(model => model.Maried)
@Html.LabelFor(model => model.Gender)
@Html.EditorFor(model => model.Gender) @Html.ValidationMessageFor(model => model.Gender)
@Html.LabelFor(model => model.HireDate)
@Html.EditorFor(model => model.HireDate) @Html.ValidationMessageFor(model => model.HireDate)

alors au runtime, le framework choisira le composant graphique HTML (Input, select etc...) le plus adapté à afficher les propriétés du model et ca sur toutes vos vues.

La bonne nouvelle, c'est que l'on peut créer nous même des templates pour les nouveaux types et encore mieux modifier les templates liés aux types de base (int, string, DateTime etc..)

Stop le blabla, place au Datepicker.

Ok, on commence par créer dossier un EditorTemplates sous Views/Shared et on y place une nouvelle vue que l'on nommera DateTime.cshtml.

adding an editor

@using MVCTest
@model DateTime?

@Html.Datepicker("", Model.HasValue ? Model.Value.ToShortDateString() : string.Empty, 0)

Remarquez la simplicité, on ne fait qu'appeler une méthode d'extension (que nous allons étudier de suite) et on formatte très légèrement la date (ToShortDateString).

Rajoutons une classe dans notre projet comportant le code suivant :

using System.Collections.Generic;
using System.Web.Mvc;

namespace MVCTest
{
    public static class JQueryHtmlHelpers
    {
        public static MvcHtmlString Datepicker(this HtmlHelper htmlHelper, string name, object value)
        {
            return System.Web.Mvc.Html.InputExtensions.TextBox(htmlHelper, name, value, new Dictionary { { "class", "datepicker" } });
        }
    }
}

Dans cette méthode d'extension, on se contente de demander le rendu d'une TextBox (input html) a laquelle on applique la class css datepicker.
Vous l'aurez compris il ne nous reste plus qu'a écrire un peu de JQuery dans la master page pour que toute les texbox du site ayant la class datepicker se transforment par magie en DatePicker.

$(document).ready(function () {
    $(".datepicker").datepicker();
});

Voila ce que ça donne :

Avant Apres

Je vous laisse imaginer tous les widgets que l'on peut intégrer facilement!