Comment appeler une méthode tous les jours, à un moment précis, en C#?

j'ai cherché sur SO et j'ai trouvé des réponses sur Quartz.net. Mais il semble être trop grand pour mon projet. Je veux une solution équivalente, mais plus simple et (au mieux) en code (aucune bibliothèque externe requise). Comment puis-je appeler une méthode tous les jours, à un moment précis?

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

  • la façon la plus simple (et laide) de faire ceci, est de vérifier l'heure chaque seconde / minute et d'appeler la méthode ,au bon moment

je veux un moyen plus efficace de le faire, pas besoin de vérifier le temps constamment, et j'ai le contrôle pour savoir si le travail est fait ou non. Si la méthode échoue (à cause de problèmes), le programme devrait savoir écrire pour enregistrer/envoyer un email. C'est pourquoi j'ai besoin d'appeler une méthode, pas un travail.

j'ai trouvé cette solution appeler une méthode à temps fixe en Java en Java. Y a-t-il un moyen similaire en C#?

EDIT: j'ai fait cette. J'ai ajouté un paramètre dans void Main(), et créé une bat (scheduled by Windows Task Scheduler) pour exécuter le programme avec ce paramètre. Le programme fonctionne, fait le travail, puis sort. Si un travail échoue, il est capable d'écrire un journal et d'envoyer des e-mails. Cette approche correspond à mes besoins :)

64
demandé sur Community 2010-07-14 08:14:29

13 réponses

  • créez une application console qui fait ce que vous recherchez
  • Utiliser Windows " Tâches Planifiées fonctionnalité " d'avoir cette console application exécutée au moment où vous en avez besoin pour exécuter

C'est vraiment tout ce dont vous avez besoin!

mise à jour: si vous voulez faire cela dans votre application, vous avez plusieurs options:

  • dans un Windows Forms app, vous pourriez puiser dans le Application.Idle événement et de vérifier si vous avez atteint le moment dans la journée pour appeler votre méthode. Cette méthode n'est appelée que lorsque votre application n'est pas occupé avec d'autres choses. Une vérification rapide pour voir si votre cible a été atteinte ne faut pas mettre trop de stress sur votre application, je pense...
  • dans un ASP.NET application web, il ya des méthodes pour "simuler" l'envoi d'événements programmés - vérifier sur ce article de Codeprojet
  • et bien sûr, vous pouvez tout simplement "roll your own" dans n'importe quelle application .NET-consultez cet article de projet Cod pour un exemple d'application

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

Quelque chose comme ceci:

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;

et ensuite dans votre gestionnaire d'événements:

void checkForTime_Elapsed(object sender, ElapsedEventArgs e)
{
    if (timeIsReady())
    {
       SendEmail();
    }
}
69
répondu marc_s 2010-07-14 04:53:19

chaque fois que je construis des applications qui nécessitent une telle fonctionnalité, Je toujours utiliser le planificateur de tâches Windows par le biais d'une bibliothèque simple .NET que j'ai trouvé.

s'il vous Plaît voir ma réponse à une question similaire pour un exemple de code et de plus amples explications.

9
répondu Maxim Zaslavsky 2017-05-23 12:10:50

comme d'autres l'ont dit, Vous pouvez utiliser une application de console pour fonctionner à l'heure prévue. Ce que d'autres n'ont pas dit, c'est que vous pouvez Cette application déclencher un événement cross processwaithandle que vous attendez dans votre application principale.

Application Console:

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

Application 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
répondu Courtney D 2010-07-14 06:02:49

la meilleure méthode que je connaisse et probablement la plus simple est d'utiliser Windows Task Scheduler pour exécuter votre code à un moment précis de la journée ou vous faire exécuter l'application en permanence et vérifier pour un moment particulier de la journée ou écrire un service windows qui fait la même chose.

4
répondu Pieter Germishuys 2010-07-14 04:17:47

je sais que c'est vieux mais que dis-tu de ça:

construisez une minuterie pour démarrer au démarrage qui calcule l'Heure de la prochaine exécution. Au premier appel du runtime, annulez le premier minuteur et démarrez un nouveau daily timer. changez chaque jour en horaire ou ce que vous voulez que la périodicité soit.

4
répondu mansoor 2011-11-17 16:00:01

j'ai créé un simple programmateur qui est facile à utiliser et vous n'avez pas besoin d'utiliser une bibliothèque externe. TaskScheduler est un singleton qui garde les références sur les minuteries afin que les minuteries ne soient pas des ordures collectées, il peut planifier plusieurs tâches. Vous pouvez définir la première course (heure et minute), si au moment de la planification de cette heure est terminée de la planification commencer le lendemain de cette heure. Mais il est facile de personnaliser le code.

programmer une nouvelle tâche est si simple. Exemple: à 11: 52 la première tâche est pour chaque 15 secondes, le second exemple est pour chaque 5 secondes. Pour l'exécution quotidienne, mettez 24 au 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
...

il suffit d'ajouter 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);
    }
}
3
répondu jannagy02 2017-06-07 10:45:40

si vous voulez qu'un exécutable s'exécute, utilisez Windows Scheduled Tasks. 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 qui tourne en permanence et stocke la dernière date à laquelle la méthode a été appelée?

faites-le Se réveiller à chaque minute (par exemple) et, si l'heure courante est supérieure à l'heure spécifiée et que la dernière date stockée n'est pas la date courante, appelez la méthode ensuite mettre à jour la date.

1
répondu paxdiablo 2010-07-14 04:18:20

j'ai récemment écrit un c# app qui devait redémarrer quotidiennement. Je me rends compte que cette question Est ancienne, mais je ne pense pas que cela fasse de mal d'ajouter une autre solution possible. C'est comme ça que j'ai géré les redémarrages quotidiens à un moment précis.

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 vous assurer que votre minuterie reste dans la portée, je recommande de la créer en dehors de la méthode en utilisant la méthode System.Timers.Timer tmrRestart = new System.Timers.Timer() . Mettez la méthode RestartApp() dans votre événement de charge de formulaire. Lors du lancement de l'application, il 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 se produit à temps et que nous n'obtenons pas d'exception pour mettre une valeur négative dans le minuteur. Dans l'événement tmrRestart_Elapsed , lancez le code dont vous avez besoin à ce moment précis. Si votre application redémarre par elle-même, vous n'avez pas nécessairement besoin d'arrêter la minuterie mais cela ne fait pas de mal non plus, si l'application ne redémarre pas, il suffit d'appeler la méthode RestartApp() à nouveau et vous être bon d'aller.

1
répondu Fancy_Mammoth 2017-03-08 08:49:04

voici un moyen de le faire en utilisant TPL. Pas besoin de créer, de disposer d'un timer, 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
répondu Gino 2017-11-28 03:17:33

plutôt que de définir un temps pour exécuter chaque seconde de chaque 60 minutes, vous pouvez calculer le temps restant et régler le minuteur à la moitié (ou une autre fraction) de cela. De cette façon, le fait de ne pas vérifier l'heure autant, mais aussi de maintenir un certain degré de précision puisque l'intervalle de temps réduit le plus proche que vous obtenez à votre temps cible.

par exemple, si vous vouliez faire quelque chose dans 60 minutes, les intervalles de temps seraient approximatifs:

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

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

(converti 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);
    }
}

Note j'ai réglez un intervalle mininum de 1000 ms, cela peut être incrémenté, réduit ou supprimé en fonction de la précision dont vous avez besoin.

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

0
répondu apc 2015-12-18 11:11:18

j'ai une approche simple à ce sujet. Cela crée un délai d'une minute avant que l'action ne se produise. Vous pouvez ajouter des secondes aussi bien pour faire le Fil.Sleep(); le 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
répondu Gustav Bok 2017-01-05 10:59:12

C'est peut être juste moi, mais il semble que la plupart de ces réponses n'étaient pas complètes ou ne fonctionnerait pas correctement. J'ai fait quelque chose de très rapide et sale. Cela dit, Je ne sais pas si c'est une bonne idée 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);
}
0
répondu Deathstalker 2017-03-08 08:15:37

24 heures fois

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
répondu Pandurang Chopade 2018-06-26 11:33:22