TheChaseMan's Frenetic SoapBox

Always looking for better ways to do things...

Going for the CTP Trifecta: Workflow, WPF, and DLINQ

I went for the trifecta and died on the finish line.  :-(   I started out by creating WinFX Windows application, and I must say that having a visual designer for XAML is pretty cool. The designer view and the underlying XAML markup looks like this:

<Window x:Class="WindowsApplication1.Window1"

    xmlns="http://schemas.microsoft.com/winfx/avalon/2005"

    xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"

    Title="WindowsApplication1" Closed="Window_Closed"

    >

      <Grid MinHeight="50" MinWidth="50" Name="grid1" >

            <Grid.Resources>

                  <DataTemplate x:Key="CustomerTemplate">

                        <DockPanel>

                              <TextBlock Width="300" Text="{Binding Path=CustomerID}" />

                              <TextBlock Width="300" Text="{Binding Path=CompanyName}" />

                              <TextBlock Width="300" Text="{Binding Path=NumOrders}"/>

                        </DockPanel>

                  </DataTemplate>

            </Grid.Resources>

 

            <ListBox VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Grid.Column="0" Grid.ColumnSpan="1" Grid.Row="0" Grid.RowSpan="1" Margin="0,0,0,68" Width="NaN" Height="NaN" x:Name="listBoxCustomers" />

            <Button VerticalAlignment="Bottom" HorizontalAlignment="Left" Grid.Column="0" Grid.ColumnSpan="1" Grid.Row="0" Grid.RowSpan="1" Margin="11,0,0,6" Width="75" Height="23" x:Name="buttonGetCustomers" Click="buttonGetCustomers_Click">Button</Button>

      </Grid>
</
Window>

In the code behind, I'm able to successfully start up my workflow. The workflow runs a DLINQ query and the problem I'm running into is getting the results back to the client - an exception is thrown saying, "The calling thread may not access this object because the object is owned by a different thread."

namespace WindowsApplication1 {

    public partial class Window1 : Window, IDataReceiver {

        private WorkflowRuntime wr;

 

        public Window1() {

            InitializeComponent();

        }

 

        public void buttonGetCustomers_Click(object sender, EventArgs e) {

            if (wr == null) {

                wr = new WorkflowRuntime();

                wr.StartRuntime();

            }

 

            Dictionary<string, object> parameters = new Dictionary<string, object>();

            parameters.Add("DataReceiver", this);

 

            WorkflowInstance instance = wr.CreateWorkflow(typeof(LINQLibrary1.Workflow1), parameters);

            instance.Start();

        }

 

        public void Process(object data) {

            this.listBoxCustomers.DataContext = data;   //exception!!! "The calling thread may not access this object because the object is owned by a different thread."

        }

 

        public void Window_Closed(object sender, EventArgs e) {

            if (wr != null) {

                if (wr.IsStarted) {

                    wr.StopRuntime();

                }

            }

        }

    }
}

The problem I'm running to may be very simple to get around. Originally I was trying to return the results of the DLINQ query (the var instance) by calling ToList(). After getting the exception, I tried creating a custom class and collection to populate the results, but no dice. This is probably because the workflow code gets upset about the form trying to touch data in its memory space. Here's the Workflow code...

public sealed partial class Workflow1 : SequentialWorkflowActivity {

    private IDataReceiver _dataReceiver;

 

    public Workflow1() {

        InitializeComponent();

    }

 

    public IDataReceiver DataReceiver {

        get { return _dataReceiver; }

        set { _dataReceiver = value; }

    }

 

 

    private void codeActivity1_ExecuteCode(object sender, EventArgs e) {

      using(SqlConnection connection = new SqlConnection("integrated security=sspi;initial catalog=northwind;data source=.\\SQLEXPRESS")) {

        DataContext db = new DataContext(connection);

        connection.Open();

 

        Table<Customer> Customers = db.GetTable<Customer>();

        Table<Order> Orders = db.GetTable<Order>();

 

        var custs =

            from c in Customers

            where c.CompanyName.StartsWith("A")

                select new {

                    c.CustomerID,

                    c.CompanyName,

                    NumOrders = c.Orders.Count

                };

       

        CustomerRecordCollection results = new CustomerRecordCollection();

        foreach(var cust in custs){

            CustomerRecord rec = new CustomerRecord();

            rec.CustomerID = cust.CustomerID;

            rec.CompanyName = cust.CompanyName;

            rec.NumOrders = cust.NumOrders;

            results.Add(rec);

        }

        _dataReceiver.Process(results);

    }
}

This is where I think everything is breaking down...calling the Process() method to pass back the results. In order to do this, when I start the workflow in the form, it is passing a reference to itself as sort of a hack to allow for a "callback." The form implements IDataReceiver which is defined in a common class library along with CustomerRecord and CustomerRecordCollection.

namespace Common {

    public interface IDataReceiver {

        void Process(object data);

    }

 

    public class CustomerRecordCollection : Collection<CustomerRecord> { }

 

    public class CustomerRecord {

        private string _customerId;

        private string _companyName;

        private int _numberOfOrders;

 

        public int NumOrders {

            get { return _numberOfOrders; }

            set { _numberOfOrders = value; }

        }

 

        public string CompanyName {

            get { return _companyName; }

            set { _companyName = value; }

        }

 

        public string CustomerID {

            get { return _customerId; }

            set { _customerId = value; }

        }

    }
}

Drop me a line if you know of a workaround so I can complete the trifecta.  :-)


Digg!

posted on Saturday, February 11, 2006 9:03 PM

Feedback

# re: Going for the CTP Trifecta: Workflow, WPF, and DLINQ 2/18/2006 10:24 PM Sean Chase

UPDATE: I figured out how to fix the threading issue.

http://unboxedsolutions.com/sean/archive/2006/02/18/875.aspx

Hope this helps someone else in the future.
.