SignalR و SQLDependency

میخواهیم در این مقاله راجب نوتیفیکیشن های Real Time با استفاده از SignalR و SQLDependency صحبت کنیم. به نظر می رسید که SignalR مانند یک انتخاب فوری است چرا که فریمورکی را برای کد سمت سرور جهت push کردن بلادرنگ داده ها به کلاینت هایی که متصل هستند، فراهم می کند.

در یکی از پروژه هایی که ما روی آن کار می کردیم، لازم بود که نوتیفیکیشن ها به صورت Real Time (بلادرنگ) به کاربر نمایش داده شوند.

اما نوتیفیکیشن ها می توانند به طور مستقیم از طریق سیستم های خارجی به دیتابیس ارسال شوند.

در این مورد نیاز به دریافت رکوردهای جدید واردشده و ارسال آن به کلاینت متصل وجود دارد. این مقاله درباره نحوه اتصال همه این قسمت ها به یکدیگر و ساخت یک راه حل نوتیفیکیشن بلادرنگ بحث می کند.

معرفی اجمالی درباره SignalR و SQLDependency

SignalR یک کتابخانه .NET است که فرایند اضافه کردن قابلیت بلادرنگ به اپلیکیشن های وب را ساده می کند. به جای اینکه سرور برای درخواست داده ها از سرور منتظر کلاینت باشد، برای ما فریمورکی را برای کد سمت سرور به منظور فراخوانی متد سمت کلاینت فراهم می کند.

برای ما یک API برای ایجاد سرور به کلاینت (RPC) ایجاد می کند که متدهای جاوا اسکریپت را از کد .NET سمت سرور فراخوانی می نماید. همچنین برای متدهایی برای اتصال، قطع اتصال، رویدادهای قطع اتصال، ارسال به همه کلاینت های متصل، ارسال به یک کلاینت مشخص و… را فراهم می کند.

برای اطلاعات بیشتر درباره SignalR می توانید به آدرس https://www.mspsoft.com/1396/01/05/started-signalr-in-asp-net مراجعه نمایید.

SQL Dependency

SQL Dependency یک کلاس تهیه شده توسط فریمورک .NET است که روی زیرساخت service broker تعبیه شده است که به اپلیکیشن این امکان را می دهد که زمانی که داده ها در دیتابیس (Microsoft SQL Server) تغییر کرد، مطلع شود.

Microsoft SQL Server به اپلیکیشن های فریمورک .NET اجازه می دهد که به SQL Server دستور SQL ارسال کرده و اگر اجرای دستور نتیجه متفاوتی را تولید کرد، درخواست نوتیفیکشن نمایند.

SQL Server به اپلیکیشن های فریمورک .NET اجازه تنظیم دستورات SELECT و EXECUTE را می دهد.

شروع کار

SQL Server

کار را با ایجاد یک جدول در دیتابیس شروع می کنیم.

SignalR و SQLDependency

ستون User ID شامل شناسه کاربری برای کاربری است که نوتیفیکیشن باید ارسال شود.

اسکریپت زیر را در دیتابیسی که جدول را در آن ایجاد کردیم، اجرا می کنیم، پس از آن SQL Server ارسال نوتیفیکیشن به اپلیکیشن .NET ای را که روی تغییرات جدول ALTER DATABASE [dbname] SET ENABLE_BROKER WITH ROLLBACK IMMEDIATE مشترک شده است، آغاز می کند. این تنها چیزی است که از سمت SQL Server لازم است.

ASP.NET MVC Web Application

حالا اجازه دهید که یک اپلیکیشن وب ایجاد نماییم. یک اپلیکیشن ابتدایی ASP.NET MVC 4 ایجاد می کنیم.

SignalR و SQLDependency

با راست کلیک روی فولدر Controllers و انتخاب add new controller یک “HomeController” ایجاد می کنیم.

SignalR و SQLDependency

روی فولدر Views راست کلیک کرده و یک فولدر جدید به نام “Home” ایجاد می کنیم.

SignalR و SQLDependency

روی فولدر “Home” جدیدی که ایجاد کردیم، راست کلیک کرده و گزینه Add و سپس View را انتخاب کرده و سپس نام آن را “index” گذاشته و روی Add کلیک می کنیم.

به Package Manager console رفته و دستور زیر را اجرا می کنیم که کتابخانه های ضروری و جاوا اسکریپت موردنیاز برای SignalR را می آورد..

 style=text-align: left Install-Package Microsoft.AspNet.SignalR

به فایل Layout.cshtml رفته و تمامی “@scripts.render” را حذف کرده و با کدهای زیر در بخش Head جایگزین می کنیم.

  link href=http://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css rel=stylesheet /
script src=https://code.jquery.com/jquery-1.11.2.min.js/script
script src=http://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js/script
script src=~/Scripts/jquery.signalR-2.2.0.min.js/script

حالا فایل index.cshtml درون فولدر Home را که ایجاده کرده ایم، باز می کنیم و سپس کد زیر را که پروکسی ایجاد شده توسط سرور برای فراخوانی کلاینت خواهد بود، اضافه می کنیم.


!--Reference the autogenerated SignalR hub script. --

script src=~/signalr/hubs/script

HTML موردنیاز برای نمایش لیست نوتیفیکیشن، تعداد و فرمی برای ارسال نوتیفیکیشن ها را ایجاد می نماییم. کد زیر را در index.cshtml اضافه شده ،در فولدر Home درون View اضافه می کنیم.

div style=width: 70%; padding: 20px
    div class=panel panel-primary
        div class=panel-heading

    ! – To show notification count--
            div style=float: left class=panel-titleNotifications/div
            div style=float: right class=badge id=cntNotifications/div
            div style=clear: both/div


        /div
        div class=panel-body
      ! – To show All the notifications--
            table class=table table-striped table-hover 
                thead
                    tr
                        th#/th
                        thText/th
                        thCreated Date/th
                    /tr
                /thead

                tbody id=notificationTab

                /tbody
            /table
        /div
    /div

    ! – Add panel notification to send notification, Make sure that user enters the user id of the domain they are logged into -- 
    div class=panel panel-primary
        div class=panel-heading
            h3 class=panel-titleCreate Notifications/h3
        /div
        div class=panel-body
            div class=form-group
                label class=control-label for=focusedInputNotification Text/label
                input class=form-control id=text type=text value=
            /div
            div class=form-group
                label class=control-label for=focusedInputSend To/label
                input class=form-control id=userName type=text value=
            /div
            a id=btnSend style=cursor: pointer class=btn btn-primarySend Notification/a
        /div
    /div
/div

حالا بیایید به کد سرور برگردیم و کدهایی برای واکشی نوتیفیکیشن ها از دیتابیس و اضافه کردن نوتیفیکیشن ها به دیتابیس ایجاد کنیم.

روی فایل پروژه راست کلیک کرده و یک فولدر جدید “Repository” و یک کلاس “NotificationRepository.cs” اضافه می کنیم.

لازم است که کد زیر را در فایل قرار دهیم. یک new item اضافه کرده و Data> ADO.NET Entity Data Model را انتخاب کرده و نامی برای آن انتخاب می کنیم.

SignalR و SQLDependency

در بخش بعدی “Generate from the database” را انتخاب می کنیم.

SignalR و SQLDependency

نام سرور، نام دیتابیس را به آن داده و Wizard را کامل می کنیم. “NotificationEntities” را با نامی که به entity داده ایم عوض می کنیم.

 public void AddNotification(string Text, string UserName)
{
    using (NotificationEntities ent = new NotificationEntities())
    {
        NotificationList obj = new NotificationList();
        obj.Text = Text;
        obj.UserID = UserName;
        obj.CreatedDate = DateTime.Now.ToUniversalTime();
        ent.NotificationLists.Add(obj);
        ent.SaveChanges();
    }
}

public List GetNotifications(string userName)
{

    using (NotificationEntities ent = new NotificationEntities())
    {
        return ent.NotificationLists.Where(e = e.UserID == userName).ToList();
    }
}

public List GetLatestNotifications(DateTime dt)
{

    using (NotificationEntities ent = new NotificationEntities())
    {
        if (dt == DateTime.MinValue)
        {
            return ent.NotificationLists.ToList();
        }
        else
        {
            DateTime dtUTC = dt.ToUniversalTime();
            return ent.NotificationLists.Where(e = e.CreatedDate  dtUTC).ToList();
        }
    }
}

کد بالا تا حدود زیادی قابل فهم است. خب ما سه متد ایجاد کردیم.

AddNotification برای اضافه کردن نوتیفیکیشن به دیتابیس، GetNotifications(username) برای دریافت نوتیفیکیشن ها برای نام کاربری مشخص شده.

درنهایت، متد GetLatestNotification(date time) که لیستی از نوتیفیکیشن ها را از آخرین اجرا انتخاب می کند تا رکوردهای اضافه شده بعد از هر اجرا را دریافت نماید، که در رویداد SQL dependency فراخوانی خواهد شد.

حالا، یک SignalR Hub می سازیم که کد سمت سرور می باشد که متد جاوا اسکریپت سمت کلاینت را برای نمایش نوتیفیکیشن فراخوانی می نماید.

روی فایل پروژه راست کلیک کرده و یک فولدر جدید با نام “Hubs” اضافه می کنیم. یک کلاس به نام “Notification.cs” ایجاد می کنیم. این کلاس حتما باید از کلاس Hub ارث بری نماید.

 public class NotificationHub: Hub
{
   // Code 

}

یک متد “SendNotification” ایجاد می کنیم که کلاینت ها برای ایجاد نوتیفیکیشن در دیتابیس آن را فراخوانی می کنند. کلاینت ها باید دو متغیر را ارسال نمایند، username برای اینکه نوتیفیکیشن باید به چه کسی ارسال شود.


 public class NotificationHub: Hub
{
   
        public void SendNotification(string message, string user)
        {
	//Create an instance of the Repository class
            NotificationRepository objRepository = new NotificationRepository();

	//Invoke the Add Notification method that we created in the repository to add the notification to the database 
            objRepository.AddNotification(message, user);          
        }

}

حالا متد onConnected را برای دریافت نوتیفیکیشن ها برای ورود کاربر و ارسال پاسخ override می نماییم.


 public class NotificationHub: Hub
{
   
        public void SendNotification(string message, string user)
        {
	//Create an instance of the Repository class
            NotificationRepository objRepository = new NotificationRepository();

	//Invoke the Add Notification method that we created in the repository to add the notification to the database 
            objRepository.AddNotification(message, user);          
        }

        public override System.Threading.Tasks.Task OnConnected()
        {
	//Create an instance of the Repository class
            NotificationRepository objRepository = new NotificationRepository();
     
	//refreshNotification is the client side method which will be writing in the future section. GetLogin() is a static extensions extract just the login name scrapping the domain name 
            Clients.User(Context.User.Identity.Name).refreshNotification(objRepository.GetNotifications(Context.User.Identity.GetLogin()));

            return base.OnConnected();

        }


}

public static class Extensions
    {
        public static string GetDomain(this IIdentity identity)
        {
            string s = identity.Name;
            int stop = s.IndexOf(\\);
            return (stop  -1) ? s.Substring(0, stop) : string.Empty;
        }

        public static string GetLogin(this IIdentity identity)
        {
            string s = identity.Name;
            int stop = s.IndexOf(\\);
            return (stop  -1) ? s.Substring(stop + 1, s.Length - stop - 1) : string.Empty;
        }
    }

حالا ما باید جدول را با تغییر SQL Dependency مرتبط کرده و SignalR hub را مقداردهی اولیه نماییم.

فایل Global.asax.cs را باز می کنیم. نکته غم انگیز درباره SQL Dependency این است که فقط رویدادی را اعلام می کند و می گوید که تغییری در جدول رخ داد اما درباره اینکه چه رکوردی اضافه یا تغییر داده شد اطلاعاتی نمی دهد.

ما باید دوباره به دیتابیس کوئری زده و تغییر ایجاد شده را دریافت کنیم.

یک متد void با نام “RegisterNotification” در فایل global.asax ایجاد می کنیم و آن را در متد application_start فراخوانی می نماییم.


private void RegisterNotification()
        {
	//Get the connection string from the Web.Config file. Make sure that the key exists and it is the connection string for the Notification Database and the NotificationList Table that we created

            string connectionString = ConfigurationManager.ConnectionStrings[NotificationConnection].ConnectionString;

	//We have selected the entire table as the command, so SQL Server executes this script and sees if there is a change in the result, raise the event
            string commandText = @
                                    Select
                                        dbo.NotificationList.ID,
                                        dbo.NotificationList.Text,
                                        dbo.NotificationList.UserID,
                                        dbo.NotificationList.CreatedDate                                      
                                    From
                                        dbo.NotificationList                                     
                                    ;

	//Start the SQL Dependency
            SqlDependency.Start(connectionString);
            using (SqlConnection connection = new SqlConnection(connectionString))
            {

                using (SqlCommand command = new SqlCommand(commandText, connection))
                {
                    connection.Open();
                    var sqlDependency = new SqlDependency(command);


                    sqlDependency.OnChange += new OnChangeEventHandler(sqlDependency_OnChange);

                    // NOTE: You have to execute the command, or the notification will never fire.
                    using (SqlDataReader reader = command.ExecuteReader())
                    {
                    }
                }
            }
        }

حالا رویداد OnChange را مدیریت می کنیم. بررسی می کنیم که اگر نوع رویداد “Insert” باشد، آخرین رکورد را دریافت کرده و آن را به کلاینت متصل ارسال می نماییم.

 DateTime LastRun;
     private void sqlDependency_OnChange(object sender, SqlNotificationEventArgs e)
     {

         if (e.Info == SqlNotificationInfo.Insert)
         {
 //This is how signalrHub can be accessed outside the SignalR Hub Notification.cs file
             var context = GlobalHost.ConnectionManager.GetHubContext();


             NotificationRepository objRepos = new NotificationRepository();

             List objList = objRepos.GetLatestNotifications(LastRun);

             LastRun = DateTime.Now.ToUniversalTime();


             foreach (var item in objList)
             {
      //replace domain name with your own domain name
                 context.Clients.User( + item.UserID).addLatestNotification(item);
             }

         }
       //Call the RegisterNotification method again
         RegisterNotification();
     }

برای معرفی کردن کلاس SignalR Hub به عنوان یک شی در این پروژه ما باید یک کلاس اضافه نماییم. این کلاس می تواند در همان فایل global.asax.cs اضافه شود.
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR();
}
}

[/csharp]

در فایل web.config یک کلید برای “owin:AppStartup” اضافه کرده و یک مقدار نام کامل قابل قبول از کلاس Startup را که در بخش قبلی ساختیم، اضافه می کنیم.

 appSettings
	…
   
    add key=owin:AppStartup value=Notifications.Signalr.Startup /
  /appSettings

درنهایت، برای سمت کلاینت، ایجاد آن دو متدی که سرور برای نمایش لیستی از نوتیفیکیشن ها و ارسال آخرین مورد اضافه شده آن ها فراخوانی می کند.

 script
    $(function () {

        // Reference the auto-generated proxy for the hub.  

        var notification = $.connection.notificationHub;

 // Client side method for receiving the list of notifications on the connected event from the server
        notification.client.refreshNotification = function (data) {
            $(#notificationTab).empty();
            $(#cntNotifications).text(data.length);
            for (var i = 0; i  data.length; i++) {
                $(#notificationTab).append(tr td  + data[i].ID + /td td + data[i].Text + /td td + data[i].CreatedDate + /td/tr);
            }
        }
     
//Client side method which will be invoked from the Global.asax.cs file. 
        notification.client.addLatestNotification = function (data) {
            $(#cntNotifications).text($(#cntNotifications).text() + 1);
            $(#notificationTab).append(tr td  + data.ID + /td td + data.Text + /td td + data.CreatedDate + /td/tr);
        }

        // Start the connection.
        $.connection.hub.start().done(function () {

	//When the send button is clicked get the text and user name and send it to server. 
            $(#btnSend).click(function () {
                notification.server.sendNotification($(#text).val(), $(#userName).val());
            });

        });
    });
/script

در اینجا کدنویسی ما به پایان رسید.

تصاویر زیر چیزهایی را که در راه حل سطح بالا اتفاق می افتد، نشان می دهد.

SignalR و SQLDependency

مرحله اول – مقداردهی اولیه (کلاینت ها متصل می شوند)

SignalR و SQLDependency

کلاینت ۱ نوتیفیکیشنی به کلاینت متصل ۳ ارسال می کند – سناریوی ۱

SignalR و SQLDependency

سرویس خارجی یک نوتیفیکیشن به کلاینت ۲ ارسال می کند – سناریوی ۲

فاطمه زکایی

فاطمه زکایی هستم. فارغ التحصیل کارشناسی مهندسی نرم افزار، مدت سه سال هست که در زمینه توسعه اپلیکیشن های تحت وب و اندروید و همچنین تولید محتوای تخصصی برنامه نویسی تحت وب و اندروید در مجموعه mspsoft در خدمت شما هستم.

نوشته‌های مرتبط

دیدگاه‌ها

*
*

این سایت از اکیسمت برای کاهش هرزنامه استفاده می کند. بیاموزید که چگونه اطلاعات دیدگاه های شما پردازش می‌شوند.

    جمال جلالی پاسخ

    سلام اصل پروژه رو نمیذارید برای دانلود ؟
    ممنون