[WF] Programmer n’est pas jouer

Publié le 03 avril 2014 par Jeremy.jeanson

Parfois un me remonte des problèmes liés à la méthode ScheduleActivity() du Context.

Les problèmes sont de deux sortes :

  • L'activité ne s'exécute pas.
  • Les activités ne s'exécutent pas dans le bon ordre.

Dans la plupart des cas, il s'agit d'une incompréhension de ce que fait exactement cette méthode et du fonctionnement du scheduler.

Dans WF, il n'y a pas à proprement parler de méthode pour exécuter une activité immédiatement. On doit systématiquement passer par le Context pour lui demander de planifier l'exécution d'une activité. Notre activité ne sera donc exécutée qu'en sortie de la méthode Execute de l'activité qui demande sa planification. Cela sous-entend que l'on peut planifier plusieurs activités à partir de la même méthode Execute().

Ex :

    protected override void Execute(NativeActivityContext context)
    {
        if ((this.branches != null) & (this.Branches.Count != 0))
        {
            CompletionCallback onCompleted = new CompletionCallback(this.OnBranchComplete);
            for (int i = this.Branches.Count - 1; i >= 0; i--)
            {
                context.ScheduleActivity(this.Branches[ i ], onCompleted);
            }
        }
    }

Trois activités sont bien planifiées, aucune n'a encore commencé à s'exécuter en sortie de la méthode Execute. Mais il n'y a aucune garantie que les activités seront exécutées dans l'ordre Activity1, Activity2, Activity3. Il n'est même pas certain qu'elles vont s'exécuter.

Pourquoi ?

Les raisons sont toutes simples :

  • D'autres activités ou l'une des trois activités planifiées peuvent mettre fin au workflow en cours d'exécution.
  • Certaines de ces activités sont peut-être asynchrones.
  • L'hôte peut demander l'annulation du workflow en cours d'exécution.

Si on veut contrôler l'ordre d'exécution, il faut planifier les activités une à une et attendre la fin de la précédente pour lancer la suivante.

Ex : (attention, cette séquence est fournie à titre d'exemple, elle ne peut être utilisée avec la persistance, car l'index n'est pas persisté)

/// 
/// Activity based on NativeActivity
/// 
public sealed class MySequence : NativeActivity
{
    public readonly Collection m_Activities;
    private Int32 m_Index;

    private CompletionCallback m_CompletionCallback;
    private FaultCallback m_FaultCallback;

    /// 
    /// Constructeur
    /// 
    public MySequence()
    {
        this.m_Activities = new Collection();
    }

    /// 
    /// Collection d'activités de la séquence
    /// 
    public Collection Activities
    {
        get { return this.m_Activities; }
    }

    /// 
    /// Execute
    /// 
    /// WF context
    /// 
    protected override void Execute(NativeActivityContext context)
    {
        // Initialisation
        this.m_Index = 0;

        // Caching callback delegates
        this.m_CompletionCallback = new CompletionCallback(this.MyCompletionCallback);
        this.m_FaultCallback = new FaultCallback (this.MyFaultCallback);

        // Schedule next activity
        this.ScheduleNext(context); 
    }

    /// 
    /// Schedule next activity in activities collection
    /// 
    /// 
    private void ScheduleNext(NativeActivityContext context)
    {
        if (this.m_Index < this.m_Activities.Count)
        {
            context.ScheduleActivity(
                this.m_Activities[this.m_Index],
                this.m_CompletionCallback,
                this.m_FaultCallback);

            this.m_Index++;
        }
    }

    public void MyCompletionCallback(NativeActivityContext context, ActivityInstance completedInstance)
    {
        this.ScheduleNext(context);
    }

    public void MyFaultCallback(NativeActivityFaultContext faultContext, Exception propagatedException, ActivityInstance propagatedFrom)
    {
        throw propagatedException;
    }


    /// 
    /// Register activity's metadata
    /// 
    /// 
    protected override void CacheMetadata(NativeActivityMetadata metadata)
    {
        if (this.m_Activities != null
            & this.m_Activities.Count != 0)
        {
            foreach (Activity a in this.m_Activities)
            {
                metadata.AddChild(a);
            }
        }
    }
}