web-dev-qa-db-fra.com

Comment appeler une méthode quotidiennement, à une heure précise, en C #?

J'ai cherché sur SO et trouvé des réponses sur Quartz.net. Mais il semble être trop gros pour mon projet. Je veux une solution équivalente, mais plus simple et (au mieux) en code (aucune bibliothèque externe n'est requise). Comment puis-je appeler une méthode quotidiennement, à une heure précise? 

J'ai besoin d'ajouter quelques informations à ce sujet:

  • la façon la plus simple (et laide) de le faire est de vérifier l'heure toutes les secondes et d'appeler la méthode au bon moment

Je veux un moyen plus efficace de faire cela, pas besoin de vérifier l'heure en permanence, et je contrôle si le travail est fait ou non. Si la méthode échoue (à cause de problèmes), le programme doit savoir écrire pour enregistrer/envoyer un courrier électronique. C'est pourquoi j'ai besoin d'appeler une méthode et non de planifier un travail.

J'ai trouvé cette solution Appeler une méthode à heure fixe en Java en Java. Existe-t-il une manière similaire en C #?

EDIT: je l'ai fait. J'ai ajouté un paramètre dans void Main () et créé une batte (planifiée par le Planificateur de tâches Windows) pour exécuter le programme avec ce paramètre. Le programme s'exécute, fait le travail, puis se ferme. Si un travail échoue, il est capable d'écrire un journal et d'envoyer un courrier électronique. Cette approche correspond bien à mes besoins :)

67
Vimvq1987
  • Créez une application console qui fait ce que vous cherchez
  • Utilisez la fonctionnalité Windows " Tâches planifiées " pour que cette application de console soit exécutée au moment où vous en avez besoin.

C'est vraiment tout ce dont vous avez besoin!

Mise à jour: si vous voulez le faire dans votre application, vous avez plusieurs options:

  • dans une application Windows Forms, vous pouvez accéder à l'événement Application.Idle et vérifier si vous avez bien atteint l'heure de la journée pour appeler votre méthode. Cette méthode est appelée uniquement lorsque votre application n'est pas occupée par d'autres tâches. Une vérification rapide pour voir si votre temps cible a été atteint ne devrait pas mettre trop de stress sur votre application, je pense ...
  • dans une application Web ASP.NET, il existe des méthodes pour "simuler" l'envoi d'événements planifiés - consultez ceci Article CodeProject
  • et bien sûr, vous pouvez aussi simplement "rouler votre propre" dans n'importe quelle application .NET - consultez cet article Article CodeProject pour un exemple d'implémentation

Update # 2: si vous voulez vérifier toutes les 60 minutes, vous pouvez créer une minuterie qui se réveille toutes les 60 minutes et, si le temps est écoulé, elle appelle la méthode.

Quelque chose comme ça:

using System.Timers;

const double interval60Minutes = 60 * 60 * 1000; // milliseconds to one hour

Timer checkForTime = new Timer(interval60Minutes);
checkForTime.Elapsed += new ElapsedEventHandler(checkForTime_Elapsed);
checkForTime.Enabled = true;

puis dans votre gestionnaire d'événements:

void checkForTime_Elapsed(object sender, ElapsedEventArgs e)
{
    if (timeIsReady())
    {
       SendEmail();
    }
}
70
marc_s

Chaque fois que je crée des applications qui nécessitent une telle fonctionnalité, je utilise toujours le planificateur de tâches Windows via une simple bibliothèque .NET que j'ai trouvé.

Please voir ma réponse à une question similaire pour un exemple de code et plus d'explications.

9
Maxim Zaslavsky

Comme d’autres l’ont déjà dit, vous pouvez utiliser une application console pour l’exécuter à l’horaire prévu. Ce que d'autres n'ont pas dit, c'est que cette application peut déclencher un processus croisé EventWaitHandle que vous attendez dans votre application principale.

Console App:

class Program
{
    static void Main(string[] args)
    {
        EventWaitHandle handle = 
            new EventWaitHandle(true, EventResetMode.ManualReset, "GoodMutexName");
        handle.Set();
    }
}

App principale:

private void Form1_Load(object sender, EventArgs e)
{
    // Background thread, will die with application
    ThreadPool.QueueUserWorkItem((dumby) => EmailWait());
}

private void EmailWait()
{
    EventWaitHandle handle = 
        new EventWaitHandle(false, EventResetMode.ManualReset, "GoodMutexName");

    while (true)
    {
        handle.WaitOne();

        SendEmail();

        handle.Reset();
    }
}
8
Courtney D

J'ai créé un planificateur simple, facile à utiliser et vous n'avez pas besoin d'utiliser une bibliothèque externe. TaskScheduler est un singleton qui conserve les références sur les minuteries afin que les minuteries ne soient pas mises au rebut, il peut planifier plusieurs tâches. Vous pouvez définir la première exécution (heure et minute) si, au moment de la planification, cette heure est dépassée et que la planification commence le jour suivant à cette heure. Mais il est facile de personnaliser le code.

Planifier une nouvelle tâche est si simple. Exemple: à 11h52, la première tâche concerne 15 secondes, le deuxième exemple, toutes les 5 secondes. Pour l'exécution quotidienne, définissez 24 sur le paramètre 3.

TaskScheduler.Instance.ScheduleTask(11, 52, 0.00417, 
    () => 
    {
        Debug.WriteLine("task1: " + DateTime.Now);
        //here write the code that you want to schedule
    });

TaskScheduler.Instance.ScheduleTask(11, 52, 0.00139,
    () =>
    {
        Debug.WriteLine("task2: " + DateTime.Now);
        //here write the code that you want to schedule
    });

Ma fenêtre de débogage:

task2: 07.06.2017 11:52:00
task1: 07.06.2017 11:52:00
task2: 07.06.2017 11:52:05
task2: 07.06.2017 11:52:10
task1: 07.06.2017 11:52:15
task2: 07.06.2017 11:52:15
task2: 07.06.2017 11:52:20
task2: 07.06.2017 11:52:25
...

Ajoutez simplement cette classe à votre projet:

public class TaskScheduler
{
    private static TaskScheduler _instance;
    private List<Timer> timers = new List<Timer>();

    private TaskScheduler() { }

    public static TaskScheduler Instance => _instance ?? (_instance = new TaskScheduler());

    public void ScheduleTask(int hour, int min, double intervalInHour, Action task)
    {
        DateTime now = DateTime.Now;
        DateTime firstRun = new DateTime(now.Year, now.Month, now.Day, hour, min, 0, 0);
        if (now > firstRun)
        {
            firstRun = firstRun.AddDays(1);
        }

        TimeSpan timeToGo = firstRun - now;
        if (timeToGo <= TimeSpan.Zero)
        {
            timeToGo = TimeSpan.Zero;
        }

        var timer = new Timer(x =>
        {
            task.Invoke();
        }, null, timeToGo, TimeSpan.FromHours(intervalInHour));

        timers.Add(timer);
    }
}
5
jannagy02

Je sais que c'est vieux mais que diriez-vous de ceci:

Construisez une minuterie qui se déclenche au démarrage et calcule le temps jusqu’à la prochaine exécution. Lors du premier appel du moteur d'exécution, annulez le premier chronomètre et démarrez un nouveau chronomètre quotidien. changez tous les jours en heure ou selon ce que vous voulez que la périodicité soit.

4
mansoor

La meilleure méthode que je connaisse, et probablement la plus simple, consiste à utiliser le Planificateur de tâches Windows pour exécuter votre code à une heure précise de la journée ou à laisser votre application s'exécuter en permanence et rechercher une heure particulière ou écrire un service Windows même. 

4
Pieter Germishuys

Voici une façon de faire cela en utilisant TPL. Pas besoin de créer/disposer d'une minuterie, etc.:

void ScheduleSomething()
{

    var runAt = DateTime.Today + TimeSpan.FromHours(16);

    if (runAt <= DateTime.Now)
    {
        DoSomething();
    }
    else
    {
        var delay = runAt - DateTime.Now;
        System.Threading.Tasks.Task.Delay(delay).ContinueWith(_ => DoSomething());
    }

}

void DoSomething()
{
    // do somethig
}
1
Gino

Si vous souhaitez exécuter un fichier exécutable, utilisez les tâches planifiées de Windows. Je vais supposer (peut-être à tort) que vous voulez une méthode à exécuter dans votre programme actuel.

Pourquoi ne pas simplement avoir un thread en cours d'exécution stockant en permanence la dernière date à laquelle la méthode a été appelée?

Faites-le se réveiller toutes les minutes (par exemple) et, si l'heure actuelle est supérieure à l'heure spécifiée et que la dernière date stockée n'est pas la date actuelle, appelez la méthode puis mettez à jour la date.

1
paxdiablo

J'ai récemment écrit une application c # qui devait être redémarrée quotidiennement. Je me rends compte que cette question est ancienne, mais je ne pense pas qu'il soit difficile d'ajouter une autre solution possible. Voici comment j'ai géré les redémarrages quotidiens à une heure spécifiée.

public void RestartApp()
{
  AppRestart = AppRestart.AddHours(5);
  AppRestart = AppRestart.AddMinutes(30);
  DateTime current = DateTime.Now;
  if (current > AppRestart) { AppRestart = AppRestart.AddDays(1); }

  TimeSpan UntilRestart = AppRestart - current;
  int MSUntilRestart = Convert.ToInt32(UntilRestart.TotalMilliseconds);

  tmrRestart.Interval = MSUntilRestart;
  tmrRestart.Elapsed += tmrRestart_Elapsed;
  tmrRestart.Start();
}

Pour que votre minuterie reste dans le champ d'application, je vous recommande de la créer en dehors de la méthode à l'aide de la méthode System.Timers.Timer tmrRestart = new System.Timers.Timer(). Placez la méthode RestartApp() dans votre événement de chargement de formulaire. Lors du lancement de l'application, elle définira les valeurs pour AppRestart si current est supérieur au temps de redémarrage, nous ajoutons 1 jour à AppRestart pour nous assurer que le redémarrage a lieu à l'heure et que nous ne recevons pas d'exception pour l'insertion d'une valeur négative dans le minuteur. Dans l'événement tmrRestart_Elapsed, exécutez le code dont vous avez besoin à ce moment précis. Si votre application redémarre d'elle-même, vous ne devez pas forcément arrêter le chronomètre, mais cela ne fait pas de mal non plus. Si l'application ne redémarre pas, il suffit d'appeler à nouveau la méthode RestartApp() et vous serez prêt à partir.

1
Fancy_Mammoth

C'est peut-être juste moi, mais il semblait que la plupart de ces réponses n'étaient pas complètes ou ne fonctionneraient pas correctement. J'ai fait quelque chose de très rapide et sale. Cela dit, je ne sais pas à quel point il est bon de le faire de cette façon, mais cela fonctionne parfaitement à chaque fois.

while (true)
{
    if(DateTime.Now.ToString("HH:mm") == "22:00")
    {
        //do something here
        //ExecuteFunctionTask();
        //Make sure it doesn't execute twice by pausing 61 seconds. So that the time is past 2200 to 2201
        Thread.Sleep(61000);
    }

    Thread.Sleep(10000);
}
1
Deathstalker

24 heures

var DailyTime = "16:59:00";
            var timeParts = DailyTime.Split(new char[1] { ':' });

            var dateNow = DateTime.Now;
            var date = new DateTime(dateNow.Year, dateNow.Month, dateNow.Day,
                       int.Parse(timeParts[0]), int.Parse(timeParts[1]), int.Parse(timeParts[2]));
            TimeSpan ts;
            if (date > dateNow)
                ts = date - dateNow;
            else
            {
                date = date.AddDays(1);
                ts = date - dateNow;
            }

            //waits certan time and run the code
            Task.Delay(ts).ContinueWith((x) => OnTimer());

public void OnTimer()
    {
        ViewBag.ErrorMessage = "EROOROOROROOROR";
    }
0
Pandurang Chopade

Plutôt que de définir un délai d’exécution toutes les secondes sur 60, vous pouvez calculer le temps restant et régler le chronomètre à la moitié (ou une autre fraction) de celui-ci. De cette façon, vous ne vérifiez pas l’heure autant, mais vous maintenez un degré de précision car l’intervalle de minuterie réduit le temps que vous vous approchez.

Par exemple, si vous voulez faire quelque chose dans 60 minutes, les intervalles de minuterie seront approximativement: 

30:00:00, 15:00:00, 07:30:00, 03:45:00, ..., 00:00:01, COUREZ!

J'utilise le code ci-dessous pour redémarrer automatiquement un service une fois par jour. J'utilise un fil parce que j'ai trouvé que les minuteries n'étaient pas fiables sur de longues périodes, bien que cela soit plus coûteux dans cet exemple, c'est le seul créé à cet effet, donc cela n'a pas d'importance. 

(Converti à partir de VB.NET)

autoRestartThread = new System.Threading.Thread(autoRestartThreadRun);
autoRestartThread.Start();

...

private void autoRestartThreadRun()
{
    try {
        DateTime nextRestart = DateAndTime.Today.Add(CurrentSettings.AutoRestartTime);
        if (nextRestart < DateAndTime.Now) {
            nextRestart = nextRestart.AddDays(1);
        }

        while (true) {
            if (nextRestart < DateAndTime.Now) {
                LogInfo("Auto Restarting Service");
                Process p = new Process();
                p.StartInfo.FileName = "cmd.exe";
                p.StartInfo.Arguments = string.Format("/C net stop {0} && net start {0}", "\"My Service Name\"");
                p.StartInfo.LoadUserProfile = false;
                p.StartInfo.UseShellExecute = false;
                p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
                p.StartInfo.CreateNoWindow = true;
                p.Start();
            } else {
                dynamic sleepMs = Convert.ToInt32(Math.Max(1000, nextRestart.Subtract(DateAndTime.Now).TotalMilliseconds / 2));
                System.Threading.Thread.Sleep(sleepMs);
            }
        }
    } catch (ThreadAbortException taex) {
    } catch (Exception ex) {
        LogError(ex);
    }
}

Remarque J'ai défini un intervalle minimum de 1000 ms. Il peut être incrémenté, réduit ou supprimé en fonction de la précision requise.

N'oubliez pas d'arrêter également votre thread/timer lorsque votre application se ferme.

0
apc

J'ai une approche simple à cela. Cela crée un délai d'une minute avant que l'action ne se produise. Vous pouvez également ajouter des secondes pour créer le Thread.Sleep (); plus court.

private void DoSomething(int aHour, int aMinute)
{
    bool running = true;
    while (running)
    {
        Thread.Sleep(1);
        if (DateTime.Now.Hour == aHour && DateTime.Now.Minute == aMinute)
        {
            Thread.Sleep(60 * 1000); //Wait a minute to make the if-statement false
            //Do Stuff
        }
    }
}
0
Gustav Bok

J'ai trouvé cela très utile:

using System;
using System.Timers;

namespace ScheduleTimer
{
    class Program
    {
        static Timer timer;

        static void Main(string[] args)
        {
            schedule_Timer();
            Console.ReadLine();
        }

        static void schedule_Timer()
        {
            Console.WriteLine("### Timer Started ###");

            DateTime nowTime = DateTime.Now;
            DateTime scheduledTime = new DateTime(nowTime.Year, nowTime.Month, nowTime.Day, 8, 42, 0, 0); //Specify your scheduled time HH,MM,SS [8am and 42 minutes]
            if (nowTime > scheduledTime)
            {
                scheduledTime = scheduledTime.AddDays(1);
            }

            double tickTime = (double)(scheduledTime - DateTime.Now).TotalMilliseconds;
            timer = new Timer(tickTime);
            timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
            timer.Start();
        }

        static void timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            Console.WriteLine("### Timer Stopped ### \n");
            timer.Stop();
            Console.WriteLine("### Scheduled Task Started ### \n\n");
            Console.WriteLine("Hello World!!! - Performing scheduled task\n");
            Console.WriteLine("### Task Finished ### \n\n");
            schedule_Timer();
        }
    }
}
0
JoKeRxbLaCk