locked
NullReferenceException when loading a WorkflowApplication RRS feed

  • Question

  • Hello,

    we are resuming a hibernated workflow, and get the following exception stack when loading:

    Workflow cannot be executed due to the following exception: System.NullReferenceException: De objectverwijzing is niet op een exemplaar van een object ingesteld.

       bij System.Activities.Hosting.WorkflowInstance.InitializeCore(IDictionary`2 workflowArgumentValues, IList`1 workflowExecutionProperties)

       bij System.Activities.Hosting.WorkflowInstance.Initialize(Object deserializedRuntimeState)

       bij System.Activities.WorkflowApplication.LoadCore(TimeSpan timeout, Boolean loadAny)

       bij System.Activities.WorkflowApplication.Load(Guid instanceId, TimeSpan timeout)

       bij System.Activities.WorkflowApplication.Load(Guid instanceId)

       bij EasyForm.NxG.Workflow.Engine.TaskExecuter.Execute() in c:\projects\autobuild\NxG 2.3\Sources\Code\Workflow\EasyForm.NxG.Workflow.Engine\TaskExecuter.cs:regel 191

    Can someone shed a light on how/why this happens? There's not enough information in the message nor stack trace to correct this workflow/fix the problem.

    With kind regards,

    Paul D'hertoghe

    Thursday, August 23, 2012 9:30 AM

All replies

  • I think you should supply the code you use for loading. Also, a translation from Dutch would be helpful :)

    Say what?

    Thursday, August 23, 2012 11:51 AM
  • Hi oweisman,

    The exception stack reads as follows when translated:

    Workflow cannot be executed due to the following exception: System.NullReferenceException: Object reference not set to an instance of an object.
       at System.Activities.Hosting.WorkflowInstance.InitializeCore(IDictionary`2 workflowArgumentValues, IList`1 workflowExecutionProperties)
       at System.Activities.Hosting.WorkflowInstance.Initialize(Object deserializedRuntimeState)
       at System.Activities.WorkflowApplication.LoadCore(TimeSpan timeout, Boolean loadAny)
       at System.Activities.WorkflowApplication.Load(Guid instanceId, TimeSpan timeout)
       at System.Activities.WorkflowApplication.Load(Guid instanceId)
       at EasyForm.NxG.Workflow.Engine.TaskExecuter.Execute() in c:\projects\autobuild\NxG 2.3\Sources\Code\Workflow\EasyForm.NxG.Workflow.Engine\TaskExecuter.cs:regel 191

    The bottom function in the call stack, which also loads the WorkflowApplication object, is detailed below (note that _workflowInstance is an object of our framework which holds all workflow data - that's also the reason why we are using an instancestore helper, we control persistency of the workflows ourselves):

            public void Execute()
            {
                Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss.fff") + " : thread " + System.Threading.Thread.CurrentThread.ManagedThreadId + " : TaskExecuter.Execute: Start execution of workflow '" + _workflowInstance.UniqueID + "'"); 
    
                try
                {
                    var definition = GetDefinition(_workflowInstance);
                    if (definition == null)   // a WorkflowInstanceEntity of an undefined WorkFlowVersionType
                    {
                        _workflowInstance.State = Entities.Enums.WorkflowInstanceState.Exception;
                        _workflowInstance.ExceptionMessage = "Workflow aborted because WorkFlowVersionType is not set.";
                        _site.DataFactory.UpdateEntities(_workflowInstance); // do not unlock workflowInstance: this is handled by the caller
                        _log.Error(_workflowInstance.ExceptionMessage);
                        return;
                    }
    
                    // make sure some necessary assemblies are loaded
                    ForceAssemblyLoading();
    
                    // when transitioning from NxG2.1 -> NxG2.2 the EasyForm.NxG.Code.EntityLib has an additional dependency on EasyFrom.NxG.Util.Common
                    // we have to import this extra assembly and it's namespaces to prevent VisualBasic expression compilation errors
                    VisualBasicSettings.Default.ImportReferences.Add(new VisualBasicImportReference()
                    {
                        Assembly = typeof(EasyForm.NxG.Util.Common.ChangeNotification.ChangeNotificationBase).Assembly.FullName,
                        Import = typeof(EasyForm.NxG.Util.Common.ChangeNotification.ChangeNotificationBase).Namespace
                    });
    
                    // setting site reference on helper object
                    DesignHelper.Instance.Site = _workflowInstance.SiteLoadedFrom;
    
                    // deserialize the workflow definition
                    var definitionReader = new StringReader(definition);
                    var activity = ActivityXamlServices.Load(definitionReader);
                    var dynamicActivity = activity as DynamicActivity;
    
                    // we wrap the implementation function with our own function to 
                    // have control over the implementation and be able to change start node if needed
                    var imp = dynamicActivity.Implementation;
                    dynamicActivity.Implementation = () =>
                    {
                        // we resolve only once the implementation and 
                        // each next time the implementation is requested, 
                        // we return the same reference instead of a 'new' implementation.
                        if (_rootactivity == null)
                        {
                            _rootactivity = imp() as NxGFlowchartActivity;
                        }
    
                        if (_rootactivity != null)
                        {
                            // change the start node if needed
                            ChangeStartNode(_workflowInstance, _rootactivity);
                            return _rootactivity;
                        }
                        else
                        {
                            return imp();
                        }
                    };
    
                    // check if it is a new one, ore one to resume
                    bool newInstance = String.IsNullOrEmpty(_workflowInstance.Data);
    
                    var inputs = GetInputs(_workflowInstance, dynamicActivity);
    
                    if (newInstance)
                    {
                        _workflowApplication = new WorkflowApplication(activity, inputs);
                    }
                    else
                    {
                        // if we resume a workflow, the input arguments should not be passed
                        // because the workflow already knows them
                        _workflowApplication = new WorkflowApplication(activity);
                    }
    
                    var tracking = new NxGTrackingParticipant(_workflowInstance);
                    var instanceStore = new NxGInstanceStore(_workflowInstance, tracking);
                    _workflowApplication.InstanceStore = instanceStore;
    
                    _workflowApplication.Extensions.Add<NxGTrackingParticipant>(() => tracking);
                    _workflowApplication.Extensions.Add<NxGCoreExtension>(() => new NxGCoreExtension(_workflowInstance, _resources, inputs, _rootactivity));
    
                    _workflowApplication.PersistableIdle = WorkflowApplication_PersistableIdle;
                    _workflowApplication.OnUnhandledException = WorkflowApplication_OnUnhandledException;
                    _workflowApplication.Completed = WorkflowApplication_Completed;
                    _workflowApplication.Unloaded = WorkflowApplication_Unloaded;
                    _workflowApplication.Aborted = WorkflowApplication_Aborted;     //  WorkflowApplication.Unloaded event is not called in Abort scenario
    
                    // load the workflow instance
                    if (!newInstance)
                    {
                        // because we use the workflow foundation not exactly as intended,
                        // we have to find the instanceid in the state of the instance.
                        // This id is autogenerated by the workflowapplication.
    
                        // We use the StringReader and XmlReader classes so we can deserialise characters that are invalid (such as \0) in 'official xml'
                        using (StringReader stringReader = new System.IO.StringReader(_workflowInstance.Data))
                        {
                            var settings = new XmlReaderSettings { IgnoreWhitespace = true, XmlResolver = null, CheckCharacters = false };
                            using (var xmlReader = XmlReader.Create(stringReader, settings))
                            {
                                var data = XElement.Load(xmlReader);
                                var instanceIdNode = data.Descendants()
                                    .Where(n => n.Name.LocalName == "WorkflowInstanceId")
                                    .Where(n => n.Parent.Name.LocalName == "Executor")
                                    .FirstOrDefault();
    
                                var workflowInstanceId = Guid.Parse(instanceIdNode.Value);
    
                                _workflowApplication.Load(workflowInstanceId);
                            }
                        }
                    }
    
                    // find and resume bookmark if available
                    var bookmarks = _workflowApplication.GetBookmarks();
                    var bookmark = bookmarks.FirstOrDefault();
    
                    _executionIsBusy = true;
    
                    if (bookmark != null)
                    {
                        // found bookmark and executing that bookmark
                        _log.Debug(" - found bookmark " + bookmark.BookmarkName + ", resuming");
                        _workflowApplication.ResumeBookmark(new Bookmark(bookmark.BookmarkName), null);
                    }
                    else
                    {
                        // no bookmarks, just execute
                        _workflowApplication.Run();
                    }
    
                    // wait until workflow is finished
                    _autoReset.WaitOne();
                }
                catch (Exception exc)
                {
                    _executionIsBusy = false;
                    // note: WorkflowApplication_Aborted and Exception handling can be run both at the same time (on different threads)
                    //       Therefore we lock the workflowInstance to prevent concurrent access
                    lock (_workflowInstance)
                    {
                        _workflowInstance.State = Entities.Enums.WorkflowInstanceState.Exception;
                        _workflowInstance.ExceptionMessage = "Workflow cannot be executed due to the following exception: " + exc.ToString();
                        // do not unlock workflowInstance: this is handled by the caller, and we run into problems because the Aborted handler might also save (which will fail if the workflowinstance is unlocked by then)
                        _site.DataFactory.UpdateEntities(_workflowInstance);
                    }
                    Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss.fff") + " : thread " + System.Threading.Thread.CurrentThread.ManagedThreadId + " : TaskExecuter.Execute: Exception caught for workflow '" + _workflowInstance.UniqueID + "'");
                    _log.Error(_workflowInstance.ExceptionMessage);
                }
                finally
                {
                    _executionIsBusy = false;
                }
            }
    

    Thursday, August 23, 2012 1:10 PM
  • Just off the top of my head (without looking into it too hard I admit),

    Are you certain that in the lines:

    var definitionReader = new StringReader(definition);
    var activity = ActivityXamlServices.Load(definitionReader);

    definitionReader is not null?

     

    Say what?

    Sunday, August 26, 2012 6:24 AM
  • How could definitionReader be null? The MSDN documentation states that the "new operator cannot be overloaded" and "If the new operator fails to allocate memory, it throws the  OutOfMemoryException". The class constructor could also throw an exception, but then again, an exception would be propagated and a null value would not be returned.

    And for all clarity: the contents of the variable "definition" is a valid workflow xaml definition.

    • Edited by PDHCoder Monday, August 27, 2012 7:02 AM
    Monday, August 27, 2012 7:00 AM
  • Sorry, my bad. Told you I didn't look at it too closely.

    Can I assume that it's the following line that throws the exception?

                                var instanceIdNode = data.Descendants()
                                    .Where(n => n.Name.LocalName == "WorkflowInstanceId")
                                    .Where(n => n.Parent.Name.LocalName == "Executor")
                                    .FirstOrDefault();
    
                                var workflowInstanceId = Guid.Parse(instanceIdNode.Value);
    
                                _workflowApplication.Load(workflowInstanceId);
    


    I'm pretty sure there are much bigger experts on Workflow persistence so I'll leave it to them.

    Just one last comment/ question: who persists the instance id mentioned in your comment
    // This id is autogenerated by the workflowapplication.

    ?


    Say what?





    • Edited by oweisman Wednesday, August 29, 2012 1:55 PM
    Wednesday, August 29, 2012 1:48 PM
  • Hi oweisman,

    it is indeed the code snippet that you extracted, which will give rise to the exception.

    Let me clarify the following comment from the code:

                      // because we use the workflow foundation not exactly as intended,
                       
    // we have to find the instanceid in the state of the instance.
                       
    // This id is autogenerated by the workflowapplication.

    We have a generic persistence layer which can save and fetch "entities", entities being documents with meta-data, users, batches, workflow instances...  Each entity has its own unique ID, which is a GUID. When a workflow is 'launced' (i.e. an instance of a certain workflow type is created), we create a "workflow instance entity" in our framework. Such a workflow instance entity refers to an existing workflow definition. The workflow instance entity is then picked up by an automatic task agent process which will create a WWF4 workflow for it and start executing it. WWF4 will assign a unique workflowInstanceId to each created WWF4 workflow instance, but this GUID has no relationship with the unique ID of the workflow instance entity in our framework. When the workflow is hibernated, the WWF4 workflowInstanceId is part of the serialised xml for the workflow instance. We save that xml data in our workflow instance entity. When the workflow is later resumed in our automatic task agent process, we have to retrieve that workflowInstanceId  from the xml data and hand it over to the WorkflowApplication object to load the workflow. Most likely, with the out-of-the box workflow persister, the workflow is fetched from the database based on this workflowInstanceId. In our implementation however, the workflow state has already been loaded, and is available through the NxGInstanceStore:

                   var tracking = new NxGTrackingParticipant(_workflowInstance);
                   
    var instanceStore = new NxGInstanceStore(_workflowInstance, tracking);
                    _workflowApplication
    .InstanceStore = instanceStore;

    Hope this sheds some light on things :-)

    With kind regards,

    Paul D'hertoghe


    • Edited by PDHCoder Wednesday, August 29, 2012 3:02 PM
    Wednesday, August 29, 2012 2:59 PM
  • Hey Paul,

    If you're positive that your instance store has been loaded correctly, and that the GUID is the right one, then I admit I'm at the end of my rope here. Other than waiting for a WF guru to respond, I'd suggest you try reflector or a look-alike (http://stackoverflow.com/questions/2425973/open-source-alternatives-to-reflector) to figure out which object is not assigned correctly when 

    System.Activities.Hosting.WorkflowInstance.InitializeCore is reached.

    Sorry I couldn't be of more help

    Orr


    Say what?

    Wednesday, August 29, 2012 5:00 PM
  • Hi Orr,

    facts are that the exception is rare, and other workflow instances based on the same definition run/hibernate/run without any problems. Hence we have every reason to believe that the instance code and GUID are correct (maybe there is some edge case in which it fails, but no idea what that could be).

    Thanks for the time you put into it, hopefully some WWF guru can provider further assistance

    Paul D'hertoghe


    • Edited by PDHCoder Thursday, August 30, 2012 7:12 AM
    Thursday, August 30, 2012 7:10 AM
  • Hello,

    We have the same problem as the one you reported in this thread.

    Did you ever find why and how to solve it ?

    Thanks in advance,

    Dominique Daue

    Thursday, September 12, 2013 11:21 AM