So I'm using Hangfire schedule jobs that send out reminders to users. Whenever I set the Job to run every monday for example at 14h00. If I do this in the morning say at 09h00. The first execution, should go off at 14h00. Problem is what seems to be happening is when I look in the dashboard it says Next execution is in 7 days.
Heres the code :
public class ReminderTimeService : IReminderTimeService
{
private readonly IRecurringJobManager _recurringJobClient;
public ReminderTimeService(
IRecurringJobManager recurringJobClient)
{
_recurringJobClient = recurringJobClient;
}
public void ScheduleJobs(List<ReminderTime> reminderTimeList)
{
foreach (var reminder in reminderTimeList)
{
_recurringJobClient.AddOrUpdate<IProactiveMessageService>(
// Job Id
$"send-status-set-reminder-{reminder.Day.Substring(0, 3)}-
{reminder.StartTime.Split(":")[0]}-{reminder.StartTime.Split(":")[1]}",
// Service to run work
c => c.SendStatusSetReminder(),
// Repeat weekly
GetWeekCronExpression(reminder.Day, reminder.StartTime),
// Use local time
TimeZoneInfo.Local);
}
private DayOfWeek GetDayOfTheWeek(string day)
{
switch (day)
{
case "Monday":
return DayOfWeek.Monday;
case "Tuesday":
return DayOfWeek.Tuesday;
case "Wednesday":
return DayOfWeek.Wednesday;
case "Thursday":
return DayOfWeek.Thursday;
case "Friday":
return DayOfWeek.Friday;
default:
return DayOfWeek.Monday;
}
}
private string GetWeekCronExpression(string day, string time)
{
var hour = Convert.ToInt16(time.Split(":")[0]);
var minute = Convert.ToInt16(time.Split(":")[1]);
var weekStartExp = Cron.Weekly(GetDayOfTheWeek(day), hour, minute);
return weekStartExp;
}
}
}
My guess is theres something I'm fundamentally not understanding or theres a bug with Hangfire.
In any case I'm happy to just get a work around.
Thanks
Maybe there is a timezone problem and your hour:minute pair is slightly in the past. I just created a recurring job with manual parameters of Cron.Weekly() and in the dashboard it says, it will be perform in two hours (as expected).
Maybe you should hover over the string In 7 days on the dashboard, then you'll see the exact time. Check if it is your expected time and check with the debugger what comes within the StartTime property in your reminder object.
If all of these don't help, would be my last rescue to also add an ordinary job by calling jobClient.Enqueue() with the desired time.
Related
I have a class that checks if the current day of the week is Wednesday. It looks like this:
public class Wednesday
{
public string IsWednesday()
{
if (DateTime.Now.DayOfWeek == DayOfWeek.Wednesday)
{
return "It’s Wednesday";
}
else
{
return "It’s not Wednesday";
}
}
}
Don't worry about the return type, I know that it's bad in this particular example to return a magic string. We're focused on its purpose, not implementation.
I would love to cover it with unit tests for both cases of the current weekday being Wednesday and being anything but Wednesday.
I am new to unit testing and I'm wondering what the best solution would be in this example.
Right now, I'm using VS2019 with MS Test V2. I've tested both scenarios like this:
[TestClass]
public class WednesdayTests
{
[TestMethod]
public void IsWednesday_CurrentWeekDayIsWednesday_ReturnsItsWedndesday()
{
if (!(DateTime.Now.DayOfWeek == DayOfWeek.Wednesday))
{
return;
}
// Arrange
Wednesday wednesdayObj = new Wednesday();
// Act
string result = wednesdayObj.IsWednesday();
// Assert
Assert.AreEqual("It’s Wednesday", result);
}
[TestMethod]
public void IsWednesday_CurrentWeekDayIsNotWednesday_ReturnsItsNotWednesday()
{
if (!(DateTime.Now.DayOfWeek != DayOfWeek.Wednesday))
{
return;
}
Wednesday wednesdayObj = new Wednesday();
string result = wednesdayObj.IsWednesday();
Assert.AreEqual("It’s not Wednesday", result);
}
}
Is it considered OK to return out of a test method when some condition is not met, like in my case?
Or is there something inherently wrong with my solution?
Glad to hear any suggestions from experienced software developers!
P.S. Just noticed that the test will only be conducted if it's actually Wendesday :) Wow, that's for sure is not the solution!
One of the fundamental principles of Unit Testing is that it should be repeatable and self-contained.
Yours, as is, is not.
One way to make unit testable methods depending on current date is to feed them with a constant "now" value (a value provided by the test runner).
public string IsWednesday() => IsWednesday(DateTime.Now);
public string IsWednesday(DateTime time)
{
if (time.DayOfWeek == DayOfWeek.Wednesday)
{
return "It’s Wednesday";
}
else
{
return "It’s not Wednesday";
}
}
Then you can run the test with fixated dates, covering both cases "wednesday" and "not wednesday". This approach is an example of a general strategy: whatever is provided during normal execution by the environment (machine name, date, culture, etc) should be a parameter in the method under test, and the test bench should provide a range of significant values.
Regarding the actual tests (if I may, do yourself a favor and use NUnit or XUnit): they become trivial.
[TestMethod]
public void ItsNotWednesday()
{
var wednesdayObj = new Wednesday();
var result = wednesdayObj.IsWednesday(new DateTime(2019, 10, 5)); // Not a wednesday
Assert.AreEqual("It’s not Wednesday", result);
}
I would say it is bad practice to have return; in unit tests.
One of the biggest benefits with unit tests is that they make changing (read refactoring) the production code a straight forward process... and we don't want that process to be dependent on what weekday someone decides to change the code on.
If your test, and code, is dependent on time you need a way to control the clock in your test.
And in practice that mean that you need to hide the fact that your production code depends on DateTime.Now behind an abstraction that you can control in your test case.
i am trying to make make app expire after some days using the registry option, i have successfully written and read from registry, my issue is to check for expiration, below is my code:
regKey = Registry.CurrentUser.OpenSubKey("Systemfiles");//subkeyname
if (regKey == null)
{
regKey = Registry.CurrentUser.CreateSubKey("Systemfiles");
regKey.SetValue("tere", Encrypt("4/16/2017"));
}
else
{
regKey = Registry.CurrentUser.OpenSubKey("Systemfiles");//subkeyname
string decryptDateValue = Decrypt(regKey.GetValue("tere").ToString()); //Keyname
DateTime mainDate = Convert.ToDateTime(decryptDateValue);
DateTime expiringDate = mainDate.AddDays(1);
if (mainDate > expiringDate)
{
expire = true;
}
else
{
//Continue execution
}
}
from my code i assumed the user's first run is the 04/16/2017, i also assumed i want the user to run the application for one day, which supposed to expire on the 04/17/2017, meaning if the user tries to start the application after 04/17/2017 the if part should execute, but i am not really getting it right, the else part always execute, i will appreciate a better way of doing it. Thanks
You've got this in your code:
DateTime expiringDate = mainDate.AddDays(1);
if (mainDate > expiringDate)
So,expiringDate would always be bigger than mainDate (one day exactly).
What you want to check is the actual Date, so it should be:
if (DateTime.Now>expiringDate)
My web-site returns information for items which it takes from disk (involving some logic located in the controller, so it is not just static assets). I tried to optimize it by returning 304 for items which are not changed, by getting file write time for the corresponding item. Now, after I update the code, my application still thinks that an item is not updated and returns 304 - it does not realize that application code is changed so the result would be different. Because of that users do not see the update immediately, only after they get rid of their cache. I would like to solve that problem by checking not only 'item update time' but also 'application update time'. Is there a way to get something like time when application was updated? By this time I would like to see kind of maximum of update times of all application files.
UPD:
As asked for example code, here is slightly simplified version:
public static DateTime? LastKnownDate(this HttpRequestBase request)
{
if (!string.IsNullOrEmpty(request.Headers["If-Modified-Since"]))
{
var provider = CultureInfo.InvariantCulture;
DateTime date;
if (DateTime.TryParse(
request.Headers["If-Modified-Since"],
provider,
DateTimeStyles.RoundtripKind,
out date)) return date;
}
return null;
}
public ActionResult Test(int id)
{
var path = #"C:\data\" + id;
var file = new FileInfo(path);
if (!file.Exists) return HttpNotFound();
var date = Request.LastKnownDate();
if (date != null && date >= file.LastWriteTimeUtc)
{
return Response.NotModified();
}
Response.AddHeader("Last-Modified", file.LastWriteTimeUtc.ToString("o"));
return File(path, "application/octet-stream");
}
I think you need something like HTTP conditional GET. More details in the spec.
This is how you can do that in ASP.NET : http://optimizeasp.net/conditional-get
Also take a loot at: Improving Performance with Output Caching
Im struggling to check if there is at least a minute between two date times. I created a game in c# and have limited a part of my game to once a minute every time this command is
executed it runs a void
The problem is that it does it even if it hasn't been a minute?
public void _CheckIfBeenAMinute
{
string TimeStamp;
using (IQueryAdapter dbClient = SilverwaveEnvironment.GetDatabaseManager().getQueryreactor())
{
dbClient.setQuery("SELECT game_timestamp FROM users WHERE id=" + Session.Id + "");
TimeStamp = dbClient.getString();
}
DateTime TimeStamp_Converted = Convert.ToDateTime(TimeStamp);
if (TimeStamp_Converted > DateTime.UtcNow.AddMinutes(-1))
{
//It has been a minuted...
//But the problem is, i it hasnt been it still does this?
this.SendMessage("You have reached your limit today");
return;
}
}
EDIT: I have decided to use timespan. But when I try to get the seconds of the timespan after it has reached 60 it resets?
Try
if ((DateTime.UtcNow - TimeStamp_Converted).TotalMinutes > 1)
It should be:
if (TimeStamp_Converted < DateTime.UtcNow.AddMinutes(-1))
It has to be trivial, but I just cannot get through it.
I have to limit amount of tasks (let's say connections, emails sent or clicks in the button) per amount of time. So e.g. I can send 1000 emails per hour.
How can I do that in c#? I don't know and don't care how much time each operation will take. I just want to make sure that for last hour, only 1000 will be executed.
class EventLimiter
{
Queue<DateTime> requestTimes;
int maxRequests;
TimeSpan timeSpan;
public EventLimiter(int maxRequests, TimeSpan timeSpan)
{
this.maxRequests = maxRequests;
this.timeSpan = timeSpan;
requestTimes = new Queue<DateTime>(maxRequests);
}
private void SynchronizeQueue()
{
while ((requestTimes.Count > 0) && (requestTimes.Peek().Add(timeSpan) < DateTime.UtcNow))
requestTimes.Dequeue();
}
public bool CanRequestNow()
{
SynchronizeQueue();
return requestTimes.Count < maxRequests;
}
public void EnqueueRequest()
{
while (!CanRequestNow())
Thread.Sleep(requestTimes.Peek().Add(timeSpan).Subtract(DateTime.UtcNow));
// Was: System.Threading.Thread.Sleep(1000);
requestTimes.Enqueue(DateTime.UtcNow);
}
}
Assuming a rolling hour window:
Maintain a list of when actions were done.
Each time you want to do your action, remove all in the list not within the hour.
If there are fewer than 1000 then do the action and add a record to your list.
Assuming hourly:
Create a proxy method and a variable that is incremented for every action, and reduced to zero on the hour.
Do your action if the counter is < 1000.
The above solution looked fine. Here is my trimmed down version:
public class EmailRateHelper
{
private int _requestsPerInterval;
private Queue<DateTime> _history;
private TimeSpan _interval;
public EmailRateHelper()
: this(30, new TimeSpan(0, 1, 0)) { }
public EmailRateHelper(int requestsPerInterval, TimeSpan interval)
{
_requestsPerInterval = requestsPerInterval;
_history = new Queue<DateTime>();
_interval = interval;
}
public void SleepAsNeeded()
{
DateTime now = DateTime.Now;
_history.Enqueue(now);
if (_history.Count >= _requestsPerInterval)
{
var last = _history.Dequeue();
TimeSpan difference = now - last;
if (difference < _interval)
{
System.Threading.Thread.Sleep(_interval - difference);
}
}
}
}
You can use Rx extensions (How to use the new BufferWithTimeOrCount in Rx that returns IObservable<IObservable<T>> instead of IObservable<IList<T>>), but I would implement the buffering manually by adding an appropriate proxy object.
You may also consider storing {action, time, user} information in a database and get number of actions in a last hour fomr the DB (or similar persisted storager) if you need to handle Application pool restarts / crashes. Otherwise clever user may circumvent your in-memory protection with overloading your server.
You can create a persistent counter for every user. Every time you receive a request (for sending an email) you need to check the value of the counter and the date of the counter creation.
If the count is greater than the limit you refuse the request
If the date is older than an hour you reset the counter and set the new creation date
If the date is correct and the count is under the limit you increase the counter
Only in the last two cases the request is executed.