时间:2019-03-17 标签:c#reliabledelayed/scheduledexecutionbestpractice

发布于 2024-07-16 00:52:33 字数 1100 浏览 10 评论 0原文

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

我正在考虑走这条路:

我可以在数据库(甚至消息队列)中有一个名为 TaskTable 的表,它可以有 TaskID(PK)、TaskName(varchar)、TaskStatus(enum success、failed、scheduled) 和 TimeOfExecution。 但我需要一个 Windows 服务来定期轮询数据库以查找任何未执行的任务。 我面临的问题是:我使用什么作为任务名称保存到数据库中? 班级名称? 类和方法名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

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

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

The project I am working on requires some executions to be done at a certain time. I am not sure what would be the best way to deal with this situation. The method must be able to survive server restart/maintenance. And method calls must be programmatically.

I am considering going down this path:

I could have a table in database (or even a message queue) called TaskTable which could have TaskID(PK), TaskName(varchar), TaskStatus(enum success,failed, scheduled) and TimeOfExecution. But I need a windows service that periodically polls the database for any unexecuted tasks. Problem I am facing is that: What do I use as the TaskName to save into database? Class name? class and method name ToString? And how can I convert the string back and programmatically invoke the method calls (I don’t want to have a giant switch statement)? A typtical task would look like below. So I ned to be able to get the name of the task "SendIncompleteNotification" and class name save it into database and on retrival invoke programatically

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

The problem now is I am having problem saving the method/property name progrmatically.

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

Is there any better ways of dealing with this situation? Thanks!

Updated:
Sorry my head was spinning. I now realized what I did wrong was to have another method/property to return my Task. What I should have done was to have a new class inhrite from my Task. And there i can easily get the class name and save the string into db and later retireve back and invoke.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

ゃ人海孤独症 2024-07-23 00:52:33

数据库有要求吗?

如果没有,那么调用通用控制台应用程序的 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();
    }
}

Is the database a requirement?

If not, what about a Windows Scheduled Task (they have a tendancy to "just work") which calls into a general console app. The arguments to the console app could be:

  • A DLL containing the task to execute
  • The name of a class implementing an interface you define
  • Other arguments

This way you can put all of your tasks into one assembly, or multiple. Alternatively you could create an attribute, apply that attribute to your tasks to give them a "friendly name", and use reflection over the assembly to find classes with the matching attribute.

Edit: example:

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
        }
    }
}

Edit 2: This is in answer to your code sample.

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();
    }
}
一百个冬季 2024-07-23 00:52:33

您可能想研究一下 Windows 工作流。 据我所知,它们是为长时间运行的进程而设计的,可以持久保存到数据库并在事件或计时器上唤醒。

You might want to look into Windows Workflow. They are designed for long running processes, can be persisted to a database and woken up on event or timer, as far as I know.

剪不断理还乱 2024-07-23 00:52:33

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

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

2) 使用 GetType 方法从程序集中获取对类类型的引用

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

4) 使用 Activator.CreateInstance 创建类型的实例

5) 使用MethodInfo 实例,传入第 4 步中的类实例。(抱歉,我在公共计算机上没有 VS 的副本来编写真实代码,但这 5 个步骤就可以了。

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

Store the assembly FullName, and type FullName and the method name. Assuming the method signature is something predictable (like no parameters and returning void)

1) create an instance of the assembly using the static LoadFrom method of the Assembly type.

2) get a reference to the class type from your assembly using the GetType method

3) get MethodInfo instance from the type using the GetMethod method

4) create an instance of the type using Activator.CreateInstance

5) execute the method using the Invoke of the MethodInfo instance, passing in the class instance from step 4. (sorry that I'm at a public computer without a copy of VS to crank out real code but those 5 steps would do.

Also if you're using SQL 2005 consider using a SqlDependency object and getting "notified" when your talbe changes rather than polling.

比忠 2024-07-23 00:52:33

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

Have you looked at Quartz, it works pretty well, and I'm pretty sure it implements all the features you need.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文