تغییرات فوری Database با استفاده از SignalR

سه شنبه 16 شهریور 1395

تکنولوژی SignalR این قابلیت را به می دهد که ما توابع real time را در وب خود اضافه کنیم .این به آن معناست که زمان اضافه کردن داده یا محتوا به سرور بلافاصله پس از درخواست انجام می شود.

تغییرات فوری Database با استفاده از SignalR

قسمت کدها

مرحله اول : ایجاد بانک اطلاعاتی و جدول ها

در این قسمت میخواهیم با استفاده از قطعه کد زیر یک بانک اطلاعاتی به نام ExperimentalDB و یک جدول به نام Student ایجاد کنیم :

USE [ExperimentalDB]
GO

/****** Object:  Table [dbo].[Student]    Script Date: 8/19/2016 9:12:29 AM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

SET ANSI_PADDING ON
GO

CREATE TABLE [dbo].[Student](
        [StudentID] [int] IDENTITY(1,1) NOT NULL,
        [StudentName] [varchar](50) NOT NULL,
        [DOB] [datetime] NOT NULL,
        [Weight] [int] NULL,
 CONSTRAINT [PK_Messages] PRIMARY KEY CLUSTERED 
(
        [StudentID] 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

SET ANSI_PADDING OFF
GO

فعال کردن Service Broker  در بانک اطلاعاتی

Service Broker یکی از امکانات معرفی شده در SQL است که برای اولین بار در SQL Server 2005   معرفی شد.با استفاده از این ویژگی ، پردازش های داخلی و خارجی میتواند پیام هایی را به صورت نا همزمان  ارسال و دریافت  کنند که با استفاده از افزونه  Transact-SQL Data Manipulation Language (DML) ایمن سازی شده اند.این عمل یک طبقه بندی و مکانیزیم پیام رسانی برای مدل برنامه نویسی نا همزمان است .

در این ویژگی زمانی که ما عملیات افزودن،ویرایش،حذف راانجام دهیم همان لحظه تغییرات اعمال می شود و SQLDependencباید قابل شناسایی باشد. (Servicen Broker) به جای یک معماری Broker که حین Publish شدن رویداد های SQL Dependency رابه عنوان یک subscriber فعال می شود و تغیرات را اعمال می کند . با استفاده از شی SqlDependency ، برنامه میتواند  از طریق OnChangeEventHandler  notification  ها را ایجاد و ثبت کند .

همان طور که مشاهده کنید دستور زیر فعال یا غیر فعال بودن Service Broker را چک می کند.

[ExperimentalDB]

SELECT NAME, IS_BROKER_ENABLED FROM SYS.DATABASES
WHERE NAME='ExperimentalDB'

Result
-------
NAME                    IS_BROKER_ENABLED
--------------          -----------------
ExperimentalDB                  0

حال Service Broker  بر روی [ExperimentalDB] فعال شده است .

--Enable the Service Broker for ExperimentalDB
ALTER DATABASE ExperimentalDB SET ENABLE_BROKER WITH ROLLBACK IMMEDIATE ;

مرحله سوم: یک پروژه از نوع MVC ایجاد می کنیم .

تصویر زیر ساختار پروژه ما را نشان می دهد:

فایل هایی که زیرشان خط کشیده شده است  اصلاح شده اند .

حال میخواهیم از طریق Nuget package بسته SinalR را نصب کنیم ، کد زیر را در Package Manager Console وارد کنید تا بسته به پروژه اضافه شود .

Install-Package Microsoft.AspNet.SignalR

فایل StudentRepository.cs

using SignalRInstantDbChangesDemo.Hubs;
using SignalRInstantDbChangesDemo.Models;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Linq;

namespace SignalRInstantDbChangesDemo.DataAccess
{
    public class StudentRepository
    {
        public static List<Student> GetStudentRecords()
        {
            var lstStudentRecords = new List<Student>();
            string dbConnectionSettings = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;

            using (var dbConnection = new SqlConnection(dbConnectionSettings))
            {
                dbConnection.Open();

                var sqlCommandText = @"SELECT [StudentID],[StudentName],[DOB],[Weight] FROM [dbo].[Student]";

                using (var sqlCommand = new SqlCommand(sqlCommandText, dbConnection))
                {
                    AddSQLDependency(sqlCommand);                   

                    if (dbConnection.State == ConnectionState.Closed)
                        dbConnection.Open();

                    var reader = sqlCommand.ExecuteReader();
                    lstStudentRecords = GetStudentRecords(reader);
                }
            }
            return lstStudentRecords; 
        }

        /// <summary>
        /// Adds SQLDependency for change notification and passes the information to Student Hub for broadcasting
        /// </summary>
        /// <param name="sqlCommand"></param>
        private static void AddSQLDependency(SqlCommand sqlCommand)
        {
            sqlCommand.Notification = null;

            var dependency = new SqlDependency(sqlCommand);

            dependency.OnChange += (sender, sqlNotificationEvents) =>
            {
                if (sqlNotificationEvents.Type == SqlNotificationType.Change)
                {
                    StudentHub.SendUptodateInformation(sqlNotificationEvents.Info.ToString());
                }
            };
        }

        /// <summary>
        /// Fills the Student Records
        /// </summary>
        /// <param name="reader"></param>
        /// <returns></returns>
        private static List<Student> GetStudentRecords(SqlDataReader reader)
        {
            var lstStudentRecords = new List<Student>();
            var dt = new DataTable();
            dt.Load(reader);
            dt
                .AsEnumerable()
                .ToList()
                .ForEach
                (
                    i => lstStudentRecords.Add(new Student()
                    {
                        StudentID = (int)i["StudentID"]
                            , StudentName = (string)i["StudentName"]
                            , DOB = Convert.ToDateTime(i["DOB"])
                            , Weight = (int)i["Weight"]
                    })
                );
            return lstStudentRecords;
        }       
    }
}

متد GetStudentRecords()  رکورد های Student را از بانک اطلاعاتی واکشی می کند . برای انجام این کار میتوان از روش ADO.NET   و روش های دیگر استفاده کرد ما در این قسمت از ADO.NET  استفاده کرده ایم .

در متد AddSQLDependency ، ما SQLDependency را اضافه کرده ایم . OnChangeEventHandler زمانی اجرا می شود که  notification   هر دستور با،  شی System.Data.SqlClient.SqlDependency  دریافت شده باشد .به محض این که تغییری بر روی بانک اطلاعاتی ایجاد شود (افزودن،ویرایش،حذف)، OnChangeEventHandler به سرعت اطالاعات را به متد SendUptodateInformation در StudentHub ارسال می کند.

dependency.OnChange += (sender, sqlNotificationEvents) =>
{
    if (sqlNotificationEvents.Type == SqlNotificationType.Change)
    {
        StudentHub.SendUptodateInformation(sqlNotificationEvents.Info.ToString());
    }
};

فایل StudentHub.cs

using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;

namespace SignalRInstantDbChangesDemo.Hubs
{
    public class StudentHub : Hub
    {
        [HubMethodName("sendUptodateInformation")]
        public static void SendUptodateInformation(string action)
        {
            IHubContext context = GlobalHost.ConnectionManager.GetHubContext<StudentHub>();

            // the updateStudentInformation method will update the connected client about any recent changes in the server data
            context.Clients.All.updateStudentInformation(action);
        }
    }
}

کلاس StudentHun.cs را برای مقاوم ساختن اتصال بین Clident  و Server  استفاده می شود.این روند را Remote Procedure Call (RPC)  مینامند که  Client  و Server  و بلعکس  را به هم متصل می کند .متد updateStudentInformation  اتصال سمت Client و تغیراتی که به تازگی بر روی  داده های سرور رخ داده است  را بروزرسانی می کند.

پوشه Models شامل مدل Student  و سروریس آن است .

فایل Student.cs   شامل مدل Student   است :

 public class Student
    {
        [DisplayName("شماره")]

        public int StudentID { get; set; }
        [DisplayName("نام")]
        public string StudentName { get; set; }
        [DisplayName("زمان")]
        public DateTime DOB { get; set; }
        [DisplayName("وزن")]
        public int Weight { get; set; }
    }

IStudentService  اینترفیسی است که شامل سرویس ها می شود:

public interface IStudentService
{
    List<Student> GetStudentDetails();
}

پیاده سازی یک پارچه در فایل StudentService.cs مشاهده می شود:

public class StudentService : IStudentService
{        
    public List<Student> GetStudentDetails()
    {
        return StudentRepository.GetStudentRecords();
    }
}

کد زیر تشکیل دهنده HomeController است :

using SignalRInstantDbChangesDemo.Models;
using System.Web.Mvc;

namespace SignalRDbUpdates.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        } 

        public ActionResult SendStudentNotification()
        {
            IStudentService studentService = new StudentService();           
            return PartialView("_StudentList", studentService.GetStudentDetails());
        }
    }
}

_StudentList  یک Partial View   است که رکورد ها را نمایش می دهد.

فایل Global.asax.cs حائز اهمیت است چون در این فایل ما SqlDependency ، را Start   و Stop می کنیم .

using System.Configuration;
using System.Data.SqlClient;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;

namespace SignalRDbUpdates
{
    public class MvcApplication : System.Web.HttpApplication
    {
        string connString = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;

        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
            GlobalConfiguration.Configure(WebApiConfig.Register);

            //Start SqlDependency with application initialization
            SqlDependency.Start(connString);
        }

        protected void Application_End()
        {
            //Stop SqlDependency
            SqlDependency.Stop(connString);
        }
    }
}

متد SqlDependency.Start(string connectionString زمانی که تغییر dependency ،  از نمونه SQL Server  که با connection string مشخص شده است اطلاع رسانی شود شروع با کار می کند.

متد SqlDependency.Stop(string connectionString)  زمانی متوقف می شود که Connection مشخص شده در System.Data.SqlClient.SqlDependency.Start فراخوانی شود.

در مرحله بعد ما نیاز به  ثبت SignalR در کلاس Startup.cs داریم . برای پیدا کردن کلاس Startup از ساختار زیر استفاده می کنیم :

[assembly: OwinStartupAttribute(typeof(SignalRDbUpdates.Startup))]

این کد نشان دهنده اجرا شدن کلاس به عنوان یک تابع دریافت یک نمونه از Microsoft.Owin.IowinContext است.هنگامی که سرور درخواست HTTP را دریافت می کند  OWIN pipeline ، middleware را فراخوانی می کند. Middleware نوع محتوا را برای پاسخ  و نوشتن پاسخ در body  انتخاب می کند.

حال در داخل تابع Configuration  ما نیاز به map SignalR hub داریم که در app builder قرار دارد . ما میتوان از روش زیر استفاده کنیم .

using Microsoft.Owin;
using Owin;

[assembly: OwinStartupAttribute(typeof(SignalRInstantDbChangesDemo.Startup))]
namespace SignalRInstantDbChangesDemo
{
    public partial class Startup
    {
        public void Configuration(IAppBuilder app)
        {           
            app.MapSignalR();   
        }
    }
}

دراین قسمت میخواهیم به View ها بپردازیم ، کد زیر تشکیل دهنده Index.cshtml است .

@{
    ViewBag.Title = "Instant Database Changes Using SignalR Demo-RNA Team";
}

<div class="row">
    
    <div class="col-md-12">
        <div id="divStudent"></div>
    </div>
</div>
@section Scripts{
 
<script src="~/Scripts/jquery.signalR-2.2.1.min.js"></script>
    <!--Reference the autogenerated SignalR hub script. -->
    <script src="/signalr/hubs"></script>
    <script type="text/javascript">
        $(function () {
            // Create a proxy to signalr hub on web server. It reference the hub.
            var notifications = $.connection.studentHub;           
            
            // Notify to client with the recent updates from hub that broadcast messages.
            notifications.client.updateStudentInformation = function (serverResponse) {
                alert('changes triggered by ' + serverResponse + ' operation');
                getStudentInformation();

            };
            
            // Connect to signalr hub
            $.connection.hub.start().done(function () {               
                getStudentInformation();
            }).fail(function (error) {
                alert(error);
            });
        });

        debugger;
        function getStudentInformation() {
            var model = $('#divStudent');
            $.ajax({
                url: '/home/SendStudentNotification',
                contentType: 'application/html ; charset:utf-8',
                type: 'GET',
                dataType: 'html'
            }).success(function (result) {
                model.empty().append(result);
            }).error(function () {

            });
        }
    </script>
}

ابتدای کد نشان می دهد که یک Proxy در SignalR Hub روی Web Server ایجاد کرده ایم.پس از انجام این کار ما نیاز به یک Connection  در Hub داریم. Server  اطلاع می می دهد که Client رکورد های جدیدی که قرار داده شد است را با استفاده از متد updateStudentInformation برروزرسانی کند و علاوه بر این کار پاسخ سرور را  دریافت کرده به عنوان رویداد (افزودن،ویرایش،حذف) ، تغییرات را اعمال می کند .

_StudentList.cshtml  رکورد های  Student   را نمایش می دهد.

@model IEnumerable<SignalRInstantDbChangesDemo.Models.Student>

<table class="table">
    <tr>
        <th>@Html.DisplayNameFor(model => model.StudentID)</th>
        <th>
            @Html.DisplayNameFor(model => model.StudentName)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.DOB)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Weight)
        </th>
        
    </tr>

@foreach (var item in Model) {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.StudentID)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.StudentName)
        </td>
        <th>
            @Html.DisplayFor(modelItem => item.DOB)
        </th>
        <td>
            @Html.DisplayFor(modelItem => item.Weight)
        </td>
        
    </tr>
}

</table>

حال میخواهیم برنامه را اجرا کنیم و عملیات را بر روی آن اجرا کنیم .

حال دستور زیر را اجرا کنید.

--Insert Script
INSERT INTO [dbo].[Student]
           ([StudentName]
           ,[DOB]
           ,[Weight])
     VALUES
           ('Iman'
           ,GETDATE()
           ,50)

نرم افزار انجام شدن عملیات را اطلاع رسانی می کند.

این پیام را نمایش می دهد : changes triggered by Insert operation

بعد از کلیک کردن بر روی دکمه ok تغیرات نمایش داده می شود.

دستور زیر را اجرا کنید :

INSERT INTO [dbo].[Student]
           ([StudentName]
           ,[DOB]
           ,[Weight])
     VALUES
           ('Sajad'
           ,GETDATE()
           ,68)

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

دستور زیر را اجرا کنید

UPDATE [dbo].[Student]
   SET [Weight] = 25
 WHERE [StudentName] = 'Iman'

همان لحظه اطلاع رسانی می شود.

این پیام را نمایش می دهد : changes triggered by Update operation

بعد از کلیک کردن بر روی دکمه ok تغیرات نمایش داده می شود.

دستور زیر را اجر کنید :

DELETE FROM [dbo].[Student]
WHERE [StudentName] = 'Sajad'

همان لحظه اطلاع رسانی می شود.

این پیام را نمایش می دهد  : changes triggered by Delete operation

بعد از کلیک کردن بر روی دکمه ok تغیرات نمایش داده می شود.

آموزش asp.net mvc

فایل های ضمیمه

برنامه نویسان

نویسنده 3355 مقاله در برنامه نویسان

کاربرانی که از نویسنده این مقاله تشکر کرده اند

در صورتی که در رابطه با این مقاله سوالی دارید، در تاپیک های انجمن مطرح کنید