使用SQL Agent和SignalR的每日提醒

目录

介绍

流程

步骤1:创建以下表

步骤2:创建一个存储过程

步骤3:创建一个作业

步骤4:创建SignalR集线器

步骤5:创建SignalR客户端

步骤6:在启动中注册SignalR

步骤7:创建集线器代理

步骤9:测试应用

结论


介绍

通过我们的应用程序向我们的用户发送通知/提醒是我们的明显要求。这些通知可能是电子邮件、文本/语音消息等。我们有一些选项可用于自动化此过程,例如SQL Server中的数据库邮件(Internet上提供了链接以启用和配置),我们可以创建Windows服务或编写Application_Start方法和计时器可以在每天的某个时间完成这项工作。最近,我被要求每天早上7点自动向应用程序用户发送邮件和文本消息,无论他们是联机还是脱机。

我不想使用以上任何内容,因此决定使用SignalRSqlTableDependencySQL Agent。这个想法是创建一个单独的表来每天记录通知,因为该表是由SQL Agent作业处理的,因此SignalR客户端会初始化SqlTableDependency。它将获取记录并通过我们的ASP.NET应用程序发送邮件/文本消息。

流程

上述解决方案仅在应用程序和服务器启动时才有效。我在这里分享了可用于执行类似任务的工作代码。

步骤1:创建以下表

首先,我们将创建一些表来保存用户、任务等。

CREATE TABLE [dbo].[tblUser](
                    [UserId] [int] NOT NULL,
                    [Name] [nvarchar](255) NOT NULL,
                    [Email] [nvarchar](500) NOT NULL,
                    [Mobile] [nvarchar](15) NOT NULL
 )
CREATE TABLE [dbo].[tblTask](
                    [TaskId] [int] NOT NULL,
                    [TaskCode] [nvarchar](50) NOT NULL,
                    [TaskDate] [date] NULL,
                    [AssignedTo] [int] NOT NULL,
                    [Status] [nvarchar](50) NOT NULL,
                    [Message] [nvarchar](500) NULL,
                    [ReminderStart] [date] NOT NULL
 )
CREATE TABLE [dbo].[tblDailyReminder](
                    [ReminderId] [int] IDENTITY(1,1) NOT NULL,
                    [TaskId] [int] NOT NULL,
                    [IsSent] [bit] NOT NULL CONSTRAINT [DF_tblDailyReminder_IsSent]  _
                    DEFAULT ((0))
)

步骤2:创建一个存储过程

这是将由SQL代理每天执行的存储过程。在这里,我们正在获取一些待处理的任务,它们的提醒日期已经开始。

Create PROCEDURE [dbo].[sp_PushReminder]                     
AS
BEGIN                   
                    Delete From tblDailyReminder
                    Insert into tblDailyReminder(TaskId,IsSent)
                    Select TaskId,0 from tblTask _
                           where Status='Pending'and ReminderStart<=GETDATE()
END
Go

注意:我们必须启用代理服务才能使用SqlTableDependency

USE master;
GO
ALTER DATABASE <Database_Name> SET ENABLE_BROKER WITH ROLLBACK IMMEDIATE;

步骤3:创建一个作业

右键单击SQL Agent 作业  新建作业
输入一般信息,例如作业名称。

单击左侧窗格上的步骤。

输入步骤名称,选择数据库,然后在命令框中键入exec procedure name。单击确定
在左侧窗格上,单击计划并相应地计划您的工作。

步骤4:创建SignalR集线器

[HubName("ReminderHub")]
   public class ReminderHub : Hub {
      private readonly ReminderClient _reminderClient;
       public ReminderHub() : this(ReminderClient.Instance) {
       }
       public ReminderHub(ReminderClient reminderClient) {
           _reminderClient = reminderClient;
       }
   }

步骤5:创建SignalR客户端

public class ReminderClient {
        private readonly static Lazy<ReminderClient> _instance = new Lazy<ReminderClient>(
        () => new ReminderClient
              (GlobalHost.ConnectionManager.GetHubContext<ReminderHub>().Clients));
        private static SqlTableDependency<tblDailyReminder> _sqltableDependency;
        public Dictionary<string, string> DictUsers = new Dictionary<string, string>();
        private ReminderClient(IHubConnectionContext<dynamic> clients) {
            Clients = clients;
            var mapper = new ModelToTableMapper<tblDailyReminder>();
            mapper.AddMapping(s => s.TaskId, "TaskId");
            mapper.AddMapping(s => s.ReminderId, "ReminderId");
            _ sqltableDependency = new SqlTableDependency<tblDailyReminder>
            (ConfigurationManager.ConnectionStrings["LocalConStr"].ConnectionString, 
            "tblDailyReminder", "", mapper);
            _ sqltableDependency.OnChanged += SqlTableDependency_Changed;
            _ sqltableDependency.OnError += SqlTableDependency_OnError;
            _ sqltableDependency.Start();
        }
        public static ReminderClient Instance  {
            get {
                return _instance.Value;
            }
        }
        private IHubConnectionContext<dynamic> Clients {
            get;
            set;
        }
        void SqlTableDependency_OnError(object sender, ErrorEventArgs e) {
            throw e.Error;
        }
        void SqlTableDependency_Changed(object sender, 
                  RecordChangedEventArgs<tblDailyReminder> e) {
            if (e.ChangeType == ChangeType.Insert)
            {
                NotificationDemoEntities notificationDemoEntities = 
                                             new NotificationDemoEntities();
                var mailRecepients = notificationDemoEntities.tblDailyReminders.Where
                (x => x.ReminderId == e.Entity.ReminderId).Include
                (x => x.tblTask).Include(x => x.tblTask.tblUser).ToList();
                if (mailRecepients != null && mailRecepients.Count() > 0)
                {
                    SendMailNotification(mailRecepients.FirstOrDefault().tblTask.tblUser.Email, 
                    mailRecepients.FirstOrDefault().tblTask.Message);
                }
            }
        }
        private void SendMailNotification(string Tomail, string message)  {
            try
            {
                MailMessage mailMessage = new MailMessage();
                SmtpClient smtp = new SmtpClient();
                mailMessage.From = new MailAddress("FromMailAddress");
                mailMessage.To.Add(new MailAddress(Tomail));
                mailMessage.Subject = "Test";
                mailMessage.IsBodyHtml = true;
                mailMessage.Body = message;
                smtp.Port = 587;
                smtp.Host = "smtp.gmail.com";
                smtp.EnableSsl = true;
                smtp.UseDefaultCredentials = false;
                smtp.Credentials = 
                new NetworkCredential("FromMailAddress", "password");
                smtp.DeliveryMethod = SmtpDeliveryMethod.Network;
                smtp.Send(mailMessage);
            }
            catch (Exception) {
            }
        }
        #region IDisposable Support
        private bool disposedValue = false; // To detect redundant calls
        protected virtual void Dispose(bool disposing) {
            if (!disposedValue) {
                if (disposing)
                {
                    _sqltableDependency.Stop();
                }
                disposedValue = true;
            }
        }

        ~ReminderClient() {
            Dispose(false);
        }
        // This code added to correctly implement the disposable pattern.
        public void Dispose() {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        #endregion
    }

步骤6:在启动中注册SignalR

确保将signalR映射到Startup类的Configuration方法中:

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

步骤7:创建集线器代理

为了每次都能使用它,我们必须创建集线器的代理。我将在Global.asax类的Application_Start()方法中这样做。

protected void Application_Start(){
            AreaRegistration.RegisterAllAreas();
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
           HubConnection hubConnection = 
                         new HubConnection("http://localhost:55027/signalr/hubs");
           hubProxy = hubConnection.CreateHubProxy("ReminderHub");
           hubConnection.Start();
  }

步骤9:测试应用

为了进行测试,我们需要手动执行作业。

右键单击该作业,然后开始该作业。工作完成后,我们的代码将被执行,并通过邮件发送通知。

每天或按照您计划的时间执行同一作业。

参考资料

要了解有关SqlTableDependency更多信息, 请访问  https://www.nuget.org/packages/SqlTableDependency

结论

我们可以使用上述技术将每日提醒发送给我们的用户。在这里,我们使用了SqlTableDependency对象来捕获数据库更改,并且我们还可以在发送邮件/消息的同时向用户推送应用程序级别的通知。我认为它也可以帮助其他开发人员。欢迎提出建议/查询。

发布了70 篇原创文章 · 获赞 130 · 访问量 42万+

猜你喜欢

转载自blog.csdn.net/mzl87/article/details/103881101