locked
WF 4 update 1. Problem after resuming a state machine RRS feed

  • Question

  • Hello,

    I am building a deployment process workflow with the stateMachine activity included in WF .Net Framework 4 update 1.

    This is a simple state machine where at each state a notification email is sent to different members of a company. Between two steps of the deployment process, my statemachine goes to a waiting state where the workflow is persisted and then unloaded.

    My SendMail activity works perfectely well (I have made tests with putting this activity in many states and everything goes well, all emails are sent correctly). I have used the SendMail activity code provided in WF samples on MSDN.

    My 'wait' activity works perfectly too. I have used code provided in the apress book : Begining WF in .Net 4.0

    Here is my problem :

    Before a waiting state, every mail is sent correctly. No problem.
    When my workflow is resumed, everything goes as if everything is ok BUT my mail is not sent.
    I see some NullReferenceExceptions in the VS2010 intellitrace but no way to catch them!

    I have the feeling that since I am using .NET 4 update 1 statemachine, my workflow is not loaded correctly and there is some data missing. However all my variables are restored correctly.

    Everything was working perfectly well when I used StateMachine in CTP Pack.

    Do wait activity / load method have to be coded differently under this new framework ?

    I have provided some code :

     

    Here is the code of the Wait activity
    ---------------------------------------------------------------------------------------------------

    public sealed class WaitForAnswer<T> : NativeActivity<T>
     {
      public WaitForAnswer()
       : base()
      {
      }
    
      public string BookmarkName { get; set; }
      public OutArgument<T> Input { get; set; }
    
      protected override void Execute(NativeActivityContext context)
      {
       context.CreateBookmark(BookmarkName,
       new BookmarkCallback(this.Continue));
      }
    
      void Continue(NativeActivityContext context, Bookmark bookmark,
      object obj)
      {
       Input.Set(context, (T)obj);
      }
      protected override bool CanInduceIdle { get { return true; } }
    
    
    

    Here is the code of the send mail activity:

    =========================================================

     

    [Designer(typeof(SendEMailDesigner))]
     public sealed class SendEMail : AsyncCodeActivity
     {
      // Define activity input arguments
      [RequiredArgument]
      public InArgument<Collection<string>> To { get; set; }
    
      [RequiredArgument]
      public InArgument<MailAddress> From { get; set; }
    
      [RequiredArgument]
      public InArgument<Deployment> Deployment { get; set; }
    
      public InArgument<Collection<Attachment>> Attachments { get; set; }
      public InArgument<Collection<string>> CC { get; set; }
      public InArgument<Collection<string>> Bcc { get; set; }
    
      public InArgument<string> Body { get; set; }
      public InArgument<string> Subject { get; set; }
    
      //Private properties associated with the activity
      private int Port;
      private bool EnableSsl;
      private string UserName;
      private string Password;
      private string Host;
    
      /// <summary>
      /// Configures the private properties of the activity with the parameters set in the configuration file
      /// </summary>
      public SendEMail()
      {
       int port = 25;
       Int32.TryParse(System.Configuration.ConfigurationManager.AppSettings["SmtpPort"], out port);
       this.Port = port;
    
       bool ssl = false;
       Boolean.TryParse(System.Configuration.ConfigurationManager.AppSettings["EnableSSL"], out ssl);
       this.EnableSsl = ssl;
    
       this.UserName = System.Configuration.ConfigurationManager.AppSettings["SmtpLogin"];
       this.Password = System.Configuration.ConfigurationManager.AppSettings["SmtpPassword"];
       this.Host = System.Configuration.ConfigurationManager.AppSettings["SmtpHost"];
      }
    
      /// <summary>
      /// Check if the data in the workflow editor is set correctly
      /// </summary>
      /// <param name="metadata">Metadata associated with the code activity</param>
      protected override void CacheMetadata(CodeActivityMetadata metadata)
      {
       if (this.Body == null)
       {
        metadata.AddValidationError("Property 'Body' of SendMail activity cannot be null or empty");
       }
    
       if (this.Subject == null)
       {
        metadata.AddValidationError("Property 'Subject' of SendMail activity cannot be null or empty");
       }
    
       if (this.From == null)
       {
        metadata.AddValidationError("Property 'From' of SendMail activity cannot be null or empty");
       }
    
       if (this.To == null)
       {
        metadata.AddValidationError("Property 'To' of SendMail activity cannot be null or empty");
       }
    
       if (this.Deployment == null)
       {
        metadata.AddValidationError("Property 'Deployment' of SendMail activity cannot be null or empty");
       }
    
       base.CacheMetadata(metadata);
      }
    
      //Builds and sends the mail asynchronously
      protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
      {
       try
       {
        MailMessage message = new MailMessage();
        message.IsBodyHtml = true;
        message.From = this.From.Get(context);
    
        foreach (string address in this.To.Get(context))
        {
         message.To.Add(address);
        }
    
        Collection<string> ccList = this.CC.Get(context);
        if (ccList != null)
        {
         foreach (string address in ccList)
         {
          message.CC.Add(address);
         }
        }
    
        Collection<string> bccList = this.Bcc.Get(context);
        if (bccList != null)
        {
         foreach (string address in bccList)
         {
          message.Bcc.Add(address);
         }
        }
    
        Collection<Attachment> attachments = this.Attachments.Get(context);
        if (attachments != null)
        {
         foreach (Attachment attachment in attachments)
         {
          message.Attachments.Add(attachment);
         }
        }
    
        message.Subject = this.Subject.Get(context);
        message.Body = this.Body.Get(context);
    
        SmtpClient client = new SmtpClient();
        client.Host = this.Host;
        client.Port = this.Port;
        client.EnableSsl = this.EnableSsl;
    
        if (string.IsNullOrEmpty(this.UserName))
        {
         client.UseDefaultCredentials = true;
        }
        else
        {
         client.UseDefaultCredentials = false;
         client.Credentials = new NetworkCredential(this.UserName, this.Password);
        }
    
        SendMailAsyncResult sendMailAsyncResult = new SendMailAsyncResult(client, message, callback, state);
        context.UserState = sendMailAsyncResult;
        return sendMailAsyncResult;
       }
       catch (Exception ex)
       {
        Console.WriteLine(ex.ToString());
        throw ex;
       }
      }
    
      protected override void EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
      {
       //TODO : Check if the mail has been sent correctly
      }
     }
    


     

    Here is the code the method that loads my workflow:
    ==========================================================================

     

    public void ResumeStateMachine(Guid workflowId, Decision newDecision, string author, string reason)
      {
       DeploymentDataDataContext dc = new DeploymentDataDataContext(InstanceStoreConnectionString);
       dc.Refresh(System.Data.Linq.RefreshMode.OverwriteCurrentValues, dc.Deployments);
    
       Deployment depl = dc.Deployments.SingleOrDefault<Deployment>(x => x.WorkflowId == workflowId);
    
       if (depl != null)
       {
        if (newDecision != Decision.NotDecided)
        {
         depl.LastDecision = newDecision;
         depl.Reason = reason;
         depl.LastDecisionAuthor = author;
        }
        else
        {
         throw new Exception("Error : can't resume the workflow without a decision.");
        }
    
        depl.LastUpdate = DateTime.Now;
        dc.SubmitChanges();
    
        //Resuming workflowApplcation
        StateMachineWorkflow _stateMachine = new StateMachineWorkflow();
        _workflowApplication = new WorkflowApplication(_stateMachine);
        _workflowApplication.InstanceStore = _instanceStore;
        
        _workflowApplication.PersistableIdle = (e) =>
         {
          return PersistableIdleAction.Unload;
         };
        
        _workflowApplication.Completed = (e) =>
        {
         _instanceUnloaded.Set();
        };
    
        _workflowApplication.Unloaded = (e) =>
        {
         _instanceUnloaded.Set();
        };
    
        try
        {
         _workflowApplication.Load(workflowId);
        }
        catch (Exception ex)
        {
         throw ex;
        }
    
        try
        {
         System.Collections.ObjectModel.ReadOnlyCollection<System.Activities.Hosting.BookmarkInfo> bookmarkCollection = _workflowApplication.GetBookmarks();
         
         if (bookmarkCollection != null && bookmarkCollection.Count > 0)
         {
          string bookmarkName = bookmarkCollection[0].BookmarkName;
    
          _workflowApplication.ResumeBookmark(bookmarkName, depl);
         }
    
         else
         {
          throw new Exception("Can't retrieve bookmark collection from workflow : " + workflowId);
         }
         
        }
        catch (Exception ex)
        {
         depl.LastDecision = Decision.NotDecided;
         throw ex;
        }
       }
    
       else
       {
        throw new Exception("Error : Can't find deployment with workflowId : " + workflowId);
       }
      }
    


     

    Thanks a lot for your help.

     


    • Edited by brix303 Saturday, June 18, 2011 11:34 AM correction
    Friday, June 17, 2011 7:17 PM

All replies

  • >I see some NullReferenceExceptions in the VS2010 intellitrace but no way to catch them!

    If you have a debugger attached, try turning off 'Just My Code' (which is required for setting breakpoints in Workflow Designer, but will mask exceptions occuring inside the framework, which is more important now) and then enabling break upon exception thrown for type NullReferenceException.

    Tim

    Wednesday, March 14, 2012 6:18 AM