انجام کارهای زمانبندی شده در برنامه‌های ASP.NET توسط DNT Scheduler
اندازه‌ی قلم متن
تخمین مدت زمان مطالعه‌ی مطلب: شش دقیقه

اگر به دو مطلب استفاده از Quartz.Net (^ و ^) و خصوصا نظرات آن دقت کرده باشید به این نتیجه خواهید رسید که ... این کتابخانه‌ی در اصل جاوایی گنگ طراحی شده‌است. در سایت جاری برای انجام کارهای زمانبندی شده (مانند ارسال ایمیل‌های روزانه خلاصه مطالب، تهیه خروجی PDF و XML سایت، تبدیل پیش نویس‌ها به مطالب، بازسازی ایندکس‌های جستجو و امثال آن) از یک Thread timer استفاده می‌شود که حجم نهایی کتابخانه‌ی محصور کننده و مدیریت کننده‌ی وظایف آن جمعا 8 کیلوبایت است؛ متشکل از ... سه کلاس. در ادامه کدهای کامل و نحوه‌ی استفاده از آن را بررسی خواهیم کرد.


دریافت کتابخانه DNT Scheduler و مثال آن

DNTScheduler 
در این بسته، کدهای کتابخانه‌ی DNT Scheduler و یک مثال وب فرم را، ملاحظه خواهید کرد. از این جهت که برای ثبت وظایف این کتابخانه، از فایل global.asax.cs استفاده می‌شود، اهمیتی ندارد که پروژه‌ی شما وب فرم است یا MVC. با هر دو حالت کار می‌کند.



نحوه‌ی تعریف یک وظیفه‌ی جدید

کار با تعریف یک کلاس و پیاده سازی ScheduledTaskTemplate شروع می‌شود:
 public class SendEmailsTask : ScheduledTaskTemplate
برای نمونه :
using System;

namespace DNTScheduler.TestWebApplication.WebTasks
{
    public class SendEmailsTask : ScheduledTaskTemplate
    {
        /// <summary>
        /// اگر چند جاب در یک زمان مشخص داشتید، این خاصیت ترتیب اجرای آن‌ها را مشخص خواهد کرد
        /// </summary>
        public override int Order
        {
            get { return 1; }
        }

        public override bool RunAt(DateTime utcNow)
        {
            if (this.IsShuttingDown || this.Pause)
                return false;

            var now = utcNow.AddHours(3.5);
            return now.Minute % 2 == 0 && now.Second == 1;
        }

        public override void Run()
        {
            if (this.IsShuttingDown || this.Pause)
                return;

            System.Diagnostics.Trace.WriteLine("Running Send Emails");
        }

        public override string Name
        {
            get { return "ارسال ایمیل"; }
        }
    }
}
- در اینجا Order، ترتیب اجرای وظیفه‌ی جاری را در مقایسه با سایر وظیفه‌هایی که قرار است در یک زمان مشخص اجرا شوند، مشخص می‌کند.
- متد RunAt ثانیه‌ای یکبار فراخوانی می‌شود (بنابراین بررسی now.Second را فراموش نکنید). زمان ارسالی به آن UTC است و اگر برای نمونه می‌خواهید بر اساس ساعت ایران کار کنید باید 3.5 ساعت به آن اضافه نمائید. این مساله برای سرورهایی که خارج از ایران قرار دارند مهم است. چون زمان محلی آن‌ها برای تصمیم گیری در مورد زمان اجرای کارها مفید نیست.
در متد RunAt فرصت خواهید داشت تا منطق زمان اجرای وظیفه‌ی جاری را مشخص کنید. برای نمونه در مثال فوق، این وظیفه هر دو دقیقه یکبار اجرا می‌شود. یا اگر خواستید اجرای آن فقط در سال 23 و 33 دقیقه هر روز باشد، تعریف آن به نحو ذیل خواهد بود:
        public override bool RunAt(DateTime utcNow)
        {
            if (this.IsShuttingDown || this.Pause)
                return false;

            var now = utcNow.AddHours(3.5);
            return now.Hour == 23 && now.Minute == 33 && now.Second == 1;
        }
- خاصیت IsShuttingDown موجود در کلاس پایه ScheduledTaskTemplate، توسط کتابخانه‌ی DNT Scheduler مقدار دهی می‌شود. این کتابخانه قادر است زمان خاموش شدن پروسه‌ی فعلی IIS را تشخیص داده و خاصیت IsShuttingDown را true کند. بنابراین در حین اجرای وظیفه‌ای مشخص، به مقدار IsShuttingDown دقت داشته باشید. اگر true شد، یعنی فقط 30 ثانیه وقت دارید تا کار را تمام کنید.
خاصیت Pause هر وظیفه را برنامه می‌تواند تغییر دهد. به این ترتیب در مورد توقف یا ادامه‌ی یک وظیفه می‌توان تصمیم گیری کرد. خاصیت ScheduledTasksCoordinator.Current.ScheduledTasks، لیست وظایف تعریف شده را در اختیار شما قرار می‌دهد.
- در متد Run، منطق وظیفه‌ی تعریف شده را باید مشخص کرد. برای مثال ارسال ایمیل یا تهیه‌ی بک آپ.
- Name نیز نام وظیفه‌ی جاری است که می‌تواند در گزارشات مفید باشد.

همین مقدار برای تعریف یک وظیفه کافی است.


نحوه‌ی ثبت و راه اندازی وظایف تعریف شده

پس از اینکه چند وظیفه را تعریف کردیم، برای مدیریت بهتر آن‌ها می‌توان یک کلاس ثبت و معرفی کلی را مثلا به نام ScheduledTasksRegistry ایجاد کرد:
using System;
using System.Net;

namespace DNTScheduler.TestWebApplication.WebTasks
{
    public static class ScheduledTasksRegistry
    {
        public static void Init()
        {
            ScheduledTasksCoordinator.Current.AddScheduledTasks(
                new SendEmailsTask(),
                new DoBackupTask());

            ScheduledTasksCoordinator.Current.OnUnexpectedException = (exception, scheduledTask) =>
            {
                //todo: log the exception.
                System.Diagnostics.Trace.WriteLine(scheduledTask.Name + ":" + exception.Message);
            };

            ScheduledTasksCoordinator.Current.Start();
        }

        public static void End()
        {
            ScheduledTasksCoordinator.Current.Dispose();
        }

        public static void WakeUp(string pageUrl)
        {
            try
            {
                using (var client = new WebClient())
                {
                    client.Credentials = CredentialCache.DefaultNetworkCredentials;
                    client.Headers.Add("User-Agent", "ScheduledTasks 1.0");
                    client.DownloadData(pageUrl);
                }
            }
            catch (Exception ex)
            {
                //todo: log ex
                System.Diagnostics.Trace.WriteLine(ex.Message);
            }
        }
    }
}
- شیء ScheduledTasksCoordinator.Current، نمایانگر تنها وهله‌ی مدیریت وظایف برنامه است.
- توسط متد ScheduledTasksCoordinator.Current.AddScheduledTasks، تنها کافی است کلاس‌های وظایف مشتق شده از ScheduledTaskTemplate، معرفی شوند.
- به کمک متد ScheduledTasksCoordinator.Current.Start، کار Thread timer برنامه شروع می‌شود.
- اگر در حین اجرای متد Run، استثنایی رخ دهد، آن‌را توسط یک Action delegate به نام ScheduledTasksCoordinator.Current.OnUnexpectedException می‌توانید دریافت کنید. کتابخانه‌ی DNT Scheduler برای اجرای وظایف، از یک ترد با سطح تقدم Below normal استفاده می‌کند تا در حین اجرای وظایف، برنامه‌ی جاری با اخلال و کندی مواجه نشده و بتواند به درخواست‌های رسیده پاسخ دهد. در این بین اگر استثنایی رخ دهد، می‌تواند کل پروسه‌ی IIS را خاموش کند. به همین جهت این کتابخانه کار try/catch استثناهای متد Run را نیز انجام می‌دهد تا از این لحاظ مشکلی نباشد.
- متد ScheduledTasksCoordinator.Current.Dispose کار مدیر وظایف برنامه را خاتمه می‌دهد.
- از متد WakeUp تعریف شده می‌توان برای بیدار کردن مجدد برنامه استفاده کرد.


استفاده از کلاس ScheduledTasksRegistry تعریف شده

پس از اینکه کلاس ScheduledTasksRegistry را تعریف کردیم، نیاز است آن‌را به فایل استاندارد global.asax.cs برنامه به نحو ذیل معرفی کنیم:
using System;
using System.Configuration;
using DNTScheduler.TestWebApplication.WebTasks;

namespace DNTScheduler.TestWebApplication
{
    public class Global : System.Web.HttpApplication
    {
        protected void Application_Start(object sender, EventArgs e)
        {
            ScheduledTasksRegistry.Init();
        }

        protected void Application_End()
        {
            ScheduledTasksRegistry.End();
            //نکته مهم این روش نیاز به سرویس پینگ سایت برای زنده نگه داشتن آن است
            ScheduledTasksRegistry.WakeUp(ConfigurationManager.AppSettings["SiteRootUrl"]);
        }
    }
}
- متد ScheduledTasksRegistry.Init در حین آغاز برنامه فراخوانی می‌شود.
- متد ScheduledTasksRegistry.End در پایان کار برنامه جهت پاکسازی منابع باید فراخوانی گردد.
همچنین در اینجا با فراخوانی ScheduledTasksRegistry.WakeUp، می‌توانید برنامه را مجددا زنده کنید! IIS مجاز است یک سایت ASP.NET را پس از مثلا 20 دقیقه عدم فعالیت (فعالیت به معنای درخواست‌های رسیده به سایت است و نه کارهای پس زمینه)، از حافظه خارج کند (این عدد در application pool برنامه قابل تنظیم است). در اینجا در فایل web.config برنامه می‌توانید آدرس یکی از صفحات سایت را برای فراخوانی مجدد تعریف کنید:
 <?xml version="1.0"?>
<configuration>
  <appSettings>
      <add key="SiteRootUrl" value="http://localhost:10189/Default.aspx" />
  </appSettings>
</configuration>
همینکه درخواست مجددی به این صفحه برسد، مجددا برنامه توسط IIS بارگذاری شده و اجرا می‌گردد. به این ترتیب وظایف تعریف شده، در طول یک روز بدون مشکل کار خواهند کرد.


گزارشگیری از وظایف تعریف شده

برای دسترسی به کلیه وظایف تعریف شده، از خاصیت ScheduledTasksCoordinator.Current.ScheduledTasks استفاده نمائید:
var jobsList = ScheduledTasksCoordinator.Current.ScheduledTasks.Select(x => new
{
   TaskName = x.Name,
   LastRunTime = x.LastRun,
   LastRunWasSuccessful = x.IsLastRunSuccessful,
   IsPaused = x.Pause,
}).ToList();
لیست حاصل را به سادگی می‌توان در یک Grid نمایش داد.
  • #
    ‫۱۰ سال و ۶ ماه قبل، دوشنبه ۲۶ اسفند ۱۳۹۲، ساعت ۲۲:۵۲
    آیا یکی از کاربرد‌های این مطلب می‌تونه مثلا ارسال یک ایمیل یک هفته قبل از اتمام زمان شارژ کاربری باشه؟
    • #
      ‫۱۰ سال و ۶ ماه قبل، دوشنبه ۲۶ اسفند ۱۳۹۲، ساعت ۲۳:۰۷
      بله. می‌توانید یک وظیفه‌ی جدید تعریف کنید که هر شب ساعت مثلا 11 و 15 دقیقه اجرا شود (نحوه‌ی تعریف متد RunAt). سپس در متد Run آن یک کوئری از دیتابیس گرفته، لیست موارد مدنظر را واکشی کرده و به آن‌ها ایمیل بزنید.
  • #
    ‫۱۰ سال و ۶ ماه قبل، سه‌شنبه ۲۷ اسفند ۱۳۹۲، ساعت ۱۵:۱۷
    کتابخانه سبکی بود اما نگرانی هایی مانند پیش آمدن همزمانی در اجرای وظایف را دارم.پیش از این از کتابخانه quartz scheduler در یک پروژه بزرگ استفاده کرده بودیم و نتیجه کار بسیار راضی کننده بود ، حتی می‌توان از Scheduler خود ویندوز و یا Jon‌های SQL Server هم بهره برد.

    این روش چه مزایا و معایبی نسبت به روش‌های موجود دارد و آیا توصیه شده برای استفاده در پروژه‌های بزرگ هست؟
    • #
      ‫۱۰ سال و ۶ ماه قبل، سه‌شنبه ۲۷ اسفند ۱۳۹۲، ساعت ۱۵:۳۲
      - «پیش آمدن همزمانی در اجرای وظایف»
      خاصیت Order را برای وظایفی که قرار است در یک زمان مشخص اجرا شوند، مقدار دهی کنید. 1و 2 و 3 و الی آخر.
      - «حتی می‌توان از Scheduler خود ویندوز و یا Jon‌های SQL Server هم بهره برد».
      بله. به شرطی‌که سرور در اختیار شما باشد و دسترسی کافی برای انجام اینکار را داشته باشید. البته در این حالت خاص، مدیریت آن یکپارچه با یک برنامه‌ی وب نیست.
      در سرورهای اشتراکی روش ارائه شده در این مطلب بدون نیاز به سطح دسترسی خاصی کار می‌کند. ضمنا برای ASP.NET نوشته شده است و این قابلیت را دارد که به شما اعلام کند مثلا تا 30 ثانیه دیگر برنامه از سرور unload می‌شود؛ توسط خاصیت IsShuttingDown. همچنین حق تقدم ترد آن طوری تنظیم شده که سبب اختلال در عملیات و عملکرد متداول سایت نشود.
      - «آیا توصیه شده برای استفاده در پروژه‌های بزرگ هست؟»
      یک به اشتراک گذاری بود از قسمتی از کدهای زیر ساخت سایت جاری که هم اکنون مورد استفاده است (مقدمه بحث).
  • #
    ‫۱۰ سال و ۶ ماه قبل، چهارشنبه ۲۸ اسفند ۱۳۹۲، ساعت ۰۵:۰۲
    سوالی که واسه من پیش اومده اینه که من یک نرم افزار Web Form  دارم . هدفم اینه که یک روز قبل از تاریخ تولد اعضا ایمیلی با عنوان تولدتان مبارک ارسال شه .حالا من باید این کد‌های گفته شده را در برنامه خود بیاورم یا یک وب سرویس بنویسم که اینکارو انجام بده؟اگه بخوام که در برنامه انجام بشه خوب کجا باید نوشت ؟ یا به عبارتی دیگه باید حتما برنامه اجرا بشه و اگه برنامه اصلا Run نشه چی میشه؟
    • #
      ‫۱۰ سال و ۶ ماه قبل، چهارشنبه ۲۸ اسفند ۱۳۹۲، ساعت ۱۲:۲۹
      - وب سرویس فقط با یک درخواست رسیده کار می‌کند. کار کتابخانه‌ی فوق، اجرا در پس زمینه‌ی برنامه به صورت مداوم است.
      - فقط دو حالت وجود دارد که برنامه اجرا نشود:
      الف) protected void Application_End فراخوانی شود. متد WakeUp نوشته شده برای این منظور و راه اندازی مجدد برنامه توسط آن، کافی است.
      ب) کل سرور ری استارت شود (نه فقط برنامه). در این حالت کافی است آدرس برنامه را به یکی از سرویس‌هایی که هر از چندگاهی برنامه را ping می‌کنند، معرفی کنید.
      • #
        ‫۱۰ سال و ۵ ماه قبل، پنجشنبه ۲۸ فروردین ۱۳۹۳، ساعت ۱۳:۳۶
        در حالتی که "کل سرور ری استارت شود (نه فقط برنامه) " ، بعد از بالا آمدن سرور و شروع به کار IIS، تابع Application_Start کار شروع برنامه را خود به خود انجام نمی‌دهد؟  
        • #
          ‫۱۰ سال و ۵ ماه قبل، پنجشنبه ۲۸ فروردین ۱۳۹۳، ساعت ۱۳:۴۴
          نه تا زمانیکه اولین درخواستی به برنامه برسد.
          کل سرور ری استارت شده. IIS برنامه را فقط زمانی مجددا بارگذاری می‌کند که درخواست نمایش یکی از قسمت‌های سایت به آن ارسال شود.
          البته IISهای جدید قابلیت Auto-Start هم دارند؛ ولی باید در تنظیمات Application pool برنامه انتخاب شود:

          همچنین Application Initialization Module نیز برای اجرا خودکار برنامه پس از ری‌استارت سرور طراحی شده.

  • #
    ‫۱۰ سال و ۶ ماه قبل، شنبه ۲ فروردین ۱۳۹۳، ساعت ۰۳:۵۳
    خیلی خیلی ممنون ... کتابخانه‌ی ساده و مفیدی است. 
    سوال من اینه که میشه کاری کرد که آدرس روت تعریف شده در فایل کانفیگ جهت بیدار شدن IIS را در کد نوشت مثل کد زیر : 
    var urlToWakeup = Request.Url.Scheme + Uri.SchemeDelimiter + Request.Url.Host +
                (Request.Url.IsDefaultPort ? "" : ":" + Request.Url.Port);
    که مجبور نباشی در هر سایتی که طراحی میکنیم  آدرس رو در فایل کانفیگ ست کنیم !
    پی نوشت : من این کار رو در متد Application_End نوشتم و خطای در دسترس نبودن Request رو دریافت کردم ! (هر چند که میدونستم این خطا رو میده !)
    • #
      ‫۱۰ سال و ۶ ماه قبل، شنبه ۲ فروردین ۱۳۹۳، ساعت ۰۵:۲۱
      می‌شود در Application_BeginRequest اطلاعات آدرس ریشه سایت را در یک متغیر استاتیک ذخیره کرد. مقدار آن در Application_End قابل استفاده است:
      namespace TestApp
      {
          public static class App
          {
              public static string SiteRootUrl;
          }
      
          public class TestApplication : HttpApplication
          {
              protected void Application_BeginRequest(object sender, EventArgs e)
              {
                  if (string.IsNullOrWhiteSpace(App.SiteRootUrl))
                  {
                      App.SiteRootUrl = Request.Url.GetLeftPart(UriPartial.Authority) + Request.ApplicationPath;
                  }
              }
      
              protected void Application_End()
              {
                  // use App.SiteRootUrl
              }
          }
      }
      • #
        ‫۱۰ سال و ۶ ماه قبل، شنبه ۲ فروردین ۱۳۹۳، ساعت ۱۷:۳۲
        خیلی خیلی ممنون :) ... اگه میشه نسخه‌ی nuget کتایخانه را هم بذارید 
  • #
    ‫۱۰ سال و ۶ ماه قبل، دوشنبه ۲۵ فروردین ۱۳۹۳، ساعت ۱۹:۰۱
    1.برای زنده نگهداشتن سایت میشه از خود همین کتابخانه استفاده کرد؟
    2.برای چه تعداد جاب بنظرتون منطقیه که استفاده بشه؟
    ممنون
  • #
    ‫۱۰ سال و ۵ ماه قبل، چهارشنبه ۱۷ اردیبهشت ۱۳۹۳، ساعت ۱۳:۳۶
    یک نکته‌ی تکمیلی
    در دات نت 4.5.2 ، متدی به نام HostingEnvironment.QueueBackgroundWorkItem اضافه شده‌است تا درخواست اجرای کارهای پس زمینه در ASP.NET به سادگی و همچنین با اطمینان بیشتری قابل انجام باشد.
    • #
      ‫۱۰ سال و ۵ ماه قبل، چهارشنبه ۱۷ اردیبهشت ۱۳۹۳، ساعت ۱۸:۱۰
      یعنی با استفاده از این متد جدیدی که در دات نت 4.5.2 اضافه شده ، دیگه نیازی به   Quartz.net  و یا  DNT  scheduler  نیست ؟ و میتوان برای کارهایی که نیاز به زمانبندی دارند از این متد استفاده کرد ؟
      • #
        ‫۱۰ سال و ۵ ماه قبل، چهارشنبه ۱۷ اردیبهشت ۱۳۹۳، ساعت ۱۸:۱۲
        فقط قسمت مدیریت تردهای آن با یک سطر QueueBackgroundWorkItem جایگزین می‌شود. مابقی قسمت‌های آن (مانند اینکه یک WorkItem چه زمانی در Queue قرار گیرد) تفاوتی نمی‌کند و مانند قبل است.
        • #
          ‫۱۰ سال و ۵ ماه قبل، چهارشنبه ۱۷ اردیبهشت ۱۳۹۳، ساعت ۱۸:۴۰
          منظور شما این خط هست ؟
          ScheduledTasksCoordinator.Current.AddScheduledTasks(
                          new SendEmailsTask(),
                          new DoBackupTask());
          • #
            ‫۱۰ سال و ۵ ماه قبل، چهارشنبه ۱۷ اردیبهشت ۱۳۹۳، ساعت ۱۸:۵۳
            این‌ها باقی خواهند ماند. قسمت new Thread و مدیریت آن جایگزین می‌شود.
            • #
              ‫۱۰ سال و ۳ ماه قبل، یکشنبه ۲۵ خرداد ۱۳۹۳، ساعت ۱۸:۱۸
              سلام.
              منظورتون فقط همین یک خط توی متد Start کلاس ScheduledTasksCoordinator بود که تغییر میکنه؟
              یعنی فقط متد Start به شکل زیر بازنویسی میشه دیگه؟
              public void Start()
                      {
                          _timer.OnTimerCallback = () =>
                          {
                              var now = DateTime.UtcNow;
                              var taskToRun = _tasks.Where(x => !x.IsRunning && x.RunAt(now)).OrderBy(x => x.Order).ToList();
                              if (_isShuttingDown || !taskToRun.Any())
                                  return;
              
                              HostingEnvironment.QueueBackgroundWorkItem(x => taskAction(taskToRun));
                          };
                      }

              • #
                ‫۱۰ سال و ۳ ماه قبل، یکشنبه ۲۵ خرداد ۱۳۹۳، ساعت ۱۹:۳۱
                - بله. قسمت‌های HostingEnvironment.RegisterObject و IRegisteredObject آن هم باید حذف شوند چون در QueueBackgroundWorkItem وجود دارند و یک CancellationToken را تنظیم می‌کند.
                + زمانیکه از  DNTScheduler استفاده می‌کنید، عملا نیازی به QueueBackgroundWorkItem ندارید. چون نکته‌ی HostingEnvironment.RegisterObject و IRegisteredObject در آن لحاظ شده. این نکته که خاموش شدن IIS را گزارش می‌کند، چند سال قبل، توسط یکی از اعضای قبلی تیم ASP.NET منتشر شده بود. دقیقا از همین نکته در QueueBackgroundWorkItem استفاده شده.

                به صورت خلاصه، DNTScheduler با دات نت 4 به بعد سازگار است و نکات QueueBackgroundWorkItem دات نت 4.5.2 را به صورت توکار پیاده سازی کرده‌است.
  • #
    ‫۱۰ سال و ۴ ماه قبل، سه‌شنبه ۲۰ خرداد ۱۳۹۳، ساعت ۰۳:۴۰
    با سلام
    من روی یه پروژه مالی دارم با ASP WebForm کار میکنم و میخواستم در اول هر ماه یک سری دستورات ویرایش یا update  را که به صورت رکورد در یک جدول ذخیره می‌کنم انجام بشن. سوالم اینه که برای این که مجموعه دستورات من انجام بشن باید برنامه جتما Run باشه و چه جوری میشه اونو به صورت پس زمینه در حال اجرا نگه داشت.
    با تشکر
    • #
      ‫۱۰ سال و ۴ ماه قبل، سه‌شنبه ۲۰ خرداد ۱۳۹۳، ساعت ۰۴:۴۹
      - به اندازه کافی در نظرات این بحث در مورد زنده نگه داشتن یک برنامه ASP.NET بحث شده. کمی وقت بگذارید و آن‌ها را مطالعه کنید.
      + اگر برنامه مالی است، احتمالا دسترسی کاملی به سرور و همچنین SQL Server (اگر با آن کار می‌کنید) دارید. در این حالت برای به روز رسانی زمانبندی شده‌ی چند رکورد شاید بهتر باشد از سرویس معروف و همیشه در حال اجرای SQL Server agent استفاده کنید. در اینجا نیز می‌شود یک job را که متشکل از دستورات T-SQL است، در فواصل زمانی مشخصی اجرا کرد.
  • #
    ‫۱۰ سال و ۳ ماه قبل، سه‌شنبه ۱۷ تیر ۱۳۹۳، ساعت ۱۵:۳۲
    سلام. اگر بخواهیم یک کار نسبتاً زمانبر که IO هم هست را توسط این کتابخانه در فواصل زمانی معین اجرا کنیم، میشه از async و await استفاده کرد؟
  • #
    ‫۱۰ سال و ۲ ماه قبل، پنجشنبه ۹ مرداد ۱۳۹۳، ساعت ۱۴:۴۹
    سلام
    من برای این سری کار‌ها از ویندوز سرویس استفاده می‌کنم. مثلا ویندوز سرویس من از ساعت 8 صبح شروع به کار می‌کنه و رویدادهایی مثل سالروز تولد رو با استفاده از پیامک به کاربران پیام تبریک ارسال می‌کنه.
    مهمترین عاملی که باعث شد من از ویندوز سرویس استفاده کنم اجرای مداوم وهمیشگی بدون ارسال درخواست به وب سایت من بود. ولی فکر می‌کنم این کتابخانه شما هم مثل ویندوز سرویس عمل می‌کنه و خودش همیشه در حال اجراست.
    حالا به نظرتون آیا از ویندوز سرویس استفاده کنم بهتره و یا اینکه از این کتابخانه استفاده کنم؟
    ممنون
    • #
      ‫۱۰ سال و ۲ ماه قبل، پنجشنبه ۹ مرداد ۱۳۹۳، ساعت ۱۵:۳۳
      فرض کن داری از یک هاست اشتراکی استفاده می‌کنی. دسترسی ادمین هم روی سرور نداری برای اجرا سرویس ویندوز یا فرض کن در یک سازمان بهت گفتن ما فقط اجازه می‌دیم فایل‌های سایتت رو روی سرور کپی کنی. دسترسی بیشتری بهت نمی‌دیم. اون وقت چکار می‌کنی؟
  • #
    ‫۱۰ سال و ۲ ماه قبل، چهارشنبه ۱۵ مرداد ۱۳۹۳، ساعت ۰۷:۲۶

    سلام. ممنون.

    دوستان برای من وقتی به صورت ساعت می‌دم درست کار نمیکنه.مثلا من می‌خوام در ساعت دو و بیست و چهار دقیقه بامداد یه کاری رو انجام بده اینجوری نوشتم:در صورتی اگه بگم دو دقیقه دو دقیقه درست انجام میشه.به نظرتون مشکل از کجاست؟ممنون

      var now = utcNow.AddHours(3.5);
        return now.Hour == 2 && now.Minute == 24 && now.Second == 1;
    • #
      ‫۱۰ سال و ۲ ماه قبل، چهارشنبه ۱۵ مرداد ۱۳۹۳، ساعت ۱۳:۱۶
      مشکل از تنظیم نبودن ساعت سرور است. مقدار DateTime.UtcNow را روی سرور بررسی کنید و با مقدار واقعی تطابق بدید. بعد اختلافش را باید در همینجا اعمال کنید. مثلا اگر پس از بررسی متوجه شدید ساعت سرور یک ساعت عقب هست، now.Hour == 2 می‌شود now.Hour == 1 و امثال این نوع محاسبات.
  • #
    ‫۱۰ سال و ۱ ماه قبل، شنبه ۱۵ شهریور ۱۳۹۳، ساعت ۲۳:۰۴

    ممنون. مفید بود. اگه بخواهیم یه کاری مثلا هر سه روز یکبار انجام بشه باید چه جوری زمان رو تعیین کنیم؟ ممنون

    • #
      ‫۱۰ سال و ۱ ماه قبل، یکشنبه ۱۶ شهریور ۱۳۹۳، ساعت ۰۰:۲۷
              public override bool RunAt(DateTime utcNow)
              {
                  if (this.IsShuttingDown || this.Pause)
                      return false;
      
                  var now = utcNow.AddHours(3.5);
                  return (now.Day % 3 == 0) && (now.Hour == 0 && now.Minute == 1 && now.Second == 1);
              }
  • #
    ‫۱۰ سال و ۱ ماه قبل، سه‌شنبه ۱۸ شهریور ۱۳۹۳، ساعت ۰۲:۵۲
    یه سوال:اختلاف زمانی ساعت گرینویچ با تهران همیشه 3:30 هست؟
    • #
      ‫۱۰ سال و ۱ ماه قبل، سه‌شنبه ۱۸ شهریور ۱۳۹۳، ساعت ۰۳:۳۸
      اگر سرور شما تنظیمات daylight saving time صحیحی داشته باشد، بله. اگر نه، خودتان این یک ساعت تفاوت را 6 ماه یکبار باید محاسبه و اعمال کنید.
  • #
    ‫۱۰ سال و ۱ ماه قبل، سه‌شنبه ۱۸ شهریور ۱۳۹۳، ساعت ۱۴:۲۷
    سلام.
    چرا هیچ ارجاعی به متد Stop در کلاس JobsRunnerTimer  وجود نداره؟ برای این کار دلیلی دارید؟
    • #
      ‫۱۰ سال و ۱ ماه قبل، سه‌شنبه ۱۸ شهریور ۱۳۹۳، ساعت ۱۴:۴۴
      نیازی نیست. چون طول عمر کل این ماژول دقیقا معادل طول عمر برنامه‌ی وب است. خاتمه‌ی آن هم به صورت خودکار با از حافظه خارج کردن AppDomain برنامه توسط IIS انجام می‌شود. تا زمانیکه برنامه در حال اجرا است این ماژول هم به همین ترتیب. هر زمان که IIS تصمیم به خاتمه‌ی برنامه گرفت، نه این ماژول، هیچ ماژول دیگری هم فرصت مقاومت پیدا نمی‌کند و راسا به همراه AppDomain جاری خاتمه می‌یابد.
  • #
    ‫۹ سال و ۱۱ ماه قبل، چهارشنبه ۷ آبان ۱۳۹۳، ساعت ۲۲:۱۳
    ممنون، عالیه
    فقط یه سوال:
    چطور می‌تونیم یک Task خاص رو خودمون فراخوانی کنیم، مثلاً بعداً از اینکه کاربر در سایت ثبت‌نام کرد به صورت هفتگی براش ایمیل ارسال بشه یا مواردی از این دست، در مطلب بالا این طوری که من متوجه شدم، توسط دستور ScheduledTasksRegistry.Init(); در Application_Start تمام وظایف به صورت همزمان شروع میشن، در این حالت باید برای هر وظیفه یک کلاس برای ریجستر کردن اون بنویسیم؟ درسته؟
    • #
      ‫۹ سال و ۱۱ ماه قبل، چهارشنبه ۷ آبان ۱۳۹۳، ساعت ۲۲:۱۸
      بله. هر وظیفه نیاز به طراحی مجزا دارد. ثبت و معرفی آن توسط متد ScheduledTasksCoordinator.Current.AddScheduledTasks در ScheduledTasksRegistry.Init انجام می‌شود.
      • #
        ‫۹ سال و ۱۱ ماه قبل، پنجشنبه ۸ آبان ۱۳۹۳، ساعت ۰۳:۲۰
        ممنون،
        آیا انجام چنین کاری توسط این کتابخانه میسر است؟
        به عنوان مثال: ابتدا یک وظیفه را توسط ScheduledTasksRegistry.Init اجرا می‌کنم، این وظیفه قرار است به مدت 6 روز، روزانه کاری را انجام دهد، بعد از پایان روز 6 یعنی در روز هفتم یک وظیفه دیگر شروع خواهد شد(تعداد روزها توسط کاربر انجام می‌گیرد). و در نهایت وظایف یکی پس از دیگری بعد زمان تعیین شده برای آنها متوقف خواهند شد. 
        • #
          ‫۹ سال و ۱۱ ماه قبل، پنجشنبه ۸ آبان ۱۳۹۳، ساعت ۰۳:۳۱
          در متد RunAt بازه زمانی را مشخص کنید. در این مثال‌ها یک سری عدد ثابت است؛ شما آن‌ها را از دیتابیس بخوانید. فقط دقت داشته باشید که خروجی true/false این متد ثانیه‌ای یکبار بررسی می‌شود. بنابراین مباحث caching را جهت واکشی اطلاعات از بانک اطلاعاتی در متد RunAt اعمال کنید تا بی‌جهت رفت و برگشت اضافی صورت نگیرد.
          • #
            ‫۷ سال قبل، یکشنبه ۱۲ شهریور ۱۳۹۶، ساعت ۱۹:۵۴
            وظیفه ای دارم که هر 1 ثانیه انجام میشه ، حال اگر در این یک ثانیه این وظیفه به اتمام نرسیده باشه آیا نیاز به اضافه کردن تاخیر دارم ؟
            در واقع مشکلی به صورت local نیست ، ولی وقتی رو سرور قرار میگیره به مشکل تاخیر در اجرا عملیات می‌خوره ؟
            • #
              ‫۷ سال قبل، یکشنبه ۱۲ شهریور ۱۳۹۶، ساعت ۲۳:۱۸
              در اینجا از یک thread timer استفاده شده‌است و callback آن در هربار فراخوانی، در یک thread pool thread مجزا اجرا می‌شود. یعنی هر وظیفه به موازات وظیفه‌‌های موجود در حال اجرا و خاتمه نیافته، اجرا خواهد شد (البته بسته به تعداد هسته‌های CPU سرور و همچنین آزاد بودن تردهای موجود در thread pool) .
              • #
                ‫۷ سال قبل، یکشنبه ۱۲ شهریور ۱۳۹۶، ساعت ۲۳:۵۱
                سپاسگذارم پس از بررسی ، مشکل از تعداد هسته‌های CPU و همچنین ناکافی بودن میزان Ram سرور بود. 
  • #
    ‫۹ سال و ۱۱ ماه قبل، پنجشنبه ۸ آبان ۱۳۹۳، ساعت ۰۵:۱۵
    سلام
    ایا میشه از این کتابخانه به صورت ثانیه ای استفاده کرد ؟ در هر ثانیه چک کنه و در صورت داشتن وظایف در این ساعت و دقیقه و ثانیه (که لیست اونها در دیتابیس هست) بهشون عمل کنه !
    مثلا اینطوری اگر کار کنه مشکلی پیش نمیاد ؟
       return now.Second  % 1 == 0 && now.Second == 1;
    • #
      ‫۹ سال و ۱۱ ماه قبل، پنجشنبه ۸ آبان ۱۳۹۳، ساعت ۰۵:۲۴
      برای این حالت در متد RunAt فقط بنویسید return true. چون این متد ثانیه‌ای یکبار فراخوانی می‌شود.
  • #
    ‫۹ سال و ۱۰ ماه قبل، دوشنبه ۳ آذر ۱۳۹۳، ساعت ۰۳:۰۳
    سلام؛ فایلی که برای دانلود گذاشتین پر از اشکاله. وقتی بازش میکنم با asp زیر خیلی از کداش خط قرمز میکشه، مشکل از کجاست، آیا سورس مشکل داره؟
    • #
      ‫۹ سال و ۱۰ ماه قبل، دوشنبه ۳ آذر ۱۳۹۳، ساعت ۰۳:۵۸
      دقیقا چه خطایی گرفتید؟
      • #
        ‫۹ سال و ۱۰ ماه قبل، دوشنبه ۳ آذر ۱۳۹۳، ساعت ۱۳:۴۱
        چند بار بازو بستش کردم خیلی از خطاهاش رفتم نمیدونم دلیلش چیه اما هنوز یک خطا داره
        خط اول Default.aspx
         <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="DNTScheduler.TestWebApplication.Default" %>
          خطا
        Error 1 Could not load type 'DNTScheduler.TestWebApplication.Default'. C:\Users\KING\Desktop\DNTScheduler\DNTScheduler.TestWebApplication\Default.aspx 1
        • #
          ‫۹ سال و ۱۰ ماه قبل، دوشنبه ۳ آذر ۱۳۹۳، ساعت ۱۴:۲۵
          - این پروژه باید یکبار کامل کامپایل شود و همچنین پوشه‌ی bin آن موجود باشد.
          - خطای Could not load type صرفا به معنای عدم کامپایل شدن برنامه و یا نبود فایل‌های dll مرتبط در پوشه‌ی bin است. مراجعه کنید به منوی Build و گزینه‌ی Rebuild solution را انتخاب کنید.
      • #
        ‫۹ سال و ۱۰ ماه قبل، دوشنبه ۳ آذر ۱۳۹۳، ساعت ۱۴:۲۵
        مشکل را حل کردم. وارد قسمت پروپرتیس شدم و استارترشو عوض کردم درست شد.
  • #
    ‫۹ سال و ۱۰ ماه قبل، دوشنبه ۳ آذر ۱۳۹۳، ساعت ۱۶:۱۶
    سلام؛ میخوام هر 15 ثانیه یک بار کدم اجرا بشه. این کارو کردم اما نشد:
    return now.Second % 15 == 0 && now.Second == 1;
    لطفا راهنمایی کنید.

    <add key="SiteRootUrl" value="http://localhost:10189/Default.aspx" />
    «همینکه درخواست مجددی به این صفحه برسد، مجددا برنامه توسط IIS بارگذاری شده و اجرا می‌گردد. به این ترتیب وظایف تعریف شده، در طول یک روز بدون مشکل کار خواهند کرد.»

    - یعنی صفحه ایندکس سایت را بهش بدیم مشکلی پیش نمیاد.
    - اگر برنامه در حال انجام کار باشه بعد یک کاربری وارد سایت بشه کد در حال اجرا از کار میفته یا فقط تایمرو زنده نگه میداره

    با تشکر
    • #
      ‫۹ سال و ۱۰ ماه قبل، دوشنبه ۳ آذر ۱۳۹۳، ساعت ۱۶:۲۲
      - در شرطی که نوشتید «now.Second == 1» اضافی است.
      - خیر؛ مشکلی نخواهد بود.
      - این کارها پس زمینه هستند. اصلا ارتباطی به UI برنامه ندارند و در ترد دیگری اجرا می‌شوند.
  • #
    ‫۹ سال و ۱۰ ماه قبل، یکشنبه ۹ آذر ۱۳۹۳، ساعت ۱۹:۳۳
    ممنون؛ اما من یه مشکلی دارم ساعت اجرا رو 1 بامداد تنظیم کردم ساعت سرور رو هم چک کردم تنظیمه اما فکر کنم اجرای ترد به اون موقع نمیرسه! اوایل ترد اجرا میشه اما همیشه موقعی که متد بیدارکننده! رو صدا میزنه  خطاهای 503 Server Unavailable و 500 Internal Server Error پرت (throw میشه).
    • #
      ‫۹ سال و ۱۰ ماه قبل، یکشنبه ۹ آذر ۱۳۹۳، ساعت ۲۱:۴۳
      - از ELMAH برای ثبت خودکار ریز جزئیات خطاهای مدیریت نشده و بررسی آتی آن‌ها استفاده کنید.
      - همچنین در کتابخانه‌ی فوق به قسمت ScheduledTasksCoordinator.Current.OnUnexpectedException هم دقت داشته باشد؛ مطابق مثال ارائه شده.
  • #
    ‫۹ سال و ۳ ماه قبل، چهارشنبه ۳ تیر ۱۳۹۴، ساعت ۲۲:۴۳
    اگر بخواهیم در هنگام اجرای وظیفه‌ی مورد نظر به نمونه ای از HttpContext دسترسی پیدا کنیم چه باید کرد؟
    به این علت که من برای اجرای کدهای job احتیاج به نمونه ای از HttpContext دارم که Null هست و به دلیل متفاوت بودن thread‌ها نمی‌تونم به آن دسترسی پیدا کنم.
    • #
      ‫۹ سال و ۳ ماه قبل، چهارشنبه ۳ تیر ۱۳۹۴، ساعت ۲۳:۵۷
      بله. امکان پذیر نیست؛ چون این وظایف در ترد اصلی یک برنامه‌ی وب اجرا نمی‌شوند. رفتار با آ‌ن‌ها باید شبیه به رفتار با برنامه‌های دسکتاپ یا کنسول ویندوزی باشد. البته به اطلاعاتی مانند HttpRuntime.AppDomainAppPath دسترسی خواهید داشت.
  • #
    ‫۹ سال و ۲ ماه قبل، شنبه ۲۰ تیر ۱۳۹۴، ساعت ۰۱:۴۰
    از StructureMap3 توی پروژه استفاده میکنم ،میخوام توی Run به دیتابیس متصل بشم اما وقتی از کد زیر استفاده میکنم با خطای Object reference not set to an instance of an object  مواجهه میشم
    DependencyResolver.Current.GetService<IService>()
    اینکار امکانپذیر است؟ نیاز به تنظیمات خاصی برای StructureMap هست؟  (درضمن از تنظیمات پیش فرض StructureMap که بهنگام نصب تولید میشود استفاده میکنم)
    • #
      ‫۹ سال و ۲ ماه قبل، شنبه ۲۰ تیر ۱۳۹۴، ساعت ۰۱:۵۵
      «اینکار امکانپذیر است؟»
      بله. من خودم استفاده می‌کنم. به این صورت:
      SmObjectFactory.Container.GetInstance<T>()
      در مورد SmObjectFactory اینجا بحث شده. چند مثال از تعریف و بکارگیری SmObjectFactory در اینجا.
  • #
    ‫۹ سال و ۱ ماه قبل، دوشنبه ۲ شهریور ۱۳۹۴، ساعت ۱۸:۴۴
    اگه زمان اجرا به عنوان مثال هر 2 ثانیه باشه و ما تابعی اجرا کنیم که ممکنه زمان اجرا اون بیشتر از 2 ثانیه باشه آیا در اجرا بار بعد  تداخل به وجود نمیاد؟
    • #
      ‫۹ سال و ۱ ماه قبل، دوشنبه ۲ شهریور ۱۳۹۴، ساعت ۱۹:۴۶
      خیر. در اینجا از یک thread timer استفاده شده‌است و callback آن در هربار فراخوانی، در یک thread pool thread مجزا اجرا می‌شود (^).
  • #
    ‫۹ سال قبل، جمعه ۲۰ شهریور ۱۳۹۴، ساعت ۱۷:۰۱
    سلام،
    ابتدا از مطلب بسیار کاربردیتون تشکر می‌کنم.

    سناریوی بنده بدین صورت است که یک task ارسال ایمیل دارم که روزی یکبار به کل کاربرام یک ایمیل رو ارسال می‌کنم. حال از آنجایی که می‌ترسم میل سرور crash کنه و نتونه با سرعت بالا به یکباره همه ایمیل‌ها رو ارسال کنه، بنده می‌خوام بین ارسال هر ایمیل یک تاخیر مثلا 1 ثانیه ای بذارم تا بدین صورت میل سرور زمان تنفس داشته باشه.

    به نظر دوستان  آیا اصلا نیاز به ایجاد تاخیر زمانی هست ؟
    اگر هست، آیا کد زیر درست است ؟
    public override void Run()
    {
        if (this.IsShuttingDown || this.Pause)
            return;
     
        var _myService = StructureMapObjectFactory.Container.GetInstance<IMyService>();
        var sendingEmailsData = _myService.GetSendingEmailsData();
     
        if (sendingEmailsData.Count > 0)
        {
            var reader = new StreamReader(SiteCoreManager.SCHEDULE_BODY_PATTERN_FILE_PATH);
            string bodyPattern = reader.ReadToEnd();
     
            foreach (var sendingEmailItem in sendingEmailsData)
            {
                MailSender.Send(bodyPattern, sendingEmailItem   );
     
                // ایجاد تاخیر
                Task.Delay(1000);                   
            }
     
        }
                 
    }
    • #
      ‫۹ سال قبل، جمعه ۲۰ شهریور ۱۳۹۴، ساعت ۱۷:۲۲
      - «آیا اصلا نیاز به ایجاد تاخیر زمانی هست؟»
      بله؛ نیاز هست: «راه‌های کم کردن احتمال اسپم شدن ایمیل‌های ارسالی توسط SMTP Client»  
      - «آیا کد زیر درست است؟»
      می‌تواند بهتر باشد. مثلا بجای 0<Count بنویسید Any (سریعتر است). یا اگر قرار است فایلی یکجا خوانده شود، بهتر است از متد File.ReadAllText استفاده کنید تا درگیر مباحث dispose کردن منابع نشوید. به علاوه هرچند در اینجا نام Task عنوان شده‌است، اما این‌ها واقعا از نوع کلاس Task دات نت نیستند. بنابراین بجای Task.Delay از Thread.Sleep استفاده کنید. متد Task.Delay یک متد blocking نیست و نحوه‌ی فراخوانی آن باید به صورت await Task.Delay باشد و همانطور که عنوان شد، در اینجا Task ایی نداریم و صرفا اجرای عملیات در یک ترد مجزا است.
      • #
        ‫۹ سال قبل، جمعه ۲۰ شهریور ۱۳۹۴، ساعت ۱۷:۴۰
        با تشکر از راهنماییتون.

        - «مثلا بجای Count بنویسید Any (سریعتر است). »
        بله، حق با شماست. حواسم نبود که دارم LinqToObject میزنم.

        در زمینه LinqToEntity‌ها و کار با دیتابیس، چقدر با این گفته موافقید که "متد Any بهینه نبوده و کند می‌باشد و بهتر است از متد Count استفاده نماییم" ؟
        چون در یک ویدئوی Clean Coding بیان شد که متد Any مختص LinqToObject است و برای LinqToEntity و کار با دیتابیس مناسب نمی‌باشد.

        پ.ن: با عرض پوزش اگر سوال زیاد مرتبط با بحث جاری نیست.
        البته نظرات زیادی مثل (^ و^) وجود دارد که همگی ویدئویی که بنده دیدم رو نقص می‌کنن!
  • #
    ‫۸ سال و ۱۲ ماه قبل، یکشنبه ۵ مهر ۱۳۹۴، ساعت ۲۳:۵۹
    سلام وقتتون بخیر
    من میخواستم یه خبرنامه بسازم که به کل کاربران ایمیل بفرسته، ولی هاستی که سایت من روش هست در ساعت 40 تا ایمیل بیشتر نمیتونم بفرستم، چطور میتونم با این Schedule چنین چیزی بسازم
    یک مورد دیگه، ممکنه خبرنامه گاهی غیر فعال بشه، این مورد رو ندیدم، امکانش نیست؟
    • #
      ‫۸ سال و ۱۲ ماه قبل، دوشنبه ۶ مهر ۱۳۹۴، ساعت ۰۰:۴۳

      - یک جاب تعریف کن که بازه‌ی اجراش یک ساعت یکبار باشه. هر بار هم که اجرا شد، یک حلقه‌ی 40 تایی رو اجرا کن.

      - یک فیلد وضعیت در دیتابیس براش تعریف کن. هر بار که جاب اجرا شد، اول چک کن که این فیلد، فعال بودن یا نبودنش تنظیم شده یا نه.

      • #
        ‫۸ سال و ۱۲ ماه قبل، دوشنبه ۶ مهر ۱۳۹۴، ساعت ۰۱:۳۶
        ممنون از پاسختون 
        - چنین حالتی نداره که مثل کد زیر که جاب رو استارت میزنه حالتی باشه که با یه بار چک کردن فیلد فعال یا غیر فعال بودن، کلا جاب رو استوپ کنه و دیگه RunAt اجرا نشه!
        ScheduledSendEmailsTaskRegistry.Init(); 

        • #
          ‫۸ سال و ۱۲ ماه قبل، دوشنبه ۶ مهر ۱۳۹۴، ساعت ۰۲:۰۱
           ScheduledTasksCoordinator.Current یک متد AddScheduledTasks داره. این لیست رو می‌تونی کم و زیاد کنی در زمان اجرا. از این لیست هست که هر بار کوئری گرفته میشه برای اجرا یا عدم اجرا (لیست قسمت گزارشگیری از وظایف تعریف شده در آخر بحث).
          • #
            ‫۸ سال و ۱۲ ماه قبل، دوشنبه ۶ مهر ۱۳۹۴، ساعت ۰۲:۱۳
            چطور میتونم حذف کنم؟ پیدا نکردم
            • #
              ‫۸ سال و ۱۲ ماه قبل، دوشنبه ۶ مهر ۱۳۹۴، ساعت ۰۲:۴۵
              در کلاس ScheduledTasksCoordinator یک متد Remove از لیست اضافه کن:
              public void RemoveTask(string taskName)
              {
                  lock (_syncLock)
                  {
                     var task = _tasks.FirstOrDefault( x => x.Name == taskName);
                     if (task != null)
                     {
                          _tasks.Remove(task);
                     }   
                  }
              }
  • #
    ‫۸ سال و ۱۱ ماه قبل، شنبه ۱۸ مهر ۱۳۹۴، ساعت ۱۲:۴۷
    باسلام و احترام.
    من این زمانبندی رو به پروژم اضافه کردم اما یه مشکلی پیش اومده و اونم اینکه زمانی که برنامه اجرا شد صفحه لاگین باز میشه ولی دکمه ورود کار نمیکنه. مشکل از اینجاست که در تابع زیر 
     protected void Application_EndRequest(object sender, EventArgs e)
            {
              
    
                HttpContextLifecycle.DisposeAndClearAll();
                ScheduledTasksRegistry.End();
                ////نکته مهم این روش نیاز به سرویس پینگ سایت برای زنده نگه داشتن آن است
                ScheduledTasksRegistry.WakeUp(ConfigurationManager.AppSettings["SiteRootUrl"]);
            }
    زمانی که دستورات 
                ScheduledTasksRegistry.End();
                ////نکته مهم این روش نیاز به سرویس پینگ سایت برای زنده نگه داشتن آن است
                ScheduledTasksRegistry.WakeUp(ConfigurationManager.AppSettings["SiteRootUrl"]);
    برداشته میشه دکمه‌ها عمل میکنه ولی وقتی این کد‌ها هست هم برنامه دیر اجرا میشه هم دکمه‌ها دیگه کار نمیکنن!
    • #
      ‫۸ سال و ۱۱ ماه قبل، شنبه ۱۸ مهر ۱۳۹۴، ساعت ۱۳:۲۶
      «Application_End» ذکر شده در مطلب که فقط یکبار در پایان کار برنامه فراخوانی خواهد شد، متفاوت هست با «Application_EndRequest» ایی که در انتهای هر درخواست اجرا می‌شود.
  • #
    ‫۸ سال و ۹ ماه قبل، شنبه ۲۱ آذر ۱۳۹۴، ساعت ۱۸:۱۷
    سلام،بسیار مفید و زیبا و از همه مهمتر برای بنده سبک بودن!
    یک سوال داشتم،جسارتا اگر من بخوام برای مثلا ارسال مطلب استفاده کنم چطور میشه؟یعنی زمان بدم که تو اون زمان مطلب منتشر بشه،این مثالهایی که فرمودید همه به صورت دوره ای بودند من میخام فقط یک بار انجام بشه و تموم شه وظیفه.یا مثلا بگم اگه فلان فیلد توی دیتابیس true شد این کارو انجام بده.میشه راهنمایی کنید؟
    • #
      ‫۸ سال و ۹ ماه قبل، شنبه ۲۱ آذر ۱۳۹۴، ساعت ۱۸:۳۴
      از متد RemoveTask برای حذف یک وظیفه‌ی مشخص استفاده کنید.
  • #
    ‫۸ سال و ۹ ماه قبل، یکشنبه ۱۳ دی ۱۳۹۴، ساعت ۱۸:۲۵
    یک نکته‌ی تکمیلی
    - این پروژه در GitHub قرار گرفت. در اینجا
    - بسته‌ی نیوگت آن‌را نیز به این صورت می‌توانید نصب کنید:
    Install-Package DNTScheduler
    چند مورد اصلاحیه در این بسته هست:
    - تعریف صحیح الگوی singleton در آن
    - همچنین می‌توانید public override Task RunAsync را نیز برای زمانیکه نیاز است از متدهای async استفاده کنید، تعریف کنید (از تعریف async void پرهیز کنید).
  • #
    ‫۸ سال و ۸ ماه قبل، یکشنبه ۴ بهمن ۱۳۹۴، ساعت ۱۵:۱۲
    سلام و خسته نباشید؛  زمانی که در ترد ایجاد شده ، خطایی رخ می‌دهد در هاست‌های اشتراکی app pool ظاهرا 20 تا40 دقیقه طول می‌کشد تا این ترد را ببندد و این باعث down  شدن سیستم طی 20 تا 40 دقیقه می‌شود باید چکار کنیم که در try cash خودمان بتوانیم ترد موجود را بندیم و در کل بر این مورد مدیریت کامل داشته باشیم ؟
    خطای در یافتی من بیشتر از 100 باز در یک روز در یکی از وظیفه‌های تعریف شده :
    a task was canceled
    نکته : فقط روی هاست این مشکل به وجود می‌اید و در لوکال مشکلی ندارم حتی بالای 2 ساعت هم چک شده بدون خطا .
    نکته 2 : دات نت فریورک سرور 4.5 هست ولی من با 4.6 برنامه را در لوکال اجرا می‌کنم.
    • #
      ‫۸ سال و ۸ ماه قبل، یکشنبه ۴ بهمن ۱۳۹۴، ساعت ۱۵:۵۳
      «a task was canceled» صرفا مرتبط هست به زنده نگه داشتن app pool برنامه؛ در زمانیکه سایت بازدید کننده‌ای ندارد. نظرات بحث را از ابتدا دنبال کنید؛ در مورد سرویس ping و امثال آن بحث شده.
  • #
    ‫۸ سال و ۳ ماه قبل، دوشنبه ۷ تیر ۱۳۹۵، ساعت ۲۲:۳۳
    برای استفاده از متد‌های Async، آیا روش زیر صحیح است:
            public override Task RunAsync()
            {
                MainAsync().Wait();
                return base.RunAsync();
            }
    
            private static async Task MainAsync()
            {
                var smsService = ObjectFactory.Container.GetInstance<ISmsService>();
                await smsService.CheckForDelivery();
            }
     روش بالا جواب میده، ولی از درست بودنش مطمئن نیستم! 
    • #
      ‫۸ سال و ۳ ماه قبل، دوشنبه ۷ تیر ۱۳۹۵، ساعت ۲۳:۵۱
      نیازی به Wait در اینجا ندارید چون این Wait در خود کتابخانه‌ی اصلی انجام می‌شود. به عبارتی محتوای MainAsync را به RunAsync منتقل کنید، کافی است.
      • #
        ‫۸ سال و ۳ ماه قبل، سه‌شنبه ۸ تیر ۱۳۹۵، ساعت ۱۴:۰۶
        پس در این صورت از کلمه کلیدی await و async استفاده نمیکنیم، درسته؟ چون باعث بروز خطا میشه.
                public override Task RunAsync()
                {
                    var smsService = ObjectFactory.Container.GetInstance<ISmsService>();
                    smsService.CheckForDelivery();
                    return base.RunAsync();
                }

        • #
          ‫۸ سال و ۳ ماه قبل، سه‌شنبه ۸ تیر ۱۳۹۵، ساعت ۱۴:۱۴
          چه خطایی گرفتید؟
          • #
            ‫۸ سال و ۳ ماه قبل، سه‌شنبه ۸ تیر ۱۳۹۵، ساعت ۱۵:۲۶
            فکر کنم من در استفاده از کدها اشتباه کردم. 
            چون وقتی از کلمه‌های کلید async و await استفاده میکردم به این صورت بود:
                    public override async Task RunAsync()
                    {
                        var smsService = ObjectFactory.Container.GetInstance<ISmsService>();
                        await smsService.CheckForDelivery();
                        return base.RunAsync();
                    }
            که در خط base.RunAsync خطا ایجاد میشد:
            Error1Since 'RegisterCompany.Web.DNTScheduler.JobsTask.CheckForDeliveryTask.RunAsync()' is an async method that returns 'Task', a return keyword must not be followed by an object expression. Did you intend to return 'Task<T>'?

            لذا کدها را به این صورت نوشتم:
                    public override async Task RunAsync()
                    {
                        var smsService = ObjectFactory.Container.GetInstance<ISmsService>();
                        await smsService.CheckForDelivery();
                        await base.RunAsync();
                    }

            آیا درسته؟
            • #
              ‫۸ سال و ۳ ماه قبل، سه‌شنبه ۸ تیر ۱۳۹۵، ساعت ۱۶:۰۳
              شما نیازی به فراخوانی base.RunAsync ندارید (فقط جهت مثال بود).
  • #
    ‫۸ سال و ۳ ماه قبل، جمعه ۱۱ تیر ۱۳۹۵، ساعت ۱۳:۳۶
    سلام اگر بخواهیم در مثال طراحی افزونه پذیر اینترفیس Iplugin را به نحوی تغییر دهیم که افزونه‌ها قادر به معرفی جابهای خود باشند(با استفاده از کتابخانه فوق) به چه نحوی عمل کنیم بهتره؟ 
  • #
    ‫۸ سال و ۳ ماه قبل، جمعه ۱۱ تیر ۱۳۹۵، ساعت ۲۲:۴۷
    شرط رو به این صورت گذاشتم : return now.Minute % 50 == 0  && now.Second==1 
    انتظار این بود که در لحظاتی که دقیقه صفر و ثانیه یک و یا دقیقه 50 و ثانیه یک باشد عملیات مورد نظر (در اینجا تهیه نسخه پشتیبان صورت پذیرد ) به عبارتی در هر ساعت دو بار این شرایط مهیا می‌شود ولی نتیجه به صورت ذیل شد :
    12:50:01      12:50:02   13:00:01   13:00:02   13:50:01   13:50:02    14:00:01   14:50:01   15:00:01   15:00:02

    زمانهایی که بولد شدند قاعدتا نباید جاب اجرا می‌شد . احتمالا مشکل چی می‌تونه باشه ؟
    • #
      ‫۸ سال و ۳ ماه قبل، شنبه ۱۲ تیر ۱۳۹۵، ساعت ۰۰:۰۲
      چه تعدادی job تعریف کردید؟ این لاگ کردن‌ها کجا انجام شده؟ آیا یکبار در متد Run و یکبار در متد RunAsync کار لاگ کردن انجام شده؟ بله. ابتدا Run اجرا می‌شود و بعد از آن RunAsync. اگر چند job تعریف شده، بله، متدهای Run هر کدام به ترتیب اجرا می‌شوند.
  • #
    ‫۸ سال قبل، پنجشنبه ۲۵ شهریور ۱۳۹۵، ساعت ۱۵:۲۶
    مطلب مفید و کاربردی می‌باشد ، ممنون.
    آیا سنارویو زیر با این کتابخانه قابل  انجام می‌باشد :
    بنده میخوام بعد از اینکه کاربر به درگاه پرداخت فرستاده شده ، تا نیم ساعت بعد از آن اگر پاسخی از بانک دریافت نشد  تا سه مرتبه هر ده دقیه یک بار وضعیت پرداخت رو بررسی کنم .
    • #
      ‫۸ سال قبل، پنجشنبه ۲۵ شهریور ۱۳۹۵، ساعت ۱۵:۳۳
      - در جدول مرتبط باید فیلدهای «وضعیت پاسخ بانک» و «تاریخ و زمان ارسال به درگاه» وجود داشته باشند.
      - سپس یک جاب تعریف کنید که هر 10 دقیقه یکبار اجرا شود. در هر بار اجرای آن، در طی یک کوئری لیست مواردی را که «به درگاه پرداخت فرستاده شده»اند و «وضعیت پاسخ بانک» آن‌ها مشخص نیست را واکشی کنید. در اینجا تنها کافی است زمان ارسال واکشی شده را با تاریخ و زمان فعلی مقایسه کنید و عکس العمل لازم (مانند بررسی وضعیت پرداخت) را انجام دهید.
  • #
    ‫۸ سال قبل، دوشنبه ۲۹ شهریور ۱۳۹۵، ساعت ۱۶:۳۲
    تشکر از مقالتون
    سوالی که برام پیش اومده اینه که آیا در انجام تسک‌های زمانبر (مثلا تسکی که شاید چند ده دقیقه اجراش طول بکشه) مشکلی بوجود نمیاد؟
    من الان از این کتابخونه توی پروژم استفاده کردم ولی نتیجه مطابق میل من نیست. یعنی فکر میکنم هرشب که تسک رو اجرا میکنه، تسک رو کامل اجرا نمیکنه. من یه سری تغییرات رو هرشب میخوام روی چندده هزار رکورد انجام بدم. ولی وقتی بررسی میکنم مثلا تغییری که باید 5 روز پیش روی یه رکورد انجام میشده شب گذشته انجام شده.
    از الماه هم استفاده میکنم ولی توی لاگ خطاها که بررسی میکردم توی تایمی که تسک اجرا میشه خطایی نداشتم.
    نکته ای وجود داره که من بهش توجه نکردم؟
    • #
      ‫۸ سال قبل، دوشنبه ۲۹ شهریور ۱۳۹۵، ساعت ۱۸:۰۹
      یک متد الحاقی لاگ ELMAH را ایجاد کنید:
      using System;
      using System.Text;
      using Elmah;
      
      namespace Common.WebToolkit
      {
          public static class ElmahLogEx
          {
              public static void LogException(this string ex)
              {
                  if (string.IsNullOrWhiteSpace(ex))
                      return;
                  LogException(new Exception(ex));
              }
      
              public static void LogException(this Exception ex)
              {
                  if (ex == null) return;
                  try
                  {
                      ErrorSignal.FromCurrentContext().Raise(ex);
                  }
                  catch
                  {
                      ErrorLog.GetDefault(null).Log(new Error(ex));
                  }
              }
          }
      }
      سپس
      - در کتابخانه‌ی فوق به قسمت ScheduledTasksCoordinator.Current.OnUnexpectedException هم دقت داشته باشد؛ مطابق مثال ارائه شده. این موارد را هم لاگ کنید:
       ScheduledTasksCoordinator.Current.OnUnexpectedException =
                          (exception, scheduledTask) => (scheduledTask.Name + ":" + exception).LogException();
      - ابتدا و انتهای هر Task را لاگ کنید (متد الحاقی فوق را به صورت معمولی و با پیام‌هایی مشخص، در ابتدا و انتهای هر Task فراخوانی کنید؛ تا در لاگ‌های ELMAH ظاهر شوند).
      - شروع به کار برنامه و خاتمه‌ی آن‌را لاگ کنید (متد الحاقی فوق را با پیام‌هایی مشخص، در متدهای Application_Start و Application_End فایل Global.asax.cs فراخوانی کنید تا مشخص شود که آیا برنامه خاتمه یافته‌است یا خیر).
      • #
        ‫۷ سال و ۱۲ ماه قبل، پنجشنبه ۱ مهر ۱۳۹۵، ساعت ۱۴:۵۳
        کاری که گفتید رو انجام دادم و بعد از بررسی لاگ الماه متوجه شدم دوتا تسکی که نوشتم هیچکدوم به پایان نمیرسن. یکی از خطاها توی متد WakeUp صورت میگیره:

        exception occured in wakeup methode:System.ArgumentNullException: Value cannot be null. Parameter name: address at System.Net.WebClient.DownloadData(String address) at xxx.Web.WebTasks.ScheduledTasksRegistry.WakeUp(String pageUrl)

        در حالیکه من توی فایل کانفیگ برنامه آدرس سایت رو به این صورت دادم:
        <add key="SiteRootUrl" value="http://xxx.com" />

        و یکی دیگه از خطاهایی که اتفاق افتاده اینه:

        System.Threading.ThreadAbortException: Thread was being aborted
          • #
            ‫۷ سال و ۱۲ ماه قبل، پنجشنبه ۱ مهر ۱۳۹۵، ساعت ۱۶:۱۶
            ممنون، تست میکنم امیدوارم مشکل حل بشه
            - فقط دوتا سوال برام پیش اومده: شما میفرمایید که به دلیل نرسیدن درخواست، IIS برنامه من رو متوقف میکنه. خب تسک من شروع شده و داره پشت صحنه کار انجام میده، با این حال بازم طبیعیه که IIS برنامم رو متوقف کنه؟ 
            - و سوال دوم اینه که برای تسکای زمانبر (که ممکنه حتی چندساعت طول بکشه) نیازی به انجام کار خاص دیگه ای نیست؟
            • #
              ‫۷ سال و ۱۲ ماه قبل، پنجشنبه ۱ مهر ۱۳۹۵، ساعت ۱۸:۲۷
              کارهای پس زمینه اهمیتی برای IIS ندارن: انجام کارهای پس زمینه در ASP.NET 4.5.2  
              حداقل کاری که می‌تونی انجام بدی این هست که خودت یک وظیفه‌ی Ping درست کنی. یک Task درست کن که هر 30 ثانیه یکبار صفحه اول سایت رو واکشی کنه. مثل کاری که RSS Readerها انجام می‌دن. اینطوری برنامه‌ات همیشه زنده می‌مونه؛ مگر اینکه یک نفر کل سرور رو ری‌استارت کنه. یا یکی از حالت‌های ری‌استارت برنامه‌های ASP.NET رخ بده.
              • #
                ‫۷ سال و ۱۲ ماه قبل، پنجشنبه ۱ مهر ۱۳۹۵، ساعت ۱۸:۵۷
                "حداقل کاری که می‌تونی انجام بدی این هست  ..."
                خب این کار رو همون قطعه کدی که توی Application_End قرار میدیم انجام میده دیگه؟ درسته؟
                • #
                  ‫۷ سال و ۱۲ ماه قبل، پنجشنبه ۱ مهر ۱۳۹۵، ساعت ۲۱:۴۱
                  اون قسمت فقط برنامه‌ای رو که توسط IIS خاتمه یافته، مجددا از صفر بارگذاری می‌کنه. اما اگر این وظیفه‌ی مخصوص Ping رو اضافه کنید، کار به خاتمه یافتن برنامه نمی‌رسه.
      • #
        ‫۵ سال و ۹ ماه قبل، دوشنبه ۱۹ آذر ۱۳۹۷، ساعت ۱۴:۴۱
        اگر برای elmah ارسال ایمیل نیز تنظیم شده باشد برای این متد الحاقی باید به صورت دستی ارسال ایمیل را تنظیم کرد؟
  • #
    ‫۷ سال و ۱۲ ماه قبل، جمعه ۲ مهر ۱۳۹۵، ساعت ۰۰:۰۴
    به روز رسانی
    نگارش 1.1 این کتابخانه منتشر شد.

    تغییرات
    - یک وظیفه‌ی توکار Ping به این کتابخانه اضافه شد تا به صورت خودکار دقیقه‌ای یکبار برنامه را Ping کند تا دیگر برنامه توسط IIS (به سادگی) از حافظه خارج نشود.
    - همچنین یک HTTP Module توکار هم به آن اضافه شد تا فایل global.asax.cs نهایی شما را خلوت کند. این ماژول به صورت خودکار در برنامه ثبت خواهد شد و نیازی به هیچگونه تنظیماتی از طرف شما ندارد.

    برای نصب
    - برای نصب بار اول:
    PM> Install-Package DNTScheduler
    - برای به روز رسانی نصب فعلی:
    PM> Update-Package

    پس از نصب
    - فایل global.asax قدیمی خود را خلوت کنید (دیگر نیازی به متد application end نیست و HTTP module توکار آن این مورد را پوشش می‌دهد).
    - همچنین دیگر نیازی به ذکر SiteRootUrl در فایل وب کانفیگ نیست.
  • #
    ‫۷ سال و ۱۱ ماه قبل، چهارشنبه ۲۱ مهر ۱۳۹۵، ساعت ۲۲:۱۳
    جهت اطلاع
    این کتابخانه برای ASP.NET Core نیز بازنویسی شد: DNTScheduler.Core  
  • #
    ‫۷ سال و ۱۰ ماه قبل، پنجشنبه ۲۰ آبان ۱۳۹۵، ساعت ۰۵:۱۴
    من از نسخه 1.1 این کتابخونه استفاده میکنم. توی لاگ الماه چنین خطایی دارم:
    Ping Task:System.Net.WebException: The remote server returned an error: (500) Internal Server Error. at System.Net.WebClient.DownloadDataInternal(Uri address, WebRequest& request) at System.Net.WebClient.DownloadData(Uri address) at System.Net.WebClient.DownloadData(String address) at DNTScheduler.ThisApp.WakeUp() at DNTScheduler.PingTask.Run() at DNTScheduler.ScheduledTasksCoordinator.taskAction(IEnumerable`1 taskToRun)
    • #
      ‫۷ سال و ۱۰ ماه قبل، پنجشنبه ۲۰ آبان ۱۳۹۵، ساعت ۰۵:۲۳
      مهم نیست. زمان application end از این خطاها خواهید دید (Internal Server Error). در اینجا server همان برنامه‌ی شما است که قابلیت Ping را در آن لحظه نداشته.
  • #
    ‫۷ سال و ۹ ماه قبل، یکشنبه ۲۱ آذر ۱۳۹۵، ساعت ۰۹:۳۵
    سلام؛ من یه سیستم ماژولار بیس نوشتم که تمام اجزا اون جداست و بروی چهار چوب اصلی یا همون هسته سوار میشه و اجرا میشه .
    یه ماژول دارم ارسال ایمیل‌های زمانبندی شده که همین کلاس شما رو قرار دادم و مستقل داره کار می‌کنه .
    الان می‌خوام دو سه ماژول دیگه ایجاد کنم که کاری رو بر مبنای زمانبندی انجام میدن .
    حالا مشکل اینجاست چطوری از این کلاس استفاده کنم که بتونه تداخل انجام تسک نداشته باشه ؟
    اولین و بهترین حالتی که به ذهنم رسید سوار کردن این کلاس بروی هسته هست که بتونه کل تسک‌ها رو مدیرت کنه و این کار با قرار دادن کلاس "ScheduledTasksRegistry " در هسته و قرار دادن کلاس "ScheduledTaskTemplate " برای هر ماژول قابل اجراست .
    اما مشکل اینجاست که فایل ScheduledTasksRegistry  کامپایل شده و نمی‌توان متد :
    ScheduledTasksCoordinator.Current.AddScheduledTasks(
    new Listener(),
    new Sender()
    );
    را تغییر داد .
    مجدد توی ذهنم overwrite کردن این متد امد ولی چطوری ؟ ایا امکانپدیر هست ؟
    دقیقا من باید بتونم این قسمت رو runtime تغییر بدم و کلاس‌های دیگر رو اضافه کنم .
    • #
      ‫۷ سال و ۹ ماه قبل، یکشنبه ۲۱ آذر ۱۳۹۵، ساعت ۱۲:۱۷
      ScheduledTasksCoordinator.Current یک وهله‌ی Singleton است و هر زمانیکه متد AddScheduledTasks و یا AddScheduledTasks بر روی آن فراخوانی شوند، به لیست موجود، موارد جدید را اضافه خواهد کرد و محدودیتی در مورد تعداد بار فراخوانی آن وجود ندارد.
  • #
    ‫۷ سال و ۶ ماه قبل، شنبه ۵ فروردین ۱۳۹۶، ساعت ۱۶:۴۷
    یک نکته: روش صحیح Dispose سرویس‌ها داخل وظایف تعریف شده

    با توجه به اینکه داخل وظایف، چرخه‌ی طول عمر یک درخواست وب رخ نمی‌دهد، کار Dispose سرویس‌های HybridHttpOrThreadLocalScoped مانند IUnitOfWork را باید دستی انجام دهید:
        public static class IoCWrapper
        {
            public static void RunAndDispose(Action action)
            {
                try
                {
                    action();
                }
                finally
                {
                    // اگر در درخواست وب بودیم، به صورت خودکار در پایان کار همه چیز تمام می‌شود
                    if (!HttpContextLifecycle.HasContext())
                    {
                        new HybridLifecycle().FindCache(null).DisposeAndClear();
                    }
                }
            }
    
            public static T GetInstance<T>()
            {
                return SmObjectFactory.Container.GetInstance<T>();
            }
        }
    و سپس برای استفاده‌ی از آن داخل وظایف:
            public override void Run()
            {
                IoCWrapper.RunAndDispose(() =>
                {
                    var draftsService = IoCWrapper.GetInstance<IBlogPostDraftsService>();
                    draftsService.RunConvertDraftsToPostsJob();
                });
            }
    به این ترتیب کار Dispose وهله‌ی Context برنامه به درستی صورت گرفته و هم برنامه دچار نشتی حافظه نخواهد شد و هم Context دریافتی حالت singleton و «کش شده» را پیدا نمی‌کند (اگر dispose نشود، هربار درخواست آن معادل است با دریافت همان وهله‌ی قبلی).
    • #
      ‫۵ سال و ۴ ماه قبل، یکشنبه ۲۲ اردیبهشت ۱۳۹۸، ساعت ۲۰:۰۶
      سلام؛ وقتی در قسمت:
      IoCWrapper.RunAndDispose(() =>
      {
           var draftsService = IoCWrapper.GetInstance<IBlogPostDraftsService>();
           draftsService.RunConvertDraftsToPostsJob();
      });
      یک متد Async قرار می‌دم
      IoCWrapper.RunAndDispose(async () =>
      {
           var draftsService = IoCWrapper.GetInstance<IBlogPostDraftsService>();
           await draftsService.RunConvertDraftsToPostsJobAsync();
      });
      دفعه اول به درستی اجرا میشه، ولی دفعه دوم خطای زیر میده
      System.ObjectDisposedException
        HResult=0x80131622
        Message=The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
        Source=EntityFramework
      • #
        ‫۵ سال و ۴ ماه قبل، یکشنبه ۲۲ اردیبهشت ۱۳۹۸، ساعت ۲۳:۰۳
        چون یک async void را ایجاد کردید و به صورت fire-and-forget کار می‌کند. یعنی IoCWrapper.RunAndDispose بلافاصله خاتمه پیدا خواهد.
        امضای مدنظر شما چنین چیزی باید باشد:
        public static Task RunAndDispose(Func<Task> action)
        تا خود RunAndDispose هم قابلیت await پیدا کند.
        • #
          ‫۵ سال و ۴ ماه قبل، سه‌شنبه ۲۴ اردیبهشت ۱۳۹۸، ساعت ۱۷:۴۷
          من این تغییرات رو اعمال کردم
          public static Task RunAndDispose(Func<Task> action)
          {
                  try
                  {
                          action();
                  }
                  finally
                  {
                          System.Diagnostics.Trace.WriteLine("Finaly");
          
                          if (!HttpContextLifecycle.HasContext())
                          {
                              new HybridLifecycle().FindCache(null).DisposeAndClear();
                          }
                  }
                  return  Task.FromResult(0);
          }

          و  متد RunAsync 
          public override async Task RunAsync()
          {
                  await SemaphoreSlim.WaitAsync();
          
                  try
                  {
                          if (this.IsShuttingDown || this.Pause)
                              return;
          
                          await IoCWrapper.RunAndDispose(async () =>
                          {
                              var draftsService = IoCWrapper.GetInstance<IBlogPostDraftsService>();
                              await draftsService.RunConvertDraftsToPostsJobAsync();
                          });
                  }
                  finally
                  {
                          SemaphoreSlim.Release();
                  }
          }
          ولی بعد از دومین بار اجرا، توی ایونت‌ها این خطارو دارم
          ActivatedEventTimeDurationThread
          Activated Historical Code ContextException thrown: 'System.ObjectDisposedException' in mscorlib.dll ("The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.") Exception thrown: 'System.ObjectDisposedException' in mscorlib.dll ("The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.") Hyperlink: Activate Historical Debugging346.49s[14880] Worker Thread

          به نظرتون جایی از کد رو اشتباه نوشتم؟
          • #
            ‫۵ سال و ۴ ماه قبل، سه‌شنبه ۲۴ اردیبهشت ۱۳۹۸، ساعت ۱۹:۲۴
            ()action باید بشود ()await action.
  • #
    ‫۶ سال و ۱۰ ماه قبل، شنبه ۲۰ آبان ۱۳۹۶، ساعت ۱۳:۳۸
    سلام؛ من این  Library روگرفتم و توی یه پروژه استفاده کردم. در محیط طراحی مشکلی نبود ولی وقتی روی سرور نسخه رو منتشر کردم به یه مشکل برخوردم. وقتی تعداد worker‌ها بیش از یکی می‌شه به ازای هر worker یه scheduler  ایجاد میشه که فکر کنم عادیه اما من نیاز دارم فقط یه worker اون scheduler رو اجرا کنه . راه کاری توی خود Library وجود داره که بشه این مسئله رو کنترل کرد؟ اگه پاسختون خیر هست پیشنهاد شما چیه ؟
    • #
      ‫۶ سال و ۱۰ ماه قبل، شنبه ۲۰ آبان ۱۳۹۶، ساعت ۱۳:۴۷
      // فقط یک ترد امکان دسترسی به کد را داشته باشد
      private static readonly SemaphoreSlim _semaphoreSlim = new SemaphoreSlim(1,1);
      
      public override async Task RunAsync()
      {
         // شروع تمام تردها معلق خواهند شد تا این سمافور به پایان برسد
         // پس از پایان کار ترد جاری، فقط یک ترد در حال انتظار، مجوز دسترسی به قطعه کد بعدی را خواهد یافت و به همین ترتیب برای سایر تردها
         await _semaphoreSlim.WaitAsync(); 
         try
         {
             await Task.Delay(20000); // Your code here
         }
         finally
         {
            _semaphoreSlim.Release();
         }
      }
  • #
    ‫۶ سال و ۱۰ ماه قبل، چهارشنبه ۲۴ آبان ۱۳۹۶، ساعت ۱۲:۴۴
    سلام 
    ممنون از کتابخانه خوبتون
    برای توقف و یا اجرای کل task در برنامه توسط کاربر چکار باید انجام داد؟
    • #
      ‫۶ سال و ۱۰ ماه قبل، چهارشنبه ۲۴ آبان ۱۳۹۶، ساعت ۱۲:۵۶
      - برای گزارشگیری از وظایف موجود از لیست ScheduledTasksCoordinator.Current.ScheduledTasks استفاده کنید.
      - برای حذف و خاتمه‌ی یک Task از متد ScheduledTasksCoordinator.Current.RemoveTask(name) استفاده کنید.
      - برای افزودن یک Task جدید از متدهای ScheduledTasksCoordinator.Current.AddScheduledTasks(params)/AddScheduledTask(task) استفاده کنید.
  • #
    ‫۶ سال و ۹ ماه قبل، یکشنبه ۵ آذر ۱۳۹۶، ساعت ۱۴:۱۶
    برای اجرای هر چند ساعت یکبار (به طور مثال هر 3 ساعت) باید مقدار دقیقه هم اعمال کرد؟
    کد زیر هر دقیقه اجرا می‌شود 
     return now.Hour % 3 == 0 && now.Second == 1;
    ولی با اعمال مقدار دقیقه ظاهرا مشکل برطرف شد
     return now.Hour % 3 == 0 && now.Minute == 0 && now.Second == 1;

  • #
    ‫۵ سال و ۸ ماه قبل، سه‌شنبه ۱۸ دی ۱۳۹۷، ساعت ۱۸:۲۴
    زمانی که وظیفه ای میخواهد اجرا شود همزمان با آن کنترلر Home هم فراخوانی می‌شود
    این درخواست هایی هست که لاگ VS قسمت Output ثبت می‌شود

    Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.Metric","time":"2019-01-08T11:23:01.0000000Z","tags":{"ai.cloud.roleInstance":"BAHARI-PC","ai.internal.sdkVersion":"m-agg2:2.8.1-22898"},"data":{"baseType":"MetricData","baseData":{"ver":2,"metrics":[{"name":"Dependency duration","kind":"Aggregation","value":4143.777,"count":1,"min":4143.777,"max":4143.777,"stdDev":0}],"properties":{"_MS.MetricId":"dependencies/duration","_MS.IsAutocollected":"True","_MS.AggregationIntervalMs":"60000","Dependency.Type":"Http","DeveloperMode":"true","Dependency.Success":"True"}}}}
    Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.Metric","time":"2019-01-08T11:23:01.0000000Z","tags":{"ai.cloud.roleInstance":"BAHARI-PC","ai.internal.sdkVersion":"m-agg2:2.8.1-22898"},"data":{"baseType":"MetricData","baseData":{"ver":2,"metrics":[{"name":"Server response time","kind":"Aggregation","value":4136.7653,"count":1,"min":4136.7653,"max":4136.7653,"stdDev":0}],"properties":{"_MS.MetricId":"requests/duration","_MS.IsAutocollected":"True","_MS.AggregationIntervalMs":"60000","DeveloperMode":"true","Request.Success":"True"}}}}
    Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.Metric","time":"2019-01-08T11:23:01.0000000Z","tags":{"ai.cloud.roleInstance":"BAHARI-PC","ai.internal.sdkVersion":"m-agg2:2.8.1-22898"},"data":{"baseType":"MetricData","baseData":{"ver":2,"metrics":[{"name":"Dependency duration","kind":"Aggregation","value":3972.6929,"count":3,"min":12.8297,"max":2687.0447,"stdDev":1092.34881420812}],"properties":{"_MS.MetricId":"dependencies/duration","_MS.IsAutocollected":"True","_MS.AggregationIntervalMs":"60000","Dependency.Type":"SQL","DeveloperMode":"true","Dependency.Success":"True"}}}}
    این هم نمونه ای از وظیفه:
    #region Using
    
    using Autofac;
    using CHK.ServiceLayer.Interfaces;
    using DNTScheduler;
    using System;
    
    #endregion
    
    namespace CHK.Web.Scheduler
    {
        public class InventoryTask : ScheduledTaskTemplate
        {
            #region Properties
    
            public override int Order => 1;
            public override string Name => "Inventory-DiscountCoupon-GiftCard";
            public IContainer Container { get; set; }
    
            #endregion
    
            #region Methods
    
            public override bool RunAt(DateTime utcNow)
            {
                if (IsShuttingDown || Pause) return false;
                var currentDateTime = utcNow.AddHours(3.5);
                return (currentDateTime.Minute % 15 == 0 && currentDateTime.Second > 5 && currentDateTime.Second < 15);
            }
    
            public override void Run()
            {
                if (IsShuttingDown || Pause) return;
                Pause = true;
                using (var scope = Container.BeginLifetimeScope())
                {
                    var schedulerService = scope.Resolve<ISchedulerService>();
                    schedulerService.UpdateInventory();
                }
                Pause = false;
            }
    
            #endregion
        }
    }

    • #
      ‫۵ سال و ۸ ماه قبل، سه‌شنبه ۱۸ دی ۱۳۹۷، ساعت ۱۸:۳۵
      این مسایل ربطی به این کتابخانه ندارد.
      The unconfigured reference in each instance of Application Insights telemetry indicates that this application isn't associated with an ikey. The data that's generated while your app is running isn't sent to Azure. The data is available only for local search and analysis.
  • #
    ‫۵ سال و ۴ ماه قبل، سه‌شنبه ۲۴ اردیبهشت ۱۳۹۸، ساعت ۲۱:۵۴
    سلام؛ در نسخه قبلی که بر روی ASP .NET MVC بود برای پروژه ای که روی سرور هاست کرده بودم و سرور "فقط" از TLS1.2 استفاده می‌کرد (سرور از طریق نرم افزار NARTAC کانفیگ شده و همه نسخه‌های SSL و Tls1.0 و Tls1.1 بر روی آن غیر فعال شده است به دلیل موارد امنیتی) مجبور بودم دو خط کد زیر را در متد WakeUp قبل از کد دانلود صفحه اضافه کنم
    ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
    ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

    تا خطایی برای دانلود صفحه دریافت نکنم و کد به درستی کار کند
    برای نسخه جدید که بر روی dot NET Core زحمت آن را کشیده اید کد آن کمی تغییر کرده و به جای استفاده از شئ WebClient و دانلود صفحه از شئ HttpClient استفاده شده.
    ممنون میشم راهنمایی بفرمایید در اینجا برای فعال سازی Tls1.2 به چه نحوی باید عمل کنم تا خطا نده؟ آیا باید دوباره همین دو خط کد بالا را قبل از خط کد
    await _client.GetStringAsync(SiteRootUrl);
    که در کلاس ThisApplication نوشته شده صدا بزنم؟


    ضمنا به چه نحوی باید متد LogError که در قطعه کد زیر در کلاس Startup نوشته شده رو شخصی سازی کنم تا همه لاگهای خطا در جدولی در بانک اطلاعاتی ذخیره شود؟ (چون از بانک اطلاعاتی مونگو استفاده کردم می‌خواستم این بخش رو برای مونگو پیاده سازی کنم)
    var logger = loggerFactory.CreateLogger(typeof(Startup));
    app.UseDNTScheduler(onUnexpectedException: (ex, name) =>
    {
          logger.LogError(0, ex, $"Failed running {name}");
    });
    • #
      ‫۵ سال و ۴ ماه قبل، سه‌شنبه ۲۴ اردیبهشت ۱۳۹۸، ساعت ۲۳:۲۹
      تاریخچه‌ی پشتیبانی از TLS 1.2 در NET. به این صورت است:
      وضعیت پشتیبانی از TLS 1.2
       نگارش دات نت
       پشتیبانی نمی‌شود. راه حلی هم ندارد.
       NET 3.5. یا قبل از آن
       پشتیبانی نمی‌شود. اما اگر نگارش بالاتری نصب است، قطعه کد زیر را استفاده کنید:
      ServicePointManager.SecurityProtocol = 
        (SecurityProtocolType)3072;
       NET 4.0.
       پشتیبانی می‌شود، اما حالت پیش‌فرض نیست و باید دستی انتخاب شود:
      ServicePointManager.SecurityProtocol = 
        SecurityProtocolType.Tls12;
       NET 4.5.
       حالت پیش‌فرض است و نیاز به تنظیمات خاصی ندارد.
       NET 4.6. و یا بالاتر
      - بنابراین اگر از Full .NET استفاده می‌شود، فقط کافی است که Target Framework برنامه را به بالاتر از NET 4.6. تنظیم کنید (الان نگارش 4.8 ارائه شده) و سرور هم همان نگارش را نصب کند. نیاز به تنظیم بیشتری ندارد. در مورد NET Core. هم به همین صورت است و HttpClient آن برای دریافت صفحات ارائه شده‌ی با TLS 1.2 هیچ مشکلی ندارد.
      - سطر ServerCertificateValidationCallback که true کردید یعنی مجوز SSL سرور شما حتی اگر معتبر نبود (و همچنین کل اینترنت؛ چون این تنظیم سراسری است)، معتبر تشخیص داده شود که غیرضروری است.
      - مورد لاگ کردن، وابستگی به بانک اطلاعاتی خاصی ندارد. این سطرها را حذف کنید و بجای آن کدهای بانک اطلاعاتی خودتان را قرار دهید. یا اگر logger پیش‌فرض سیستم شما اطلاعات را بجای کنسول یا صفحه‌ی Debug، در بانک اطلاعاتی ذخیره می‌کند (^ یا ^)، این قسمت نیازی به تغییر ندارد.
      • #
        ‫۴ سال و ۱ ماه قبل، دوشنبه ۲۳ تیر ۱۳۹۹، ساعت ۲۲:۴۲
        در حال حاضر با اینکه یک Certificate معتبر گرفتم و روی IIS فعال کردم و در کروم هم گواهینامه رو سبز نمایش میده ولی برای ping خطای زیر رو در Event Viewer ویندوز می‌بینم برای هر دقیقه که اجرا میشه؟!!
        آخرین نسخه که asp . net Core 3.1.5 هست روی سرور نصب هست همینطور dot net framework 4.8
        همه موارد دیگر پروژه درست کار می‌کنه و خطایی ندارم غیر از این بخش که در Event Viewer هم میشه دید
        ممنون میشم راهنمایی بفرمایید چطور رفعش کنم

        Category: DNTScheduler.Core.ScheduledTasksCoordinator
        EventId: 0
        
        Failed running DNTScheduler.Core.PingTask
        
        Exception: 
        System.AggregateException: One or more errors occurred. (The SSL connection could not be established, see inner exception.)
         ---> System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.
         ---> System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.
        
        • #
          ‫۴ سال و ۱ ماه قبل، سه‌شنبه ۲۴ تیر ۱۳۹۹، ساعت ۰۰:۱۹
          اینکه عنوان می‌کند مجوز مدنظر غیرمعتبر است، مشکل این کتابخانه نیست. اگر این قسمت برای شما کار نمی‌کند، آن‌را غیرفعال کنید: options.AddPingTask = false 
  • #
    ‫۵ سال و ۳ ماه قبل، چهارشنبه ۱ خرداد ۱۳۹۸، ساعت ۱۷:۲۸
    وظیفه ای دارم با این تنظیمات 
    services.AddDNTScheduler(options =>
                {
                    // DNTScheduler needs a ping service to keep it alive. Set it to false if you don't need it.
                    options.AddPingTask = true;
                    options.AddScheduledTask<DoFillDataTask>(
                        runAt: utcNow =>
                        {
                            var schedulerTask = _config.GetSection("SchedulerTask");
                            var now = utcNow.AddHours(4.5);
                             return  now.Hour == 10 && now.Minute == 0 && now.Second == 0;
                        },
                        order: 1);
                });
    که هر ثانیه پیغامی با سطح fatal میده. در صورتی که وظیفه با موفقیت انجام میشه.
    پیغام :Failed running DNTScheduler.Core.PingTask  
    با تغییر PingTask = false مشکل برطرف نمیشه. 
    شرح کامل پیغام: error.txt 
    • #
      ‫۵ سال و ۳ ماه قبل، چهارشنبه ۱ خرداد ۱۳۹۸، ساعت ۱۷:۵۰
      «هر ثانیه؟» خیر. تنظیم شده هر دقیقه یکبار آدرس root سایت شما را فراخوانی کند. 400 Bad Request هم یعنی این آدرس root سایت قابل دسترسی نیست و خطا می‌دهد. AddPingTask هم اگر false باشد، این job اصلا اضافه نخواهد شد. بنابراین این تنظیم در زمان راه اندازی اولیه‌ی برنامه تاثیر دارد و نه پس از آن.  
      • #
        ‫۵ سال و ۳ ماه قبل، چهارشنبه ۱ خرداد ۱۳۹۸، ساعت ۱۸:۳۲
        کلمه هر ثانیه اشتباه من بود و هر دقیقه صحیح هست. این وظیفه برای یک web API  نوشته شده و آدرس root خاصی نداره و مطابق ذیل هست: 
        " {context.Request.Scheme}://{context.Request.Host.Value}"
        منتها وقتی آدرس root درخواست میشه در browser تبدیل به چنین آدرسی میشه به خاطر swagger : 
        https://domain.com/index.html
        authorize خاصی هم نداره.
        • #
          ‫۵ سال و ۳ ماه قبل، چهارشنبه ۱ خرداد ۱۳۹۸، ساعت ۱۹:۳۸
          HttpClient کار Auto Redirect را به صورت خودکار انجام می‌دهد. بنابراین مشکلی با این redirect ندارد. سایر لاگ‌های سرور را بررسی کنید تا علت bad request مشخص شود. یک مثال جدید Api هم با Swagger اضافه شد و با آن هم مشکلی نیست و PingTask بدون خطا کار می‌کند.
  • #
    ‫۵ سال و ۱ ماه قبل، دوشنبه ۷ مرداد ۱۳۹۸، ساعت ۰۳:۲۵
    سلام
    من برای .net core از لینک زیر استفاده کردم :
    اما در ویو مقدار IsRunning تسک dobackups برابر با false هست lastRun هم خالی ست . ممنون میشم بگید کجا رو دارم اشتباه میکنم ؟
    • #
      ‫۵ سال و ۱ ماه قبل، دوشنبه ۷ مرداد ۱۳۹۸، ساعت ۰۳:۴۲
      به نحوه‌ی تعریف آن وظیفه و خصوصا بازه‌ی زمانی اجرای آن دقت کنید؛ برای مثال قسمت now.Day % 3 == 0 آن یعنی صرفا یکسری روزهای خاص اجرا شود، نه تمام روزها. بعد now.Hour == 0 && now.Minute == 1 && now.Second == 1 آن یعنی، در آن روزهای خاص، فقط در اولین دقیقه‌ی روز اجرا شود و نه هیچ وقت دیگری.
  • #
    ‫۵ سال و ۱ ماه قبل، سه‌شنبه ۸ مرداد ۱۳۹۸، ساعت ۰۴:۲۸
    آیا می‌شود برای نسخه core هم کاری کرد که کاربر بتواند بصورت دستی تسکی را به دلخواه در زمانهای متفاوت زمانبندی کند یا غیرفعال کند؟ بطور مثال به تسک x از امروز تا سه روز دیگر نیاز دارد آنرا امروز فعال کند و سه روز بعد بصورت دستی آنرا غیر فعال نماید .
    • #
      ‫۵ سال و ۱ ماه قبل، سه‌شنبه ۸ مرداد ۱۳۹۸، ساعت ۰۴:۵۹
      - runAt را طوری تنظیم کنید که در طی یک بازه زمانی کار کند.
      - و یا سرویس IOptions<ScheduledTasksStorage> storage را به قسمتی از برنامه‌ی خودتان تزریق کرده و از متد RemoveScheduledTask آن استفاده کنید.
  • #
    ‫۵ سال و ۱ ماه قبل، دوشنبه ۲۱ مرداد ۱۳۹۸، ساعت ۲۳:۳۳
    آیا این امکان وجود دارد که در زمان اجرای برنامه تسکی را به مجموعه تسک‌ها اضافه نمود ؟ من در یک متد بصورت زیر تسکی را به مجموعه اضافه می‌کنم اما ارور وجود نداشتن سرویس برای تسک اضافه شده را دارم .

         public bool AddTask()
            {
                _tasksStorage.Value.AddScheduledTask<DoEnableProductTasks>(
                runAt: utcNow =>
                {
                    var now = utcNow.AddHours(4.5);
                    return now.Hour == 18 && now.Minute == 57 && now.Second == 1;
                },
                order: 1);
    
                return true;
            }

    • #
      ‫۵ سال و ۱ ماه قبل، دوشنبه ۲۱ مرداد ۱۳۹۸، ساعت ۲۳:۵۳
      - بجای اینکار (اضافه و حذف کردن سرویس‌ها در زمان اجرای برنامه که با ری‌استارت برنامه در سمت سرور غیرمعتبر می‌شود)، بهتر است با توجه به اینکه این وظایف به صورت یک سرویس ثبت می‌شوند، یک سرویس سفارشی فعال یا غیرفعالسازی را تعریف کنید و آن‌را به سازنده‌ی این وظایف تزریق و استفاده کنید. سپس زمانیکه حلقه‌ی انجام وظایف به RunAsync رسید، در اینجا فرصت خواهید داشت تا سرویس سفارشی جدید تزریق شده را بررسی کرده و از فعال بودن یا نبودن این وظیفه مطلع شوید (برای مثال این سرویس بر اساس نامی که به آن ارسال می‌کنید، به بانک اطلاعاتی مراجعه کرده و روشن و یا خاموش بودن آن‌را بررسی کند. تنظیم بانک اطلاعاتی آن‌را هم واگذار کنید به قسمت مدیریتی برنامه).  
      + این نوع Taskهای پویا را باید در ابتدای کار برنامه و در کلاس آغازین آن، به صورت زیر معرفی کنید:
      services.AddTransient<DoEnableProductTasks>();
  • #
    ‫۴ سال و ۳ ماه قبل، چهارشنبه ۲۱ خرداد ۱۳۹۹، ساعت ۱۶:۰۲
    چطوری میشه موقع Stop کردن IIS و یا stop شدن پروژه به هر دلیلی (قبل از Stop کامل) متدی call شود که آخرین اطلاعاتی که در رم و در job‌ها اجرا شده رو در دیتابیس ذخیره کرد؟
    مثلا تعداد کاربران آنلاین رو در یک متغیر static ذخیره می‌کنم که وقتی پروژه به هر دلیلی Stop شد میخوام آخرین اطلاعات متغیر استاتیک رو در دیتابیس ذخیره کنم.
    البته در یک وظیفه (job) در پایان هر روز کل تعداد بازدید‌ها رو به تاریخ همان روز در دیتابیس ذخیره می‌کنم ولی می‌خواستم اگر به هر دلیلی پروژه Stop شد آخرین تعداد رو تا لحظه Stop شدن در دیتابیس ذخیره کنم.

    برای حل این مشکل چه راهکاری پیشنهاد می‌کنید؟
    • #
      ‫۴ سال و ۳ ماه قبل، چهارشنبه ۲۱ خرداد ۱۳۹۹، ساعت ۱۶:۰۶
      راهکار امنی وجود ندارد. متد رویدادگردان Application_End در فایل global.asax.cs ممکن است در این موارد فراخوانی شود یا خیر.
  • #
    ‫۳ سال و ۱۱ ماه قبل، شنبه ۵ مهر ۱۳۹۹، ساعت ۱۵:۵۲
    سلام. روی هاستی که آپلود شده متد RunAsync در زمان‌های موعد دو مرتبه اجرا میشه
    • #
      ‫۳ سال و ۱۰ ماه قبل، یکشنبه ۱۱ آبان ۱۳۹۹، ساعت ۱۴:۲۸
      در واقع متد RunAt با توجه به لاگ هایی که گرفتم دو بار اجرا میشه که هر ثانیه دوبار لاگ میندازه که باعث میشه RunAsync هم دوباره اجرا بشه ، همین پروژه روی یک هاست قرار دادم مشکلی دیده نشد ولی روی سرور اختصاصی این مشکل دیده میشه. همچنین هر دو اجرا هر کدام یک وهله جدایی از دیگری هستند که مقادیر داخلی هر کدام جداگانه تغییر میکند و تاثیری روی هم ندارند.