Derinlemesine yazılım eğitimleri için kanalımı takip edebilirsiniz...

Asp.NET MVC – SignalR ve SqlDependency Eşliğinde Notifications

Merhaba,

Günümüz web projeleri server ve development merkezinden çıkarak, kullanıcı merkezli bir hal almışlardır. Yani demem o ki, yapılan tüm işlemler ne kadar yoğunluk, süreç ve maliyet kavramlarıyla olgunlaşsada, bu olgulara karşılık kullanıcı olaya sadece vakit nakittir ilkesiyle yani “en kısa sürede” ifadesiyle bakmakta ve bu şekilde nitelendirerek tercih edilebilirlik özelliği göstermektedir. Haliyle projelerde maliyetle beraber süre ve ortakçısı performansı düşünen biz yazılım geliştiriciler, bu faktörlerin yanında kullanıcı perspektifindende olayı ele almalı ve bu faktörlerden soyutlanmış bir şekilde vakit kavramını dahada minimize edebilmek üzerine kafa yormalıyız.

İşte bu bahsettiğim yaklaşımda bir konuyla sizlere içerik sunuyor olacağım. Web sitemizin veritabanında olan herhangi bir olayı(ki bu olayı biz belirleyebiliyoruz) Client’lara otomatik olarak yansıtacak ve bu işlemi asenkron bir şekilde post-back işlemlerine girmeden gerçekleştirecek bir yapı üzerine konuşacağız. Bu yapının inşa sürecinde veritabanını takip edecek olan ve yakın zamanda üzerine kalem oynattığımız SqlDependency sınıfıyken, tüm işlemi asenkron bir şekilde yürütecek olan ve clientlar ile haberleşmemizi sağlayacak olan yapımız ise SignalR kütüphanesi olacaktır.

Yazımızda her konuda yaptığımız gibi örnek bir proje üzerinde seyredeceğiz. Hal böyleyken boş bir Asp.NET MVC projesi açınız ve “Home(Controller).cs” isminde bir Controller sınıfı ekleyiniz. Ayriyetten eklenen bu Controller’a “Index” isminde bir Action metod oluşturunuz ve Views katmanındaki .cshtml görüntüsünüde fiziksel olarak ekleyiniz. Bunların yanında bu adresteki SignalR NuGet paketini

Install-Package Microsoft.AspNet.SignalR -Version 2.0.3

komutunu ‘Package Manager Console’ penceresinde çalıştırarak projenize entegre edin. (Tabi SignalR paketini ‘Manage NuGet Packages’ isimli pencereden de projenize entegre edebilirsiniz. Bu işlemleri bildiğinizi ve yaptığınızı varsayarak hızlı geçiyorum.) Son olarak örneklendirmede kullanacağımız Northwind veritabanını SQL Server’a yükleyiniz.

İşte şimdi adımları seri bir şekilde takip etmeye ve makaleyi sindirmeye hazırsınız.

Adım 1

İlk olarak çalışacağımız veritabanının(Northwind) Service Broker bilgisi önemlidir.
Bu bilgiyi öğrenmek için

SELECT name, is_broker_enabled FROM sys.databases

komutunu çalıştırmanız yeterlidir.
Asp.NET MVC - SignalR ve SqlDependency Eşliğinde Notifications
Ekran görüntüsünde de gördüğünüz gibi “1” değerine sahip olan veritabanların Service Broker’ı aktif demektir. Yok eğer “0” değerini görüyorsanız

ALTER DATABASE Northwind SET ENABLE_BROKER

komutunu çalıştırmanız, o veritabanı için Service Broker’ı aktifleştirmeniz için yeterlidir.

Adım 2

Yapının ilerisinde kullanmak için veritabanı provider bilgisini web.config dosyasına aşağıdaki gibi taşıyoruz.

<configuration>
  <connectionStrings>
    <add name="BaglantiProvider" providerName="System.Data.SqlClient" connectionString="Server=.;Database=Northwind;Trusted_Connection=True;" />
  </connectionStrings>
.
.
.
</configuration>

Adım 3

Projemizde App_Start klasörüne Startup.cs ekliyoruz ve içerisine aşağıdaki işlemleri gerçekleştiriyoruz.

    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.MapSignalR();
        }
    }

Adım 4

Ardından Global.asax dosyamızı açıyor ve içerisinde aşağıdaki gibi ayar gerçekleştiriyoruz.

    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            SqlDependency.Start(ConfigurationManager.ConnectionStrings["BaglantiProvider"].ConnectionString);
            AreaRegistration.RegisterAllAreas();
            RouteConfig.RegisterRoutes(RouteTable.Routes);
        }

        protected void Application_End()
        {
            SqlDependency.Stop(ConfigurationManager.ConnectionStrings["BaglantiProvider"].ConnectionString);
        }
    }

Burada web.config içerisine gömdüğümüz “BaglantiProvider” isimli sağlayıcının ConnectionString özelliği ile direkt olarak providerı get ediyor ve SqlDependency ile bu bağlantı yolunun takibini başlatıyoruz.

Adım 5

Ardından projede “SignalHub” isimli bir klasör oluşturup içerisine “PersonelHub” sınıfı ekliyoruz ve aşağıdaki gibi içini dolduruyoruz.

    public class PersonelHub : Hub
    {
        public static void Show()
        {
            IHubContext context = GlobalHost.ConnectionManager.GetHubContext<PersonelHub>();
            context.Clients.All.tetikle();
        }
    }

Adım 6

Models katmanında “Personel” isminde bir Entity(Class) ve “VeriTabaniIslemleri” isminde de bir class tanımlıyoruz.

    public class Personel
    {
        public int PersonelID { get; set; }
        public string Adi { get; set; }
        public string SoyAdi { get; set; }
        public string Unvan { get; set; }
    }
    public class VeriTabaniIslemleri
    {
        public IEnumerable<Personel> PersonelleriGetir()
        {
            using (SqlConnection baglanti = new SqlConnection(ConfigurationManager.ConnectionStrings["BaglantiProvider"].ConnectionString))
            {
                if (baglanti.State == ConnectionState.Closed)
                    baglanti.Open();
                using (SqlCommand cmd = new SqlCommand(@"SELECT [PERSONELID], [ADİ], [SOYADİ], [UNVAN] from [dbo].[PERSONELLER]", baglanti))
                {
                    SqlDependency dependency = new SqlDependency(cmd);
                    dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
                    using (SqlDataReader dr = cmd.ExecuteReader())
                    {
                        List<Personel> Personeller = new List<Personel>();
                        while (dr.Read())
                        {
                            Personeller.Add(new Personel
                            {
                                Adi = dr["Adi"].ToString(),
                                PersonelID = Convert.ToInt32(dr["PersonelID"].ToString()),
                                SoyAdi = dr["SoyAdi"].ToString(),
                                Unvan = dr["Unvan"].ToString()
                            });
                        }
                        return Personeller;
                    }
                }
            }
        }
        private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
        {
            PersonelHub.Show();
        }
    }

Gördüğünüz gibi “VeriTabaniIslemleri” sınıfı içerisinde “PersonelleriGetir” isimli metod ile veritabanındaki personelleri liste olarak elde etmekteyiz.

Adım 7

Index Action’ının .cshtml görüntüsünü aşağıdaki gibi düzenleyelim.

@{
    Layout = null;
}
<!DOCTYPE html>
<html>
<head>
    <title>Personeller</title>
    <script src="~/scripts/jquery-1.10.2.min.js"></script>
    <script src="~/scripts/jquery.signalR-2.2.0.min.js"></script>
    <script src="~/signalr/hubs" type="text/javascript"></script>
    <script type="text/javascript">
        $(function () {
            var pers = $.connection.personelHub;

            pers.client.tetikle = function () {
                getData();
            };

            $.connection.hub.start();
            getData();
        });

        function getData() {
            var tbl = $('#PersonelListesi');
            $.ajax({
                url: '@Url.Action("PersonelleriGetir", "Home")',
                type: 'GET',
                datatype: 'json',
                success: function (data) {
                    tbl.html(data);
                }
            });
        }
    </script>
</head>
<body>
    <div id="PersonelListesi">

    </div>
</body>
</html>

Burada JavaScript komutlarıyla “personelHub” isminde bir Hub oluşturuyoruz ve “PersonelHub.cs” sınıfımızda dinamik olarak üretilen “tetikle” değerine bu Hub’ı bağlıyoruz. Haliyle bu ilişkide bir fonksiyon devrededir.

Yani ne zaman “PersonelHub.cs” sınıfındaki Show metodu tetiklenir, Client’lar da bu fonksiyon tetiklenecektir.

Adım 8

“Helper” isminde bir class açın ve içerisinde aşağıdaki işlemleri yapın.

    public class Helper
    {
        public static string RazorViewRender(object model, string filePath)
        {
            var sw = new StringWriter();
            var context = new HttpContextWrapper(HttpContext.Current);
            var routeData = new RouteData();
            var controllerContext = new ControllerContext(new RequestContext(context, routeData), new HomeController());
            var razor = new RazorView(controllerContext, filePath, null, false, null);
            razor.Render(new ViewContext(controllerContext, razor, new ViewDataDictionary(model), new TempDataDictionary(), sw), sw);
            return sw.ToString();
        }
    }

Bu sınıfımız Asp.NET MVC – Programatik Olarak .cshtml Dosyasını Render Etmek başlıklı makalemde de bahsettiğim gibi programatik olarak View render etmemizi sağlayacaktır.

Adım 9

7. adımdaki “PersonelleriGetir” Action’ını Controller’da oluşturun ve aşağıdaki gibi içerisini doldurun.

    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        public string PersonelleriGetir()
        {
            VeriTabaniIslemleri Veri = new VeriTabaniIslemleri();
            var Personeller = Veri.PersonelleriGetir();
            return Helper.RazorViewRender(Personeller, "~/Views/Shared/_PersonelListesi.cshtml");
        }
    }

Adım 10

9. adımdaki “_PersonelListesi.cshtml” dosyasını oluşturun ve içeriğini aşağıdaki gibi oluşturun.

@model IEnumerable<SingelRDBNotify.Models.Entitys.Personel>

<table class="table">
    <tr>
        <th>
            Adı
        </th>
        <th>
            Soyadı
        </th>
        <th>
            Ünvan
        </th>
    </tr>

    @foreach (var item in Model)
    {
        <tr>
            <td>
                @item.Adi
            </td>
            <td>
                @item.SoyAdi
            </td>
            <td>
                @item.Unvan
            </td>
        </tr>
    }
</table>

Bu adımdan da sonra projenizi derleyip çalıştırınız. Aşağıdaki videoda olduğu gibi Personeller tablosundaki verilerle oynayınız ve Client’taki değişiklikleri takip ediniz.

Ayriyetten projemizin birden fazla Client üzerinde nasıl çalıştığını aşağıdaki videodan da inceleyebilirsiniz.

Bir yazımızın daha sonuna gelmiş bulunmaktayız. Bol bol faydalanmanız dileğiyle…
Okuduğunuz için teşekkürlerimi sunarım…
Bir sonraki makalemizde görüşmek üzere…
İyi çalışmalar…

Bunlar da hoşunuza gidebilir...

12 Cevaplar

  1. Ali Namlı dedi ki:

    Anlatımınız için teşekkürler.
    Uygulamamda kullandım ancak IIS server üzerinden aynı ağda 2 kişi aynı anda bağlandığında sayfa donuyor uzun süre sonra değişiklikler güncelleniyor. Localhostta hiçbir sorun olmuyor.
    Yardımcı Olabilir misiniz?

  2. Mehmet dedi ki:

    Bu güzel anlatım için teşekkürler, katmanlı mimaride (ör. öğrenci takip) bu örneği nasıl uygulayabiliriz. Mimari yapısı (Model Katmanı(Model-Controller)-Api Katmanı-Ui Katmanı) ve Sorguları Stored Procedure..Api işin içine girince takıldım kaldım. bir öneriniz yada paylaşabileceğiniz bir örnek varmı.saygılarımla.

  3. mehmet_bil dedi ki:

    Bu örneği Api İle nasıl yaparız. database i Api ile nasıl dinleriz.

  4. Hale dedi ki:

    Merhaba, bütün adımları uygulamama rağmen application start olduğunda signalr çalışırken veritabanındaki tabloda değişiklik olduğunda onchange methodu tetiklenmiyor neden olabilir ?

  5. wizz dedi ki:

    Merhaba,
    SqlDependency olmadan Hubın clientlara belirli aralıklarda veri gönderimini nasıl yapabiliriz.
    Dakikada bir gidip client lara veri gönderimi yapsın şeklinde

  6. Süphan SAVAŞ dedi ki:

    Hocam merhabalar sizin makalenizden yola çıkarak asp net core mvc de bir uygulama geliştirdim katmanlı mimaride siparişler listesini şu şekilde getirmekteyim ; viewcomponent tasarladım bir adet ve uygulama çalışınca viewcomponent tetikleniyor ve veritabanından ilgili siparişleri getiriyor ve ben üstünde sizin yaptığınız gibi sqldependency ile ilgili tablodan verileri çekiyorum ve onchange metodunda SignalR’ın ilgili hub’ı tetikleniyor tek bir kullanıcıda çalışıyor fakat sayfayı yenilediğim zaman sqlde bir değişiklik olduğu zaman sayfa yenileme sayısı kadar ilgili metotlar tetikleniyor ve bu durum çok kötü bir senaryo oluşturuyor ve benim uygulamamda farklı farklı userlar login olabilmekte örnek veriyorum 10 tane user aynı anda sipariş oluşturup izleme yapıyor ve bir değişiklik olduğu zaman anında görmek istiyor fakat ben bu senaryoda signalr da bunu yapamadım sizden bu konuda yardım talep ediyorum. SignalR hubda OnConnectedAsync() metodunda Context.ConnectionId’yi yakalıyorum fakat sqldependency onchange metodu tetiklendiğinde context boş geliyor ne yapacağımı bilemedim.

  7. TAMER dedi ki:

    SIGNALR WPF FROMLARINDA NASIL KULLANABILRIZ

  8. Şevval dedi ki:

    Hocam merhaba kaynağınızı projemde denedim fakat SingelRDBNotify kısmında derleme hatadı alıyorum bağlantı sağlanmıyor çözümü nedir

  1. 07 Aralık 2016

    […] Asp.NET MVC – SignalR ve SqlDependency Eşliğinde Notifications başlıklı makaleyi hatırlıyorsunuzdur ? Bu makale içeriğindeki yapıları yönergeleriyle beraber şuanda üzerinde çalıştığım bir projede uygulamaktayım. Lakin projemi derleyip çalıştırdığım zaman aşağıdaki hatayla karşılaşmaktayım. […]

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir