Kaan Aslan
Erim Hısım
Microsoft Blog
Celal Kınoğlu
MSDN
SignalR ile Veritabanı Güncelleme Bildirimleri

Merhabalar,
Şöyle bir senaryo düşünelim. Bir kağıt fabrikamız var. Bu fabrikaya periyodik olarak ürünler gelmekte bizde şirket içerisinde kullanılan bir CRM yaptık ve bu CRM'in içerisinde stokları tuttuğumuz ayrı bir modülümüz var. Bu modül'ü el terminallerine uygun tasarladık ve fabrikanın depo elemanlarına verdik. Depo elemanları gelen ürünleri değerlerine göre (kg, cins, birim) programa işledi. Bizde bu işlenen değerleri database yazdırdıktan sonra hemen ofiste önünde CRM açık olan yöneticilerin web ekranına basacağız. Bunu asenkron yapacağız yani; depodaki eleman bir kayıt girdiğinde yukarıdaki muhasebe birimleri veya yöneticilerin görmesi için CRM'mimizin üstüne bildirim olarak bu kaydı göstereceğiz. Ama dediğim gibi bu asenkron olmalı yani sayfa reflesh olmamalı ya da ajaxla db sorgusu yapılmamalı tamamen canlı olmalı. Senaryomuz bu. Konuyu çok uzattım ama mantığa oturtmak için bu şekilde açıkladım. SignalR nedir bilmiyorsunuz google'a signalR yazdıgınız anda karşınıza chat, chatSignalR vs vs keywordlar gelecektir. Çünkü signalR asenkron (canlı) bir iletişime olanak sağlıyor. Bu yapacağımız örneği bir işte kullanmam gerekti bende sizlerle paylaşmak istedim :) Şimdi başlayalım.
 
Öncelikle vs'mizi açalım ve new project diyelim;
Daha sonra MVC şablonunu seçelim ve projemizi oluşturalım. Projeyi oluşturduktan sonra signalR kütüphanesini projemize dahil etmemiz gerekiyor. Tools > Nuget Package Manager > Package Manager Console açın ve consola şu kodu yazıp enter’a basın 
PM> Install-Package Microsoft.AspNet.SignalR
Projenize signalR’ı bir başka yoldan’da ekleyebilirsiniz. Tools>Nuget Package Manager>Manage Nuget Packages for Solution dedikten sonra arama kısmına SignalR yazıp kütüphaneyi kolayca projenize dahil edebilirsiniz.
Şimdi bu senaryoya uygun bir database oluşturalım. Var olan projenizin database’ine yeni bir tablo olarak ekleyip’te kullanabilirsiniz veya yeni bir db oluşturabilirsiniz. Yeni bir db oluşturdum adına BlogDemos dedim. Ve tablomuzun ismi’de Messages tablomuz şu şekilde olsun;
 
Evet databasemizi de hazırladıktan sonra hemen projemize dönelim ve Messages adında model class’ımızı oluşturalım.
 
public class Messages
    {
        public int MessageID { get; set; }
 
        public string Message { get; set; }
 
        public string EmptyMessage { get; set; }
 
        public DateTime MessageDate { get; set; }
    }
 
Field’larımızı yazdıkta sonra Projemiz’in kök dizinine Hubs diye bir klasör oluşturup içerisine MessagesHub adında bir class oluşturalım. Ve içerisinde connection stringimizi alıp Hub’a göndereceğimiz mesajın methodunu yazalım.(SendMessages()) Burada gözden kaçmaması gereken detay class’ımız Hub’dan kalıtılmış olmalı. Bunun için yukarıda
 
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs; ekli olmalı.
public class MessagesHub : Hub
    {
        private static string conString = ConfigurationManager.ConnectionStrings["DefaultConnection"].ToString();
        public void Hello()
        {
            Clients.All.hello();
        }
 
        [HubMethodName("sendMessages")]
        public static void SendMessages()
        {
            IHubContext context = GlobalHost.ConnectionManager.GetHubContext<MessagesHub>();
            context.Clients.All.updateMessages();
        }
 
        
    }
 
Bu işlemden sonra Model’imizin içerisinde MessagesRepository.cs adında bir class daha tanımlayalım. Ve burada Db’mizden mesajlarımızı okuyalım ve hub’a işleyelim. Bunu da şu şekilde yapıyoruz.
 
 
readonly string _connString = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
 
        public IEnumerable<Messages> GetAllMessages()
        {
            var messages = new List<Messages>();
            using (var connection = new SqlConnection(_connString))
            {
                connection.Open();
                using (var command = new SqlCommand(@"SELECT [MessageID], [Message], [EmptyMessage], [Date] FROM [dbo].[Messages]", connection))
                {
                    command.Notification = null;
 
                    var dependency = new SqlDependency(command);
                    dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
 
                    if (connection.State == ConnectionState.Closed)
                        connection.Open();
 
                    var reader = command.ExecuteReader();
//Mesajlarımızı okuyoruz. Aslında burada klasik Db işlemi yapıyoruz.
                    while (reader.Read())
                    {
                        messages.Add(item: new Messages { MessageID = (int)reader["MessageID"], Message = (string)reader["Message"], EmptyMessage =  reader["EmptyMessage"] != DBNull.Value ? (string) reader["EmptyMessage"] : "", MessageDate = Convert.ToDateTime(reader["Date"]) });
                    }
                }
              
            }
            return messages;
           
            
        }
 
Şimdi arkadaşlar en önemli yere geldik sql dependency yapacağımız metodu yazalım. 
 
  private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
        {
            if (e.Type == SqlNotificationType.Change)
            {
                MessagesHub.SendMessages();
            }
        }
 
Burada bir püf nokta var. SqlNotificationType.Change olduğunda dediğimiz anda gidip veritabanımızı rollback’e almaya calısıyor aslında bir nevi kitliyor. Aksi takdirde bu notification çalışmayacaktır. Databasenizin Rollback’e açık olması gerekmekte. Eğer açık değilse 
 
ALTER DATABASE BlogDemos SET ENABLE_BROKER WITH ROLLBACK IMMEDIATE ;
 
Bu query’i sqlînizde çalıştırmanız gerekmekte. Hem enable_broker yapıyor hemde rollback’e izin verdirtiyoruz. Bu izni vermediğiniz takdirde SqlNotification çalışmayacaktır.
 
 
Modelimizi oluşturduk Hubs class’ımızı yazdık sonra olarak Home/Index.cshtml view’imize geliyoruz ve içerisindeki html kodlarını silip diyoruz ki;
 
<div class="row">
    <div class="col-md-12">
       <div id="messagesTable"></div>
    </div>
</div>
 
@section Scripts{
<script src="/Scripts/jquery.signalR-2.1.1.js"></script>
    <script src="/signalr/hubs"></script>
 
<script type="text/javascript">
    $(function () {
        
        var notifications = $.connection.messagesHub;
 
        notifications.client.updateMessages = function () {
            getAllMessages()
           
        };
        // Bağlantı başlangıç
        $.connection.hub.start().done(function () {
            alert("connection started");
            getAllMessages();
        }).fail(function (e) {
            alert(e);
        });
    });
 
 
    function getAllMessages()
    {
        var tbl = $('#messagesTable');
        $.ajax({ 
            url: '/home/GetMessages',
            contentType: 'application/html ; charset:utf-8',
            type: 'GET',
            dataType: 'html'
        }).success(function (result) {
            tbl.empty().append(result);
        }).error(function () {
            
        });
    }
</script>
 
}
 
Ardın’dan HomeController’a gidip GetMessages Action methodumuzu yazıyoruz. 
 
  public ActionResult GetMessages()
        {
            MessagesRepository _messageRepository = new MessagesRepository();
            return PartialView("_MessagesList", _messageRepository.GetAllMessages());
        }
 
Shared klasörümüzün altında _MessagesList adında bir partialView oluşturuyoruz. Ve burada klasik data listeleme işini yapıyoruz.
 
 
@model IEnumerable<SignalRDbUpdates.Models.Messages>
 
<p>
    @Html.ActionLink("Create New", "Create")
</p>
<table class="table">
    <tr>
        <th>@Html.DisplayNameFor(model => model.MessageID)</th>
        <th>
            @Html.DisplayNameFor(model => model.Message)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.EmptyMessage)
        </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.Message)
        </td>
        <th>
            @Html.DisplayFor(modelItem => item.EmptyMessage)
        </th>
        <td>
            @Html.DisplayFor(modelItem => item.MessageDate)
        </td>
        
    </tr>
}
 
</table>
 
Ve büyük bir zevk ile projemizi debug edip test etmeye baslıyoruz. Sql’de message tablomuza bir kayıt gidiyoruz ve o sırada sayfamıza bakıyoruz bir de görüyoruz ki o da ne woww! 
 
 
Projenin tüm kaynak kodlarını ekte aşağıdan edinebilirsiniz. Db’yi kendiniz 2 dk’da oluşturabilirsiniz onada üşeniyorsanız alttaki sorguyu çalıştırın 
 
USE [BlogDemos]
GO
 
/****** Object:  Table [dbo].[Messages]    Script Date: 10/16/2014 12:43:55 ******/
SET ANSI_NULLS ON
GO
 
SET QUOTED_IDENTIFIER ON
GO
 
CREATE TABLE [dbo].[Messages](
[MessageID] [int] IDENTITY(1,1) NOT NULL,
[Message] [nvarchar](50) NULL,
[EmptyMessage] [nvarchar](50) NULL,
[Date] [datetime] NULL,
 CONSTRAINT [PK_Messages] PRIMARY KEY CLUSTERED 
(
[MessageID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
 
GO
 
ALTER TABLE [dbo].[Messages] ADD  CONSTRAINT [DF_Messages_Date]  DEFAULT (getdate()) FOR [Date]
GO
 
Dosyayı indirmek için tıklayın. 

Bir daha ki yazıda görüşmek üzere!

 


27 Mayıs 2015 / Bu makale 5303 kez okunmuştur.

Bu Yazıya Yapılan Yorumlar
Yazan :ahmet
merhaba bu güncellemeyi herkese değilde sadece session ile oturum açmış bir kişiye ait tabloyu nasıl gösteririm

Yazan :Oğuzhan
Merhaba,Session'ı oluşturduktan sonra ilgili tabloyu çağıran bir metod oluşturup if sorgusuna sokabilirsin.İyi çalışmalar :)Yorumun için teşekkür ederim.

Yazan :hanifi kuzucu
örneği sizin dediğiniz gibi yaptım blogdemos adında bir vt açıp sıfırdan kendimde yazdım o da çalıştı. Lakin yeni bir veritabanı açıp yada eski çalışan bir veritabanına bunu uygulamak istediğimde veriler geliyor fakat veritabanında güncelleme yaptığımda ya veri girişi olduğunda ekranı tazelemiyor. hocam enamle broker komutunuda veriyorum. ama olmuyor. team id şifre versem bağlanıp bakma imkanınız olur m u?

Yazan :Gökhan
Merhaba, çok faydalı bir çalışma olmuş. Ancak C# asp.net ile de gösterebilirseniz çok güzel olur. Geliştirdiğim bir projede lazımda.

Yazan :Ayşe Sayarı
Merhabalar, ne denli teşekkür etsek azdır. Tek takıldığım nokta, eğer belli başlı kişilere gönderilecekse, tüm client lara göndermek yerine nasıl birkaç kişiye gönderim yapabiliriz ?Bu sorunun bir yanıtı var mı acaba ?Teşekkür ederim.

Yazan :Ayşe Sayarı
Merhabalar, ben Akif Tuncel Meslek lisesini yeni bitirdim ve kendi çabamla yazılım sektöründe ilerlemek için çaba gösteriyorum.Makalenizi zevkle okudum; Signal R teknolojisi üzerine deneyim sahibi değilim, sadece şunu danışmak istiyorum;Clients.User(ConnectionId).send(message); (bu doğru bir syntax mı) Dilim döndüğünce stackoverflow a sordum, bana verilen yanıtlar yüzeysel idi,Signal R nin kendine ait bir ConnectionUser tablosu olduğu, Start Hub esnasında bağlanan kullanıcı dolayısıyla oluşturulan ConnectionID nin Kendi database imde olan UserID ile birlikte tanıtılması gerektiği ancak bu şekildeClients.User(UserId).send(message); sözdiziminin geçerli olacağı paylaşıldı ama,Start Hub esnasında nasıl ConnectionID yi yakalayabileceğim (ki esas sorum bu idi)daha sonra da Signal R nin ConnectionUsers tablosuna kendi database imdeki kullanıcı id si ile birlikte nasıl tanıtabileceğimden pek bahsedilmediConnectionUsers.Add(ErdemBeyUserID, ConnectionID) gibi bir metod mu var anlayamadım açıkçası (bulamadım da bu saat oldu 5:00)Çok eşekkür ederim.


Sizde yorum yapabilirsiniz
İsminiz :
 
Mail Adresiniz :
   
Yorumunuz :