0

I have a dummy webisite where I want to run a task in a while(true) loop and I want it to run forever. I'm in a school project about soccer bets and I need this (sort of) cloud website to automatically update games and results for me everyday, insert them in the database and send emails, depending on eacher users bets (if they gots points in the game or not).

Although, I can't seem to keep it alive. This is my code:

 private static string LigacaoBD = @"connectionString";
 private static ApiMethods sportRadar = new ApiMethods();
 private static Jogo jogo = new Jogo(LigacaoBD);
 private static List<SportRadar.ClassesCliente.Jogo> jogos;
 private static List<SportRadar.ClassesCliente.Competicao> competicoes;

 protected void Page_Load(object sender, EventArgs e)
 {
      // Iniciates a new asynchronous task
     Task.Factory.StartNew(() => Actualizacoes());

 }


 public void Actualizacoes()
 {
     bool verif = true;

     while (true)
     {
         // Time in which I want the task to run the task on the method
         if (DateTime.UtcNow.ToLocalTime().Hour == 1 && DateTime.UtcNow.ToLocalTime().Minute == 30 && DateTime.UtcNow.ToLocalTime().Second == 0)
                verif = true;


         // quando voltar a ficar online será actualizar.
         if (verif)
         {
             // I want the games 7 days from now
             DateTime dt = new DateTime(DateTime.UtcNow.ToLocalTime().Year, DateTime.UtcNow.ToLocalTime().Month, DateTime.UtcNow.ToLocalTime().Day + 7);
             // This is due to the providers limitation to trial accounts
             Thread.Sleep(1000);
             // Makes the request for the tournaments to the API
             competicoes = sportRadar.Competicoes();

             Thread.Sleep(1000);
             // Makes the request for the daily schedules to the API
             jogos = sportRadar.JogosDoDia(dt);

             // Saves each game in each tournament to the database
             foreach (SportRadar.ClassesCliente.Jogo j in jogos)
             {
                 foreach (SportRadar.ClassesCliente.Competicao c in competicoes)
                     if (j.Id_Torneio == c.Id)
                     {
                         jogo.Guardar(j.Clube_Casa, j.Clube_Visitante, c.Nome, c.Pais, j.Data);
                         break;
                     }
             }
             // I want the results from the day before
             dt = new DateTime(DateTime.UtcNow.ToLocalTime().Year, DateTime.UtcNow.ToLocalTime().Month, DateTime.UtcNow.ToLocalTime().Day-1);

             Thread.Sleep(1000);
             // Makes the request for the daily results to the API
             jogos = sportRadar.ResultadosDoDia(dt);

             // Updates the results on the database and sends e-mails according to the points aquried but each user
             foreach (SportRadar.ClassesCliente.Jogo j in jogos)
             {
                 foreach (SportRadar.ClassesCliente.Competicao c in competicoes)
                     if (j.Id_Torneio == c.Id)
                     {
                         List<Utilizador> utilizadores = jogo.AlterarResultado(j.Clube_Casa, j.Clube_Visitante, c.Nome, c.Pais, j.Data, j.Golos_Casa, j.Golos_Visitante);

                         if (utilizadores.Count > 0)
                         {
                             foreach (Utilizador u in utilizadores)
                                    Verificacoes.EnviarEmail("smtp.live.com", "betmagnum@hotmail.com", "Senha098!", u.Email,
                                        "BetMagnum - Ganhou pontos num jogo!", "Parabéns, " + u.Nickname + ". Ganhou " + u.Pontuacao + " pontos no jogo " +
                                        j.Clube_Casa + " - " + j.Clube_Visitante + ".<br/><br/>Resultado do jogo:<br/>" +
                                        j.Clube_Casa + " " + j.Golos_Casa + " - " + j.Golos_Visitante + " " + j.Clube_Visitante + "<br/><br/>" +
                                        "Pontos ganhos: "+u.Pontuacao, 587);
                            }
                            break;
                        }
                }

                verif = false;

            }
            Thread.Sleep(1000);
        }
    }

So, if I open the page, it will be active and will go to the page load and run the task. Everything goes smoothly. But on the next day, I don't get any updates to the database or emails unless I open the page again, which is not what I want.

How can this be achieved?

Thanks for the help, in advance.

Soheil Alizadeh
  • 2,936
  • 11
  • 29
  • 56
taiko
  • 458
  • 6
  • 22
  • 2
    Have you considered using an service? ... This is also a posibility: https://www.hangfire.io/ – Stefan Aug 20 '17 at 11:52
  • Does https://www.hanselman.com/blog/HowToRunBackgroundTasksInASPNET.aspx help? – mjwills Aug 20 '17 at 11:59
  • [Quartz.net](https://www.quartz-scheduler.net/) can also be used to schedule stuff. However you have to consider AppPool recycles and the 20 minute sleep of the pool. If you truly want to reliably do stuff every x minutes or at specific intervals, use a service or program on the server. – VDWWD Aug 20 '17 at 12:01
  • Thanks. From what you've suggested, I more inclined to HangFire. Although, I see I can only use the database direclty through GlobalConfiguration.Configuration.UseSqlServerStorage, apparently. Can I use this or comething similar to be able to use my class library? It already has the connections I need and to the specified stored procedures and validations, etc. I could be wrong, but from what I've seen this does not let you do that. Am I wrong? – taiko Aug 20 '17 at 12:47
  • I was testing it with simple messages on the console printing DateTime.Now (to see if the addorupdate method would update minutely as I teel it to) and it seems get the datetime only once and then always prints the same. This it run through the method only once? If so, it won't do because have have to be able to make the request to the provider. That and seems that everytime I restart the project it keeps the other threads and add this new one. So, it shows all of them, including the new one. – taiko Aug 20 '17 at 14:16
  • Ok, my mistake. I understand the process of HangFire better, now. I'm still having a problem with adding recurring jobs in asp.net (tested previously on console and it worked fine but I guess the process is different in asp.net). I'll open another question for this. Thank you all. @Stefan, since it was you who suggested Hangfire in the first place, I think you should put it into an answer so I can accept it. :) – taiko Aug 21 '17 at 15:51
  • @taiko: as requested :-) – Stefan Aug 22 '17 at 08:37

1 Answers1

1

As I mentioned in the comment: You should consider using a service, or perhaps hang-fire which is, according to there website:

An easy way to perform background processing in .NET and .NET Core applications. No Windows Service or separate process required.

Backed by persistent storage. Open and free for commercial use.

https://www.hangfire.io/

There are some easy examples on there homepage, like:

var jobId = BackgroundJob.Schedule(
     () => Console.WriteLine("Delayed!"),
     TimeSpan.FromDays(7));

or

//recurring CRON
RecurringJob.AddOrUpdate(
    () => Console.WriteLine("Recurring!"),
    Cron.Daily);

CRON is nice actually, for scheduled tasks, see https://en.wikipedia.org/wiki/Cron

note: if you are using hangfire, the process which created the instance must be alive to execute the tasks. Although the persistent storage saves the tasks and execute them if delayed, if your application is not running, the task will not be executed at the exact given moment.

In your case, if your app-pool is idle or not running because it has been recycled, the task will execute at the next warmup cycle of your website, this typically occurs when you try to access the site.

If this is unwanted, you can configure your web-host (IIS) to keep the application pool alive or do an immediate startup after recycling. The "how to" depends a bit on the host you are using, but basically it's the same on almost every system.

More on this matter:

IIS https://serverfault.com/questions/333907/what-should-i-do-to-make-sure-that-iis-does-not-recycle-my-application

IIS https://technet.microsoft.com/en-us/library/cc753179(v=ws.10).aspx

or: https://weblog.west-wind.com/posts/2013/Oct/02/Use-IIS-Application-Initialization-for-keeping-ASPNET-Apps-alive

or in worst case: How do I set up my IIS to keep my application alive?

Community
  • 1
  • 1
Stefan
  • 17,448
  • 11
  • 60
  • 79
  • Wait, so this means that the server is going to sleep on it, either way? I'm using the somee server so don't have access to the IIS. Does this mean that HangFire is not going to keep alive, then? – taiko Aug 22 '17 at 09:59
  • Well, you should try to google that I guess. I haven't used hangfire myself for a while, while I was working with it, it itself didn't keep the app-pool alive. Maybe they changed that because it's an obvious addition. What it does assure is that, even if the app-pool is down, the delayed jobs will run when it comes back again. – Stefan Aug 22 '17 at 10:07
  • Here's what the docs say: http://docs.hangfire.io/en/latest/deployment-to-production/making-aspnet-app-always-running.html – Stefan Aug 22 '17 at 10:09
  • I forgot to say that it worked. But I had to make requests to it to keep the thread alive. So, I used uptimerobot's keyword request monitor and it worked wonders. Thanks. :) – taiko Sep 09 '17 at 21:35
  • 1
    cool, glad you managed to work it out :) – Stefan Sep 10 '17 at 16:02