signalrlogo

Dans cet article, je vais expliquer comment afficher en temps réel les notifications de changement de base de données SQL Server en utilisant SignalR et SqlDependency.

Concrètement, la base de données peut être mise à jour ou synchronisée par un service Windows en arrière-plan, notre besoin est d’afficher les données mises à jour en temps réel. La suite de cet article vous montrera comment mettre en œuvre ce mécanisme dans une application ASP .Net MVC.

 

 

SignalR ?

En bref, SignalR est une bibliothèque client/serveur qui permet d’ajouter des fonctionnalités temps-réel. Elle se base sur les Websockets qui assurent le polling client moyennant un canal bidirectionnel et fullduplex entre le client et le serveur. Cette bibliothèque va masquer la complexité de la gestion des appels JavaScripts au serveur.

Hub?

Un Hub est une classe de SignalR côté serveur. Elle va se charger de gérer des appels entre clients et le serveur dans les deux sens.

SQL Dependency ?

Il existe 3 manières de gérer les notifications SQL dans les applications .Net :

  1. SqllNotificationRequest : gérer les notifications bas niveau : en plus d'activer le « Service Broker », il faut créer des QUEUE et des SERVICE dans la base de données
  2. SqlDependency : faire abstraction de la complexité bas niveau des fonctionnalités de notification et permet de détecter facilement les modifications qui sont faites sur la base de données ;
  3. SqlCacheDependency : utiliser implicitement un SqlDependency pour tout ce qui concerne la gestion de cache dans les applications ASP.NET.

Service Broker ?

Le Service Broker est un service qui permet l'activation des notifications de requêtes SQL. Il permet aussi la communication entre des instances SQL Server efficacement et sûrement.

Implémentation

Passons à la pratique : pour faire simple, je vais interroger directement la base de données depuis la classe Repository dans une application ASP .NET MVC, suivons les étapes suivantes :

  1.  Configuration de la base de données:
    Tout d’abord, vérifiez si le service broker est activé ou pas sur la base de données.

    select name, is_broker_enabled from sys.databases

    Si le service n’est pas activé, activez le avec la commande suivante :

    ALTER DATABASE Houda_NetSubscriber SET ENABLE_BROKER

    Reste à créer la table Messages dans la base de données:

    CREATE TABLE [dbo].[Messages](
    	[MessageID] [int] IDENTITY(1,1) NOT NULL,
    	[Message] [nvarchar](50) NULL,
    	[Date] [datetime] NULL)
  2. Définition de la chaîne de connexion dans le fichier de configuration web.config :
    <connectionStrings>
        <add name="DefaultConnection" connectionString="Data Source=dev-db-01;Initial Catalog=Houda_NetSubscriber;Integrated Security=True;MultipleActiveResultSets=True;" providerName="System.Data.SqlClient" />
      </connectionStrings>
    
  3. Installation SignalR depuis Nuget : Ouvrez la console du Package Manager et exécutez la commande suivante:
    Install-Package Microsoft.AspNet.SignalR
  4. Configuration SqlDependency :
    • Activer le listener au moment de l’initialisation de l’application et l’arrêter à la fin dans le fichier Global.asax.cs :
      protected void Application_Start()
      {
        //...
        SqlDependency.Start(connString);
      }
      
      protected void Application_End()
      {
         //Stop SQL dependency
         SqlDependency.Stop(connString);
      }
    • Ajoutez la classe Model « Message »
      public class Message
      {
         public int MessageID { get; set; }
         public string MessageTxt { get; set; }
         public DateTime MessageDate { get; set; }
      }
    • et la classe Repository pour obtenir les changements de données de la base SQL :
      public class MessageRepository
          {
              readonly string _cnxString = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
      
              public IEnumerable<Message> GetAllMessages()
              {
                  var messages = new List<Message>();
                  using (var connection = new SqlConnection(_cnxString))
                  {
                      connection.Open();
                      using (var command = new SqlCommand(@"SELECT [MessageID], [Message], [Date] FROM [dbo].[Messages]", connection))
                      {
                          command.Notification = null;
      
                          var dependency = new SqlDependency(command);
                          dependency.OnChange += new OnChangeEventHandler(DependencyOnChange);
      
                          if (connection.State == ConnectionState.Closed)
                              connection.Open();
      
                          var reader = command.ExecuteReader();
      
                          while (reader.Read())
                          {
                              messages.Add(item: new Message { MessageID = (int)reader["MessageID"], MessageTxt = (string)reader["Message"], MessageDate = Convert.ToDateTime(reader["Date"]) });
                          }
                      }
                    
                  }
                  return messages;
                 
                  
              }
      
              private void DependencyOnChange(object sender, SqlNotificationEventArgs e)
              {
                  if (e.Type == SqlNotificationType.Change)
                  {
                      MessageHub.SendMessage();
                  }
              }
          }

      Vous avez bien remarqué l'utilisation de la classe MessageHub, je vais vous donner dans la suite son implémentation.

  5. Création de la class Hub:
    public class MessageHub : Hub
        {
            private static string _cnxStr = ConfigurationManager.ConnectionStrings["DefaultConnection"].ToString();
            public void Hello()
            {
                Clients.All.hello();
            }
    
            [HubMethodName("SendMessage")]
            public static void SendMessage()
            {
                IHubContext context = GlobalHost.ConnectionManager.GetHubContext<MessageHub>();
                context.Clients.All.updateMessages();
            }
        }
  6. Enregistrer SignalR dans la classe de démarrage
    app.MapSignalR();
  7. Ajouter une action dans le controller qui permet de mettre à jour la vue quand les données
    public ActionResult GetMessages()
    {
       MessageRepository _messageRepository = new MessageRepository();
       return PartialView("UpdatedMessagesList", _messageRepository.GetAllMessages());
    }
  8. Ajouter les scripts SignalR dans la vue : dans notre exemple, nous allons créer une fonction GetMessages qui permet de retourner les données mises à jour et les afficher dans une table dans la vue principale
    @section Scripts{
    <script src="/Scripts/jquery.signalR-2.1.1.js"></script>
     <!--Référencer le script de la classe hub SignalR autogénéré -->
    <script src="/signalr/hubs"></script>
    <script type="text/javascript">
        $(function () {
            // Déclarer le proxy pour référencer la classe Hub.
            var notifications = $.connection.messageHub;
    
            // Créer une fonction que la classe Hub peut appeler pour diffuser les messages.
            notifications.client.updateMessages = function () {
                GetMessageAll()
               
            };
            // Ouvrir la connexion
            $.connection.hub.start().done(function () {
                GetMessageAll();
            }).fail(function (e) {
                alert(e);
            });
        });
    
    
        function GetMessageAll()
        {
            var dataTbl = $('#tblMessages');
            $.ajax({
                url: '/home/GetMessages',
                contentType: 'application/html ; charset:utf-8',
                type: 'GET',
                dataType: 'html'
            }).success(function (result) {
                dataTbl.empty().append(result);
            }).error(function () {});
        }
    </script>
    
    }

    La vue Principale contient simplement une division pour afficher le résultat contenu dans une PartialView qu'on verra dans la suite :

    <div class="row">
      <div id="tblMessages"></div>
    </div>
  9. Création de la vue partielle UpdatedMessagesList:
    @model IEnumerable<SignalRDbUpdates.Models.Message>
    
    <table class="table">
        <tr>
            <th>@Html.DisplayNameFor(model => model.MessageID)</th>
            <th>
                @Html.DisplayNameFor(model => model.MessageTxt)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.MessageDate)
            </th>
        </tr>
    
    @foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.MessageID)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.MessageTxt)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.MessageDate)
            </td>
            
        </tr>
    }
    </table>

Avec ces modifications, lorsque les données sont ajoutées, modifiées ou supprimées dans la table Messages, la méthode DependencyOnChange dans la classe Repository se déclenche.
Vous savez maintenant comment abonner une applications ASP .NET MVC à une base de données, de synchroniser vos données et de rafraîchir automatiquement les vues côté client.


1 commentaire

Fabrice · 6 juillet 2015 à 0 h 11 min

Bel article, super instructif.
J'ai repris votre cours, hélas mon application n'a pas pu tourner.
Le message d'erreur est survenu au niveau de la classe Repository.cs plus précisément la variable " var reader = command.ExecuteReader();" qui ne gère pas une exception.
Que faire??
Au besoin merci de bien vouloir nous laisser votre code source.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *