c#可靠的延迟/预定执行最佳实践

时间:2009-03-26 03:23:12

标签: c# scheduled-tasks delayed-execution

我正在进行的项目要求在某个时间执行一些执行。我不确定处理这种情况的最佳方法是什么。该方法必须能够在服务器重启/维护后继续存在。方法调用必须以编程方式进行。

我正在考虑走这条道路:

我可以在数据库(甚至是消息队列)中创建一个名为TaskTable的表,它可以具有TaskID(PK),TaskName(varchar),TaskStatus(枚举成功,失败,已调度)和TimeOfExecution。但我需要一个Windows服务,定期轮询数据库以查找任何未执行的任务。我面临的问题是:我使用什么作为TaskName保存到数据库中?班级名称?类和方法名称ToString?我怎样才能将字符串转换回来并以编程方式调用方法调用(我不希望有一个巨大的switch语句)?一项持久性的任务如下所示。所以我必须能够获得任务“SendIncompleteNotification”的名称,并将类名保存到数据库中,并以回溯方式调用

public static Task<string> SendIncompleteNotification
{
  get
    {
      return new Task<string>
        (
          a => Console.WriteLine("Sample Task")
          , "This is a sample task which does nothing."
        );
   }
}

现在的问题是我无法以原理方式保存方法/属性名称。

var type = ApplicationTask.SendIncompleteNotification.GetType();
//type.Name shows "Task`1" rather than SendIncompleteNotification

有没有更好的方法来处理这种情况?谢谢!

更新: 抱歉,我的头在旋转。我现在意识到我做错了是有另一个方法/属性来返回我的任务。我应该做的是从我的任务中获得一个新的类inhrite。在那里,我可以轻松获取类名并将字符串保存到db中,然后再退回并调用。

4 个答案:

答案 0 :(得分:3)

您可能需要查看Windows Workflow。据我所知,它们专为长时间运行的进程而设计,可以持久保存到数据库并在事件或计时器上唤醒。

答案 1 :(得分:3)

数据库是否是必需的?

如果没有,那么Windows计划任务(他们倾向于“正常工作”)会调用一般控制台应用程序。控制台应用程序的参数可以是:

  • 包含要执行的任务的DLL
  • 实现您定义的接口的类的名称
  • 其他论点

通过这种方式,您可以将所有任务放入一个或多个任务中。或者,您可以创建一个属性,将该属性应用于您的任务以为其提供“友好名称”,并对程序集使用反射来查找具有匹配属性的类。

编辑:示例:

interface ITask
{
    void Execute(ExcecutionContext context);
}

[TaskName("Send Emails")
class SendEmailsTask : ITask
{
    public void Execute(ExcecutionContext context) 
    {
        // Send emails. ExecutionContext might contain a dictionary of 
        // key/value pairs for additional arguments needed for your task. 
    }
}

class TaskExecuter 
{
    public void ExecuteTask(string name) 
    {
        // "name" comes from the database entry
        var types = Assembly.GetExecutingAssembly().GetTypes();    
        foreach (var type in types)
        {
            // Check type.GetCustomAttributes for the TaskAttribute, then check the name
        }
    }
}

编辑2:这是对您的代码示例的回答。

class YourClass
{
    public static Task<string> SendIncompleteNotification
    {
        get {
            return new Task<string>(
                s => Console.WriteLine("Executing task... arguments: {0}", s),
                "My task");
        }
    }
}


interface ITask
{
    void Execute(object o);
}

class Task<T> : ITask
{        
    public Task(Action<T> action, string name)
    {
        Action = action;
    }

    public string Name { get; set; }
    public Action<T> Action { get; set; }

    void ITask.Execute(object o)
    {
        Action((T)o);
    }
}

class Program
{
    static void Main(string[] args)
    {
        // Assume that this is what is stored in the database
        var typeName = typeof (YourClass).FullName;
        var propertyName = "SendIncompleteNotification";
        var arguments = "some arguments";

        // Execute the task
        var type = Type.GetType(typeName);
        var property = type.GetProperty(propertyName);
        var task = (ITask)property.GetValue(null, null);
        task.Execute(arguments);
        Console.ReadKey();
    }
}

答案 2 :(得分:1)

存储程序集FullName,并键入FullName和方法名称。假设方法签名是可预测的(如没有参数并返回void)

1)使用Assembly类型的静态LoadFrom方法创建程序集的实例。

2)使用GetType方法

从程序集中获取类类型的引用

3)使用GetMethod方法从类型中获取MethodInfo实例

4)使用Activator.CreateInstance

创建该类型的实例

5)使用MethodInfo实例的Invoke执行方法,从第4步传入类实例。(抱歉,我在没有VS副本的公共计算机上发出实际代码,但这5个步骤会做。

此外,如果您使用的是SQL 2005,请考虑使用SqlDependency对象,并在您的talbe更改而不是轮询时获得“通知”。

答案 3 :(得分:1)

你看过Quartz,它运作得很好,而且我很确定它会实现你需要的所有功能。