Pergunta

O projeto que eu estou trabalhando em requer algumas execuções a ser feito em um determinado momento. Eu não estou certo de qual seria a melhor maneira de lidar com esta situação. O método deve ser capaz de sobreviver reinício do servidor / manutenção. E chamadas de método deve ser programaticamente.

Estou pensando em ir por este caminho:

Eu poderia ter uma tabela no banco de dados (ou até mesmo uma fila de mensagens) chamou TaskTable que poderia ter TaskID (PK), TaskName (varchar), TaskStatus (sucesso enum, falhou, programada) e TimeOfExecution. Mas eu preciso de um serviço de janelas que periodicamente o banco de dados para todas as tarefas não executadas. Problema que estou enfrentando é que: O que eu uso como o TaskName para salvar no banco de dados? Nome da classe? classe eo nome do método ToString? E como eu posso converter a volta corda e programaticamente invocar as chamadas de método (eu não quero ter uma instrução switch gigante)? Uma tarefa typtical seria semelhante abaixo. Então eu definidos para ser capaz de obter o nome da tarefa "SendIncompleteNotification" eo nome da classe salvá-lo em banco de dados e em retrival invocar programaticamente

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

O problema agora é que eu estou tendo problema ao salvar o nome do método / propriedade progrmatically.

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

Existe alguma maneira melhor de lidar com esta situação? Obrigado!

Atualizado: Desculpe minha cabeça estava girando. Agora eu percebi que eu fiz de errado era ter outro método / propriedade para retornar a minha tarefa. O que eu deveria ter feito era ter uma nova inhrite classe de minha tarefa. E lá eu posso facilmente obter o nome da classe e salvar a string em db e voltar mais tarde retireve e invocar.

Foi útil?

Solução

O banco de dados um requisito?

Se não, o que acontece com um de tarefas do Windows Scheduled (eles têm uma tendência a "trabalhar apenas") que põe em um aplicativo geral console. Os argumentos para a aplicação de console poderia ser:

  • A DLL que contém a tarefa de executar
  • O nome de uma classe que implementa uma interface que você definir
  • Outros argumentos

Desta forma, você pode colocar todas as suas tarefas em uma montagem ou múltipla. Alternativamente, você pode criar um atributo, aplicar esse atributo para as suas tarefas para dar-lhes um "nome amigável", e usar a reflexão sobre o conjunto para encontrar classes com o atributo correspondente.

Edit: exemplo:

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:. Isto é, em resposta à sua amostra de código

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();
    }
}

Outras dicas

Você pode querer olhar para Windows Workflow. Eles são projetados para os processos de execução longa, pode ser mantido em um banco de dados e acordado no evento ou timer, tanto quanto eu sei.

Guarde o FullName montagem e escreva FullName e o nome do método. Supondo que a assinatura do método é algo previsível (como sem parâmetros e retornando void)

1) criar uma instância do conjunto utilizando o método LoadFrom estático do tipo de montagem.

2) obter uma referência para o tipo de classe de sua montagem usando o método GetType

3) obter instância MethodInfo do tipo usando o método GetMethod

4) criar um exemplo do tipo utilizando Activator.CreateInstance

5) executar o método usando o Invoke da instância MethodInfo, passando a instância de classe a partir do passo 4. (pena que estou em um computador público sem uma cópia do VS a manivela para fora do código real, mas essas 5 etapas faria fazer.

Além disso, se você estiver usando SQL 2005 considere o uso de um objeto SqlDependency e ficar "notificado" quando seu talbe muda ao invés de polling.

Você já olhou para Quartz , ele funciona muito bem, e eu tenho certeza que ele implementa todos os recursos necessários.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top