Link to home
Start Free TrialLog in
Avatar of jackwebb22002
jackwebb22002Flag for United States of America

asked on

Workflow, passing an object to a workflow

I'm learning Workflow using the book ProWF by Bruce Bukovics, and have a question.

I see examples of passing value type parameters from the host to the workflow, but when I

try to pass a class type parameter, I get:
"Event \"OccasionReceived\" on interface type \"SharedWorkflows.IOrderServices\" for

instance id \"99a5a2a5-155c-4e34-a793-1fbf792f7727\" cannot be delivered."

Relevant Snippets:

IOrderServices.cs

    /// <summary>
    /// Define the contract for placing an order
    /// </summary>
    [ExternalDataExchange]
    public interface IOrderServices
      {
        /// <summary>
        /// Occasion
        /// </summary>
        event EventHandler<OccasionReceivedEventArgs> OccasionReceived;

        /// <summary>
        /// Send a message to the host application
        /// </summary>
        /// <param name="message"></param>
        void OnSendMessage(String message);

        /// <summary>
        /// Send a request to host to get Occasion
        /// </summary>
        /// <param name="message"></param>
        void OnSendRequestOccasion(String message);
    }

OrderServics.cs


using System;
using System.Workflow.Activities;
using System.Workflow.Runtime;

namespace SharedWorkflows
{
    /// <summary>
    /// A local service that provides events used to create an Order
    /// </summary>
    public class OrderService : IOrderServices
      {
        #region IOrderServices
        public event EventHandler<OccasionReceivedEventArgs> OccasionReceived;

        /// <summary>
        /// Send a message from a workflow to the host application
        /// </summary>
        /// <param name="message"></param>
        public void OnSendMessage(String message)
        {
            if (MessageReceived != null)
            {
                MessageReceivedEventArgs args = new MessageReceivedEventArgs

(WorkflowEnvironment.WorkflowInstanceId, message);
                MessageReceived(this, args);
            }
        }

        /// <summary>
        /// Send a request from workflow to host to get Occasion
        /// </summary>
        /// <param name="message"></param>
        public void OnSendRequestOccasion(String message)
        {
            if (RequestOccasion != null)
            {
                MessageReceivedEventArgs args = new MessageReceivedEventArgs

(WorkflowEnvironment.WorkflowInstanceId, message);
                RequestOccasion(this, args);
            }
        }
        #endregion

        #region Members used by the host application
        public event EventHandler<MessageReceivedEventArgs> MessageReceived;
        public event EventHandler<MessageReceivedEventArgs> RequestOccasion;

        public void OnOccasionReceived(OccasionReceivedEventArgs args)
        {
            if (OccasionReceived != null)
            {
                OccasionReceived(null, args);
            }
        }
        #endregion
      }
}



OccasionReceivedEventArgs.cs :

using System;
using System.Workflow.Activities;
using pos_N3;

namespace SharedWorkflows
{
    /// <summary>
    /// Passes an Occasion from the UI to the workflow via the local service
    /// </summary>
    [Serializable]
    public class OccasionReceivedEventArgs : ExternalDataEventArgs
    {
        public Occasion Occasion { get; set; }
        public OccasionReceivedEventArgs(Guid instanceId, Occasion occasion)
            : base(instanceId)
        {
            Occasion = occasion;
        }
    }
}

UI Occasion selected by the click of a button.  Button's Tag property contains actual

Occasion row from Database:


        private void btnOccasion_Click(object sender, EventArgs e)
        {
            Button x = (Button)sender;
            Occasion y = (Occasion)x.Tag;
            Standards.at_Occasion = pos_N3.Occasion.Clone(y);
            if (Standards.at_Occasion != null)
            {
                OccasionReceivedEventArgs args = new OccasionReceivedEventArgs(_instanceId,

Standards.at_Occasion);
                args.WaitForIdle = true;
                try
                {
                    _orderService.OnOccasionReceived(args);
                }
                catch (Exception exception)
                {
                    String jjwMsg = exception.Message;
                    MessageBox.Show(exception.Message, "Sending Occasion Exception",

MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }


Error occurs just above where I tried the _orderService.OnOccasionReceived(args);

Has anyone passed a class parameter to a workflow?  If so, what did I not get from the

book?  Or was I asleep during that chapter?

Thanks ever so much,

JJW
Avatar of Reza Rad
Reza Rad
Flag of New Zealand image

Is the Occassion class serializable?

Avatar of jackwebb22002

ASKER

Do you mean in it's original definition, i.e., in pos_N3?  The answer is no, the class is defined as such:

Within pos_N3, the Occasion class is a "Linq To Sql" added item.  Which in effect, derives the class from the SQL databse table "Occasion".

Is there an easy way to make the class serializable?
for making a class serializable just you need to add [Serializable] tag before class definition. but in linq to sql i didn't check it before , you can check it yourselft.

a question:
Is it compile time error or runtime error?

runtime error at the line that I mentioned in the oroginal post.

MSDN says to set the Serialization mode of the ClassDataContext in the O/R designer to "Unidirectional".  Which I did.  This forces each Class to have the "DataContract()" attribute, which in turn sets each field's property to have the "DataMember()" attribute assigned.  Thus the Class becomes serializable.

But all to no avail, I still get the orignal "Event "OccasionReceived" on interface type "SharedWorkflows.IOrderServices" for instance id "SOME GUID" cannot be delivered."




<Table(Name:="dbo.Occasion"),  _
 DataContract()>  _
Partial Public Class Occasion
	Implements System.ComponentModel.INotifyPropertyChanging, System.ComponentModel.INotifyPropertyChanged
	
	Private Shared emptyChangingEventArgs As PropertyChangingEventArgs = New PropertyChangingEventArgs(String.Empty)
	
	Private _OccasionID As Integer
	
	Private _OccasionDescription As String
	
	Private _RequiresName As Boolean
	
	Private _RequiresPhone As Boolean
	
	Private _RequiresAddress As Boolean
	
	Private _Fee As Decimal
	
	Private _FeeDescription As String
	
	Private _isDefault As Boolean
	
	Private _Orders As EntitySet(Of [Order])
	
	Private serializing As Boolean
	
    #Region "Extensibility Method Definitions"
    Partial Private Sub OnLoaded()
    End Sub
    Partial Private Sub OnValidate(action As System.Data.Linq.ChangeAction)
    End Sub
    Partial Private Sub OnCreated()
    End Sub
    Partial Private Sub OnOccasionIDChanging(value As Integer)
    End Sub
    Partial Private Sub OnOccasionIDChanged()
    End Sub
    Partial Private Sub OnOccasionDescriptionChanging(value As String)
    End Sub
    Partial Private Sub OnOccasionDescriptionChanged()
    End Sub
    Partial Private Sub OnRequiresNameChanging(value As Boolean)
    End Sub
    Partial Private Sub OnRequiresNameChanged()
    End Sub
    Partial Private Sub OnRequiresPhoneChanging(value As Boolean)
    End Sub
    Partial Private Sub OnRequiresPhoneChanged()
    End Sub
    Partial Private Sub OnRequiresAddressChanging(value As Boolean)
    End Sub
    Partial Private Sub OnRequiresAddressChanged()
    End Sub
    Partial Private Sub OnFeeChanging(value As Decimal)
    End Sub
    Partial Private Sub OnFeeChanged()
    End Sub
    Partial Private Sub OnFeeDescriptionChanging(value As String)
    End Sub
    Partial Private Sub OnFeeDescriptionChanged()
    End Sub
    Partial Private Sub OnisDefaultChanging(value As Boolean)
    End Sub
    Partial Private Sub OnisDefaultChanged()
    End Sub
    #End Region
	
	Public Sub New()
		MyBase.New
		Me.Initialize
	End Sub
	
	<Column(Storage:="_OccasionID", AutoSync:=AutoSync.OnInsert, DbType:="Int NOT NULL IDENTITY", IsPrimaryKey:=true, IsDbGenerated:=true),  _
	 DataMember(Order:=1)>  _
	Public Property OccasionID() As Integer
		Get
			Return Me._OccasionID
		End Get
		Set
			If ((Me._OccasionID = value)  _
						= false) Then
				Me.OnOccasionIDChanging(value)
				Me.SendPropertyChanging
				Me._OccasionID = value
				Me.SendPropertyChanged("OccasionID")
				Me.OnOccasionIDChanged
			End If
		End Set
	End Property
	
	<Column(Storage:="_OccasionDescription", DbType:="NVarChar(255) NOT NULL", CanBeNull:=false),  _
	 DataMember(Order:=2)>  _
	Public Property OccasionDescription() As String
		Get
			Return Me._OccasionDescription
		End Get
		Set
			If (String.Equals(Me._OccasionDescription, value) = false) Then
				Me.OnOccasionDescriptionChanging(value)
				Me.SendPropertyChanging
				Me._OccasionDescription = value
				Me.SendPropertyChanged("OccasionDescription")
				Me.OnOccasionDescriptionChanged
			End If
		End Set
	End Property
	
	<Column(Storage:="_RequiresName", DbType:="Bit NOT NULL"),  _
	 DataMember(Order:=3)>  _
	Public Property RequiresName() As Boolean
		Get
			Return Me._RequiresName
		End Get
		Set
			If ((Me._RequiresName = value)  _
						= false) Then
				Me.OnRequiresNameChanging(value)
				Me.SendPropertyChanging
				Me._RequiresName = value
				Me.SendPropertyChanged("RequiresName")
				Me.OnRequiresNameChanged
			End If
		End Set
	End Property
	
	<Column(Storage:="_RequiresPhone", DbType:="Bit NOT NULL"),  _
	 DataMember(Order:=4)>  _
	Public Property RequiresPhone() As Boolean
		Get
			Return Me._RequiresPhone
		End Get
		Set
			If ((Me._RequiresPhone = value)  _
						= false) Then
				Me.OnRequiresPhoneChanging(value)
				Me.SendPropertyChanging
				Me._RequiresPhone = value
				Me.SendPropertyChanged("RequiresPhone")
				Me.OnRequiresPhoneChanged
			End If
		End Set
	End Property
	
	<Column(Storage:="_RequiresAddress", DbType:="Bit NOT NULL"),  _
	 DataMember(Order:=5)>  _
	Public Property RequiresAddress() As Boolean
		Get
			Return Me._RequiresAddress
		End Get
		Set
			If ((Me._RequiresAddress = value)  _
						= false) Then
				Me.OnRequiresAddressChanging(value)
				Me.SendPropertyChanging
				Me._RequiresAddress = value
				Me.SendPropertyChanged("RequiresAddress")
				Me.OnRequiresAddressChanged
			End If
		End Set
	End Property
	
	<Column(Storage:="_Fee", DbType:="Money NOT NULL"),  _
	 DataMember(Order:=6)>  _
	Public Property Fee() As Decimal
		Get
			Return Me._Fee
		End Get
		Set
			If ((Me._Fee = value)  _
						= false) Then
				Me.OnFeeChanging(value)
				Me.SendPropertyChanging
				Me._Fee = value
				Me.SendPropertyChanged("Fee")
				Me.OnFeeChanged
			End If
		End Set
	End Property
	
	<Column(Storage:="_FeeDescription", DbType:="NVarChar(255)"),  _
	 DataMember(Order:=7)>  _
	Public Property FeeDescription() As String
		Get
			Return Me._FeeDescription
		End Get
		Set
			If (String.Equals(Me._FeeDescription, value) = false) Then
				Me.OnFeeDescriptionChanging(value)
				Me.SendPropertyChanging
				Me._FeeDescription = value
				Me.SendPropertyChanged("FeeDescription")
				Me.OnFeeDescriptionChanged
			End If
		End Set
	End Property
	
	<Column(Storage:="_isDefault", DbType:="Bit NOT NULL"),  _
	 DataMember(Order:=8)>  _
	Public Property isDefault() As Boolean
		Get
			Return Me._isDefault
		End Get
		Set
			If ((Me._isDefault = value)  _
						= false) Then
				Me.OnisDefaultChanging(value)
				Me.SendPropertyChanging
				Me._isDefault = value
				Me.SendPropertyChanged("isDefault")
				Me.OnisDefaultChanged
			End If
		End Set
	End Property
	
	<Association(Name:="Occasion_Order", Storage:="_Orders", ThisKey:="OccasionID", OtherKey:="OccasionID"),  _
	 DataMember(Order:=9, EmitDefaultValue:=false)>  _
	Public Property Orders() As EntitySet(Of [Order])
		Get
			If (Me.serializing  _
						AndAlso (Me._Orders.HasLoadedOrAssignedValues = false)) Then
				Return Nothing
			End If
			Return Me._Orders
		End Get
		Set
			Me._Orders.Assign(value)
		End Set
	End Property
	
	Public Event PropertyChanging As PropertyChangingEventHandler Implements System.ComponentModel.INotifyPropertyChanging.PropertyChanging
	
	Public Event PropertyChanged As PropertyChangedEventHandler Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
	
	Protected Overridable Sub SendPropertyChanging()
		If ((Me.PropertyChangingEvent Is Nothing)  _
					= false) Then
			RaiseEvent PropertyChanging(Me, emptyChangingEventArgs)
		End If
	End Sub
	
	Protected Overridable Sub SendPropertyChanged(ByVal propertyName As [String])
		If ((Me.PropertyChangedEvent Is Nothing)  _
					= false) Then
			RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
		End If
	End Sub
	
	Private Sub attach_Orders(ByVal entity As [Order])
		Me.SendPropertyChanging
		entity.Occasion = Me
	End Sub
	
	Private Sub detach_Orders(ByVal entity As [Order])
		Me.SendPropertyChanging
		entity.Occasion = Nothing
	End Sub
	
	Private Sub Initialize()
		Me._Orders = New EntitySet(Of [Order])(AddressOf Me.attach_Orders, AddressOf Me.detach_Orders)
		OnCreated
	End Sub
	
	<OnDeserializing(),  _
	 System.ComponentModel.EditorBrowsableAttribute(EditorBrowsableState.Never)>  _
	Public Sub OnDeserializing(ByVal context As StreamingContext)
		Me.Initialize
	End Sub
	
	<OnSerializing(),  _
	 System.ComponentModel.EditorBrowsableAttribute(EditorBrowsableState.Never)>  _
	Public Sub OnSerializing(ByVal context As StreamingContext)
		Me.serializing = true
	End Sub
	
	<OnSerialized(),  _
	 System.ComponentModel.EditorBrowsableAttribute(EditorBrowsableState.Never)>  _
	Public Sub OnSerialized(ByVal context As StreamingContext)
		Me.serializing = false
	End Sub
End Class

Open in new window

Did you try to test it on new workflow instance or an existing workflow instance, maybe problem is on an existing workflow instance
Each time I test, I use F5 to "Start Debugging".  The error each time displays a new GUID, which implies that the Workflow instance is both new, and unique.  And the Failed delivery of the event to the workflow, terminates the instance that is running.

So I'm assuming that the workflow is new each time, but I can be assuaged otherwise if you think I should do something else.
ASKER CERTIFIED SOLUTION
Avatar of Reza Rad
Reza Rad
Flag of New Zealand image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Ok, I'm trying the attached.

Got the same exception, but interestingly enough, the last thing that happened before the exception was:

      <OnSerializing(),  _
       System.ComponentModel.EditorBrowsableAttribute(EditorBrowsableState.Never)>  _
      Public Sub OnSerializing(ByVal context As StreamingContext)
            Me.serializing = true
      End Sub

When I pressed "Step" (F11) after the End Sub statement, the exception was thrown.


        public void OnOccasionReceived(Occasion occasion, Guid guid)
        {
            OccasionReceivedEventArgs args = new OccasionReceivedEventArgs(guid, occasion);
            if (OccasionReceived != null)
            {
                OccasionReceived(null, args);
            }
        }

        //public void OnOccasionReceived(OccasionReceivedEventArgs args)
        //{
        //    if (OccasionReceived != null)
        //    {
        //        OccasionReceived(null, args);
        //    }
        //}


and

        private void btnOccasion_Click(object sender, EventArgs e)
        {
            Button x = (Button)sender;
            Occasion y = (Occasion)x.Tag;
            Standards.at_Occasion = pos_N3.Occasion.Clone(y);
            if (Standards.at_Occasion != null)
            {
                OccasionReceivedEventArgs args = new OccasionReceivedEventArgs(_instanceId, Standards.at_Occasion);
                args.WaitForIdle = true;
                try
                {
                    _orderService.OnOccasionReceived(Standards.at_Occasion, _instanceId);
                    //_orderService.OnOccasionReceived(args);
                }
                catch (Exception exception)
                {
                    String jjwMsg = exception.Message;
                    MessageBox.Show(exception.Message, "Sending Occasion Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }

Open in new window

While that didn't exactly work, I did conjure up a workaround.

Deep in the trenches of my Database Tier, I built a function to take an Occasion as an argument, and return a Dictionary of it.  I also built the reverse function at the same time.

Then in my OccasionReceivedEventArgs, instead of passing the Occasion Class instance, I passed a dictionary of it, as in
        public void OnOccasionReceived(Occasion occasion, Guid guid)
        {
            Dictionary occasionDictionary = new Dictionary(pos_Db.dB.occasionToDictionary(occasion));
            OccasionReceivedEventArgs args = new OccasionReceivedEventArgs(guid, occasionDictionary);
            if (OccasionReceived != null)
            {
                OccasionReceived(null, args);
            }
        }

then in the Workflow, I set the Dependency Property in the reverse direction, as in:
        private void OccasionReceived_Invoked(object sender, ExternalDataEventArgs e)
        {
            OccasionReceivedEventArgs eventArgs = e as OccasionReceivedEventArgs;
            if (eventArgs != null)
            {
                OrderOccasion = dB.dictionaryToOccasion(eventArgs.PosDictionary);
                if (OrderOccasion.RequiresPhone)
                {
                }
            }
        }


These changes, appear to make the Class Instance really serializable.

This worked.

You get the points because you pointed us to the root problem, that of non-serialized, and your efforts.

You know, the book never really touched on how to pass a non-trivial variable back and forth between a Host and a Workflow.

Anyway, thanks again
Glad to solve,
Thanks for sharing results, this is helpful
and thanks for points
regards,