2

我有一个从数据库加载配置的对象。我使用 datetime 字段(称为 )存储上次运行作业的时间,并使用名为GroupsLastRun的字符串字段存储作业应该运行的频率CaptureusersandgroupsCaptureusersandgroups存储三种不同类型的“每日”、“每周”和“每月”。

基本上,我有一个循环应该只在作业即将运行时继续。到目前为止,我已经达到以下几点:

if (configEntity.GroupsLastrun > DateTime.Now.AddDays(-1) && configEntity.Captureusersandgroups == "DAILY") continue;
if (configEntity.GroupsLastrun > DateTime.Now.AddDays(-7) && configEntity.Captureusersandgroups == "WEEKLY") continue;
if (configEntity.GroupsLastrun > DateTime.Now.AddDays(-30) && configEntity.Captureusersandgroups == "MONTHLY") continue;

我确信(确定)有更好的方法来解决这个问题,但主要是作为 SQL Server 开发人员,我缺乏解决这个问题的批判性思维/工具。什么是更好的方法或者我应该学习什么以便更好地思考这个问题?

4

3 回答 3

7

几点:

  • 除非您想受时区和夏令时转换等的支配,否则我会使用DateTime.UtcNow而不是DateTime.Now(并确保您也存储UTC 值)
  • 正如 pswg 所提到的,只需要询问当前日期/时间一次 - 但不是性能,我想说重要的原因是一致性。在这种情况下,您似乎只会实际使用其中一个值,但在其他情况下,我看到人们编写同时使用两个评估的条件,如果代码在午夜运行会导致问题
  • 由于检查条件并同时确定截止日期,您有重复的代码。我会把两者分开。

所以,我会有这样的代码:

// Consider whether you actually want DateTime.UtcNow.Date
DateTime now = DateTime.UtcNow;

DateTime deadline;
switch (configEntity.Captureusersandgroups)
{
    case "DAILY": deadline = now.AddDays(-1);
    case "WEEKYLY": deadline = now.AddDays(-7);
    case "MONTHLY": deadline = now.AddMonths(-1);
    // I'm assuming there's *always* a schedule
    default: throw new InvalidOperationException("Invalid schedule");
}
if (configEntity.GroupsLastrun > deadline)
{
    continue;
}

请注意,从“现在”中减去一个月与从“那时”中添加一个月不同。例如,如果最后一次运行是在 1 月 30 日,那么下一次运行将在 3 月 1 日之前使用上述代码 - 而如果您在 1 月 30 日添加一个月,它将在 2 月 28 日运行(除非您使用两个值的日期)。仔细考虑您想要的行为。

(作为一个快速插件,我显然还建议考虑使用我的Noda Time库进行日期/时间工作。它可以更清楚地说明任何特定值是本地时间还是某个时区等)

于 2013-04-10T06:06:42.810 回答
3

两点:

  1. DateTime.Now每次调用时都可以(潜在)返回不同的日期。它也不是很快。只需调用一次,您将获得更好的一致性和非常轻微的性能提升。
  2. 您可能应该使用标准方法来添加星期和月份,以保持一致性(例如,一个月并不总是 30 天)和全球化(例如,并非所有文化都有 7 天的星期)。请注意,没有简单的方法可以DateTime单独添加几周;你必须使用 aCalendar代替。

尝试这个:

var now = DateTime.UtcNow; // See Jon Skeet's answer
var cal = CultureInfo.InvariantCulture.Calendar;
if (configEntity.GroupsLastrun > now.AddDays(-1) && configEntity.Captureusersandgroups == "DAILY") continue;
if (configEntity.GroupsLastrun > cal.AddWeeks(now, -1) && configEntity.Captureusersandgroups == "WEEKLY") continue;
if (configEntity.GroupsLastrun > now.AddMonths(-1) && configEntity.Captureusersandgroups == "MONTHLY") continue;

或者只是cal用于一切:

var now = DateTime.UtcNow; // See Jon Skeet's answer
var cal = CultureInfo.InvariantCulture.Calendar;
if (configEntity.GroupsLastrun > cal.AddDays(now, -1) && configEntity.Captureusersandgroups == "DAILY") continue;
if (configEntity.GroupsLastrun > cal.AddWeeks(now, -1) && configEntity.Captureusersandgroups == "WEEKLY") continue;
if (configEntity.GroupsLastrun > cal.AddMonths(now, -1) && configEntity.Captureusersandgroups == "MONTHLY") continue;
于 2013-04-10T05:52:52.820 回答
1

作为一种替代方法,您可以将运行期间存储在enumlike 中:

enum RunPeriod
{
    Daily = 1,
    Weekly = 7,
    Monthly = 30
}

然后您可以将int值而不是存储string在数据库中。这允许您在数据库端进行过滤,例如:

var configsToRun = 
    from c in _myContext.Configs
    where EntityFunctions.AddDays(c.LastRun,(int)c.RunPeriod) > DateTime.Now);
于 2013-04-10T06:16:04.853 回答