Second Stanza

July 25, 2010

MVVM – MEF – Reactive Data Grid – Part III

Filed under: .NET Development, MEF, MVVM, Rx Extensions, Silverlight — Tags: — dfbaskin @ 12:18 am

There are a number of ways to retrieve data from a web service. Silverlight provides this functionality through:

The Reactive Extensions for .NET (Rx) builds upon this by providing functionality that allows for composing asynchronous and event-based applications. The MVVMGrid application uses the Rx extensions for the grid services and in certain event handling. I’ll focus on the grid services first.

An application service makes a call out to a web service and provides a response to the application. It must also provide some mechanism to notify the application that a service has failed for some reason. In the MVVMGrid application, the GridService class exposes its methods using IObservable collections. So instead of using an event or an asynchronous interface, a client to the service simply calls the Subscribe method to obtain the response from the service or a failure status.

Let’s examine the grid data service. The grid’s data is generated randomly by the application, but could have easily been retrieved from a web service.

        public IObservable<GridDataCollection> GetGridItems()
        {
            return
                Observable
                    .Repeat( 0, 1 )
                    .Select( _ =>
                    {
                        GridDataCollection gridDataCollection = new GridDataCollection();
                        gridDataCollection.AddRandomData( 100 );
                        return gridDataCollection;
                    } )
                    .SubscribeOn( Scheduler.ThreadPool );
        }

Here

  • The integer 0 is repeated once in order to get an IObservable collection started.
  • A GridDataCollection object is created, 100 randomly-generated GridData items are inserted into it, and then it is selected.
  • The subscription is moved to a worker thread so that the generation of these elements does not occur on the UI thread (which could make the Silverlight application less responsive).

In the GridViewModel, the OnImportsSatisfied method is used to initiate the process of obtaining data from the services.

        private void SetExceptionMessage( Exception ex )
        {
            ErrorMessageOne = String.Concat( "Error: ", ex.GetType().FullName );
            ErrorMessageTwo = ex.Message;
        }

        public void OnImportsSatisfied()
        {
            ...

            Service
                .GetGridItems()
                .ObserveOnDispatcher()
                .Subscribe( d =>
                {
                    DataItems = new ObservableCollection<GridData>( d );
                },
                SetExceptionMessage );
        }

The GetGridItems method of the service object returns an IObservable collection.  Before subscribing, I ensure that the subscription handlers will be invoked on the UI thread (since I will be changing user interface elements). Finally, I subscribe to the IObservable interface and provide it two of the three possible handlers for handling the response: the OnNext handler and the OnError handler (the OnCompleted handler is not required in this case). On a successful response, the DataItems property on the GridViewModel is populated with the items to display. On an error response, I simply set the error message properties which will, in turn, be displayed below the grid.

Now let’s look at a more complicated example.

The “views” for the data grid are defined in XML files located on the web server. The GridService class is used to fetch both the list of all views as well as the individual views.

        private const string ListFileName = "/Data/Settings.xml";
        private readonly Uri WebSiteUri = HtmlPage.Document.DocumentUri;

        public IObservable<ViewSettingsCollection> GetViewSettingsCollection()
        {
            Uri listFileUri = new Uri( WebSiteUri, ListFileName );
            WebRequest httpReq = HttpWebRequest.Create( listFileUri );
            return
                Observable
                    .FromAsyncPattern<WebResponse>( httpReq.BeginGetResponse, httpReq.EndGetResponse )()
                    .Select( r =>
                    {

                        XDocument rspDoc;
                        using( Stream rspStream = r.GetResponseStream() )
                            rspDoc = XDocument.Load( rspStream );

                        return
                            rspDoc
                                .Element( "settings" )
                                .Elements( "items" )
                                .Select( items =>
                                    new ViewSettingsCollection {
                                        DefaultViewSettingsID = (int?)items.Attribute( "default" ),
                                        Items = new List<ViewSettingsCollectionItem>(
                                            items
                                                .Elements( "item" )
                                                .Select( item => new ViewSettingsCollectionItem {
                                                    ID = (int)item.Attribute( "id" ),
                                                    Description = (string)item.Attribute( "desc" ),
                                                    Link = (string)item.Attribute( "link" )
                                                })
                                        )
                                })
                                .Single();

                    } );
        }

An HttpWebRequest object is used to fetch the XML file from the Data folder on the web server. The FromAsyncPattern method is used to wrap the BeginGetResponse and EndGetResponse methods of the request object. When the response is received, LINQ queries are used to create a new ViewSettingsCollection object. The new instance of the ViewSettingsCollection object is then selected for the observable collection.

Back in the GridViewModel, the service is called to fetch the ViewSettingsCollection object.

            Service
                .GetViewSettingsCollection()
                .ObserveOnDispatcher()
                .Subscribe( vsc =>
                {
                    SettingsCollection = vsc;
                    SelectedSettings = SettingsCollection.GetDefaultSettings();
                },
                SetExceptionMessage );

Again, the subscription handlers will be invoked on the UI thread and the OnNext handler will simply set properties on the GridViewModel. Notice that the SelectedSettings property is also set. When this property is changed, either here or when the user changes the selected view in the drop-down box, the setter for this property calls the grid service to get the details of the view.

        public ViewSettingsCollectionItem SelectedSettings
        {
            get { return selectedSettings; }
            set
            {
                selectedSettings = value;
                OnSelectedSettingsChanged();
                OnPropertyChanged( "SelectedSettings" );
            }
        }

        ...

        private void OnSelectedSettingsChanged()
        {
            if( SelectedSettings != null )
            {
                Service
                    .GetViewSettings( SelectedSettings )
                    .ObserveOnDispatcher()
                    .Subscribe( vs =>
                    {
                        ViewSettings = vs;
                    },
                    SetExceptionMessage );
            }
        }

This allows us to set up the columns to display in the data grid.  I’ll examine this issue next.

Advertisement

July 24, 2010

MVVM – MEF – Reactive Data Grid – Part II

Filed under: .NET Development, MEF, MVVM, Rx Extensions, Silverlight — Tags: — dfbaskin @ 11:12 pm

The MVVMGrid application is architected using the Model-View-ViewModel (MVVM) pattern. So here are the parts:

  • GridView.xaml – this is the view containing the data grid. Visual elements are bound to data contained within the GridViewModel. The GridView knows about the properties that the GridViewModel exposes.
  • GridViewModel – this is the view model which is used as the DataContext of the GridView. The GridViewModel knows nothing about the GridView class.
  • GridData, ViewSettings, etc. – these are classes that define the data model that the application uses.
  • GridService – this is the class that provides the services to fetch the grid data and the view information.

We use the Managed Extensibility Framework (MEF) to stitch these pieces together. First, we need to create a CompositionContainer to handle the imports and exports of the various components. In this application, we only need to look in the one assembly for components, so only a single AssemblyCatalog is needed.

    public partial class App : Application
    {
        private CompositionContainer partsContainer;

        [Import]
        public UserControl MainView { get; set; }

        public App()
        {
            this.Startup += this.Application_Startup;
            this.Exit += this.Application_Exit;
            this.UnhandledException += this.Application_UnhandledException;

            InitializeComponent();
        }

        private void Application_Startup( object sender, StartupEventArgs e )
        {
            partsContainer = new CompositionContainer( new AssemblyCatalog( typeof( App ).Assembly ) );
            partsContainer.SatisfyImportsOnce( this );

            RootVisual = MainView;
        }

Normally in Silverlight, we would set our RootVisual to a new UserControl we create in the application startup. Here, we allow MEF to find our root visual for us. How does it know which UserControl to create? The App class defines an Import attribute with a type of UserControl. The SatisfyImportsOnce method goes into the catalog we’ve supplied to it and finds a UserControl that is marked for export. Our GridView control is marked with the Export attribute and thus MEF is able to find it.

    [Export(typeof(UserControl))]
    public partial class GridView : UserControl
    {
        [Import]
        public GridViewModel ViewModel
        {
            get { return DataContext as GridViewModel; }
            set { DataContext = value; OnViewModelChanged(); }
        }


Similarly, our GridViewModel is imported into our GridView, setting it as the DataContext for the view and thus enabling the data binding to work. The GridViewModel then imports the GridService.

    [Export]
    public class GridViewModel : INotifyPropertyChanged, IPartImportsSatisfiedNotification
    {
        [Import]
        public GridService Service { get; set; }

Notice that the GridViewModel implements a couple of important interfaces, INotifyPropertyChanged and IPartImportsSatisfiedNotification. The first interface is used in data binding.  When properties on the GridViewModel change, the PropertyChanged event is fired. This allows data bindings to know that the data that is bound has changed and the user interface element needs to be updated.

The second interface is related to MEF. If an exported component implements this interface, then MEF will call the OnImportsSatisfied method on the object when all of the imports for the object have been satisified and the object is safe to use. In our application, we use this method to begin retrieving the data from our services that will be used in the data grid.

The source code for this project can be downloaded from CodePlex.

MVVM – MEF – Reactive Data Grid – Part I

Filed under: .NET Development, MEF, MVVM, Rx Extensions, Silverlight — Tags: — dfbaskin @ 10:27 pm

I want to explore some of the issues with MVVM, MEF, and Reactive Extensions in developing .NET applications. I’ve created an example Silverlight application that employs these technologies in some interesting ways. I’ll use this example as the basis for our discussion.

First, an overview.

The application displays a simple data grid. The grid itself has a defined set of columns that it can display (bound to the data elements that it knows the data source contains). The actual columns that are displayed and the order of these columns can be modified by selecting a different “view” for the data grid. The user selects the view through a drop-down box above the grid.

MVVMGrid

For this example application, the data is built using random values for few data types: strings, boolean, and DateTime. The views are requested from the server and update the visibility, display order, and width of the grid columns. Even though actual web services are not used, this application provides a simulated service interface to obtain these data elements.

This project was built using Visual Studio 2010. The source code can be downloaded from CodePlex.

Here are the links to the posts in this series:

June 19, 2010

Sequential Asynchronous Workflows in Silverlight Using Rx Framework

Filed under: .NET Development, Rx Extensions, Silverlight — Tags: , — dfbaskin @ 4:44 pm

Jeremy Likness has an excellent post called Sequential Asynchronous Workflows in Silverlight using Coroutines. It seems like this example would also work well using the Reactive (Rx) Extensions Framework.

Here’s the source for an Rx handler of the RandomNumberService:


        /// <summary>
        ///     Once we have the references, start the workflow (the Reactive Way!)
        /// </summary>
        public void OnImportsSatisfied()
        {
            int alpha = 0, red = 0, green = 0, blue = 0;

            int[] maxValuesRequired = new int[]{ 128, 255, 255, 255 };
            var colorWorkflow =
                Observable
                    .ToObservable( maxValuesRequired )
                    .SelectMany( m =>
                        Observable
                            .FromAsyncPattern<int, int>(
                                ( maxValue, callback, state ) => {
                                    GetRandomNumberResult asyncResult = new GetRandomNumberResult();
                                    Service.GetRandomNumber( maxValue, v =>
                                    {
                                        asyncResult.SetResult( v );
                                        callback( asyncResult );
                                    } );
                                    return asyncResult;
                                },
                                ( asyncResult ) => {
                                    return (int)asyncResult.AsyncState;
                                }
                            )( m )
                    )
                    .ObserveOnDispatcher();
            
            int colorIndex = 0;
            colorWorkflow
                .Subscribe( result =>
                {
                    switch( colorIndex++ )
                    {
                    case 0:
                        alpha = result + 128;
                        break;
                    case 1:
                        red = result;
                        break;
                    case 2:
                        green = result;
                        break;
                    case 3:
                        blue = result;
                        break;
                    }
                },
                ex =>
                {
                },
                () =>
                {
                    RectangleFill = new SolidColorBrush(
                        new Color
                        {
                            A = (byte)alpha,
                            B = (byte)blue,
                            G = (byte)green,
                            R = (byte)red
                        } );
                } );
        }

I am wrapping the service call so that it follows the asynchronous service pattern, but this is the more common method of acessing services in a Silverlight application anyway.

Updated July 24, 2010:

I corrected the code by moving the setting of the RectangleFill property to the subscription’s completed method rather than in the Finally method. The latter is more designed for cleaning up after the subscription is completed.

August 1, 2009

MissingMethodException and WaitOne

Filed under: .NET Development — dfbaskin @ 12:59 pm

I ran into the same problem as Mike Plate describes here. Fortunately, there is a now separate download for the .NET 2.0 Service Pack 2 here. Installing this service pack fixed the problem for me.

Thanks Mike!

May 2, 2009

Second Stanza Source Code Repository

Filed under: .NET Development, Silverlight — dfbaskin @ 11:46 am

I’ve created a project on CodePlex to store the source code I’ve publish within this blog. The project is located at secondstanza.codeplex.com. The first set of source code is a set of useful Silverlight controls. Enjoy!

February 26, 2009

SetPrincipalPolicy in Unit Tests

Filed under: .NET Development, Testing — Tags: — dfbaskin @ 9:42 pm

A while back, I noticed that this test always fails in Visual Studio 2008 w/SP1:


[TestClass]
public static class TestInfo
{
    [AssemblyInitialize]
    public static void InitializeTesting( TestContext testContext )
    {
        AppDomain.CurrentDomain.SetPrincipalPolicy(
             System.Security.Principal.PrincipalPolicy.WindowsPrincipal );
        Assert.IsInstanceOfType(
             System.Threading.Thread.CurrentPrincipal,
             typeof(System.Security.Principal.WindowsPrincipal) );
    }
}

According to the documentation:

Setting this value will only be effective if you set it before using the Thread.CurrentPrincipal property. For example, if you set Thread.CurrentPrincipal to a given principal (for example, a generic principal) and then use the SetPrincipalPolicy method to set the PrincipalPolicy to WindowsPrincipal, the current principal will remain the generic principal.

So it’s apparent that MS Test is doing just that and causing the test to fail. This was confirmed in the Microsoft Connect item I posted about it:

This is by design. We noticed this when doing testing of the fix for SP1 and made a trade off. The tradeoff is a performance hit that we would take each time we invoke a method in the test assembly verses using a simple workaround. The workaround is to explicitly set the “System.Threading.Thread.CurrentPrincipal”.

We use the Thread.CurrentPrincipal policy prior to invoking any test method to make a backup of its value. We do this so we can restore it to the original policy after the test finishes executing. This was done to solve the issues with custom principals being serialized back into the main AppDomain and failing to deseralize.

Thanks
Visual Studio Product Team

At least the workaround is simple enough.

February 23, 2009

Conflict Between TypeMock and Reporting

Filed under: .NET Development — dfbaskin @ 1:47 pm

When using a LocalReport object within a unit test where TypeMock Isolator was running, I got the error:

Microsoft.Reporting.WinForms.LocalProcessingException : An error occurred during local report processing.
  ----> Microsoft.ReportingServices.ReportProcessing.ReportProcessingException :
Failed to load expression host assembly.
Details: Operation could destabilize the runtime.

If TypeMock Isolator was disabled, the test ran correctly. This particular test was not using TypeMock calls at all. It was just the presence of the TypeMock Isolator engine that caused the error. I assumed that there was some conflict between TypeMock Isolator and the report processing engine.

This post, by Mohamed Sharaf, provided some clues as to what was causing the error. In the end, using the ExecuteReportInCurrentAppDomain method allowed the test to run correctly.

December 27, 2008

Unit Testing Workflow Activities

Filed under: .NET Development, WorkFlow — Tags: , — dfbaskin @ 10:30 pm

I’ve been using a technique similar to the code below to unit test custom workflow activities.


using System;
using System.Collections.Generic;
using System.Workflow.Runtime;
using System.Workflow.ComponentModel;
using System.Workflow.Runtime.Hosting;
using System.Workflow.Runtime.Tracking;
using System.Configuration;
using System.Threading;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace TrackingError
{
    public class TestActivity : Activity
    {
        public static readonly DependencyProperty MessageProperty = DependencyProperty.Register( "Message", typeof( String ), typeof( TestActivity ) );

        public string Message
        {
            get { return (String)GetValue( MessageProperty ); }
            set { SetValue( MessageProperty, value ); }
        }

        protected override ActivityExecutionStatus Execute( ActivityExecutionContext executionContext )
        {
            Message = "Success";
            return ActivityExecutionStatus.Closed;
        }
    }

    [TestClass]
    public class ShowTrackingError
    {
        public ShowTrackingError()
        {
        }

        private TestContext testContextInstance;

        public TestContext TestContext
        {
            get { return testContextInstance; }
            set { testContextInstance = value; }
        }

        private static string ConnectionString
        {
            get { return ConfigurationManager.ConnectionStrings["WorkFlowDB"].ConnectionString; }
        }

        private WorkflowRuntime workflowRuntime;
        private AutoResetEvent resetEvent;
        private Dictionary<String, Object> outputArgs;
        private bool completedWorkflow;

        [TestInitialize]
        public void CreateWorkFlowEnvironment()
        {
            bool startedWorkflow = false;
            try
            {
                resetEvent = new AutoResetEvent( false );
                outputArgs = null;
                completedWorkflow = false;

                workflowRuntime = new WorkflowRuntime();

                SharedConnectionWorkflowCommitWorkBatchService sharedConnService = new SharedConnectionWorkflowCommitWorkBatchService( ConnectionString );
                workflowRuntime.AddService( sharedConnService );

                SqlWorkflowPersistenceService persistService = new SqlWorkflowPersistenceService( ConnectionString );
                workflowRuntime.AddService( persistService );

                SqlTrackingService trackingService = new SqlTrackingService( ConnectionString );
                workflowRuntime.AddService( trackingService );

                workflowRuntime.WorkflowCompleted += delegate( object sender, WorkflowCompletedEventArgs e ) {
                    outputArgs = e.OutputParameters;
                    completedWorkflow = true;
                    resetEvent.Set();
                };
                workflowRuntime.WorkflowTerminated += delegate( object sender, WorkflowTerminatedEventArgs e ) {
                    resetEvent.Set();
                };
                workflowRuntime.WorkflowAborted += delegate( object sender, WorkflowEventArgs e ) {
                    resetEvent.Set();
                };

                workflowRuntime.StartRuntime();
                startedWorkflow = true;
            }
            finally
            {
                if( !startedWorkflow )
                    if( workflowRuntime != null )
                    {
                        workflowRuntime.Dispose();
                        workflowRuntime = null;
                    }
            }
        }

        [TestCleanup]
        public void ShutDownWorkFlowEnvironment()
        {
            if( workflowRuntime != null )
            {
                try
                {
                    workflowRuntime.StopRuntime();
                    workflowRuntime.Dispose();
                }
                finally
                {
                    workflowRuntime = null;
                }
            }
        }

        [TestMethod]
        public void UnitTestWorkflowActivity()
        {
            Assert.IsNotNull( workflowRuntime );

            WorkflowInstance wfInst = workflowRuntime.CreateWorkflow( typeof( TestActivity ) );
            Assert.IsNotNull( wfInst );

            wfInst.Start();

            resetEvent.WaitOne( 5000 );

            Assert.IsTrue( completedWorkflow );
            Assert.IsNotNull( outputArgs );
            Assert.AreEqual( "Success", (String)outputArgs["Message"] );
        }
    }
}

(Note that common code like the methods marked with the TestInitialize and TestCleanup attributes are actually in a shared based class, but included in the test class here for our discussion.)

However, the above unit test does not work. The activity is aborted so the completedWorkflow value is never set to true. This behavior is apparent both in this unit test as well as in an application that runs the single activity. Perhaps I’ve misunderstood something, but I believe this should work. The activity should not abort.

It appears that the SqlTrackingService class expects a CompositeActivity, not a Activity-derived class since the error written to the log is:

System.Workflow.Runtime.Hosting Error: 0 : SharedConnectionWorkflowCommitWorkBatchService caught
      exception from commitWorkBatchCallback: System.InvalidCastException: Unable to cast object
      of type 'TrackingError.TestActivity' to type 'System.Workflow.ComponentModel.CompositeActivity'.
   at System.Workflow.Runtime.Tracking.SqlTrackingService.SqlTrackingChannel.InsertWorkflow(DbCommand command, Guid workflowInstanceId, Type workflowType, Activity rootActivity)
   at System.Workflow.Runtime.Tracking.SqlTrackingService.SqlTrackingChannel.ExecuteInsertWorkflowInstance(DbCommand command)
   at System.Workflow.Runtime.Tracking.SqlTrackingService.SqlTrackingChannel.Commit(Transaction transaction, ICollection items)
   at System.Workflow.Runtime.WorkBatch.PendingWorkCollection.Commit(Transaction transaction)
   at System.Workflow.Runtime.WorkBatch.Commit(Transaction transaction)
   at System.Workflow.Runtime.VolatileResourceManager.Commit()
   at System.Workflow.Runtime.WorkflowExecutor.DoResourceManagerCommit()
   at System.Workflow.Runtime.Hosting.SharedConnectionWorkflowCommitWorkBatchService.CommitWorkBatch(CommitWorkBatchCallback commitWorkBatchCallback)

Several work-arounds make this unit test pass:

  • Instead of derving the custom activity from the Activity class, it can be derived from CompositeActivity.
  • The activity can be wrapped in some other activity, such as the TransactionScopeActivity. However, the properties used to test the results of the workflow must be propagated out to the wrapping activity.
  • The SqlTrackingService workflow runtime service can be removed.

I’ve opened a Microsoft Connect feedback item here. A Microsoft forums post is here.

November 12, 2008

Windows WorkFlow Activity Data

Filed under: .NET Development, WorkFlow — Tags: — dfbaskin @ 12:54 am

One of the first questions I had about Windows Workflow (in .NET 3.0) was how data was moved around between workflow activities. Activities, of course, are the meat of a workflow process in that they define the actual work a workflow will address. So if one activity picks up a useful data value that some other activity will later need, what technique should be used to pass this data between these activities?

Windows Workflow Foundation provides several features that aid in accomplishing this task:

  • Activity Properties – each activity may publish it’s own bindable set of properties that can be used as input and output parameters for the activity.
  • Workflow Properties – the workflow itself can publish it’s own set of properties.
  • Declarative Binding – using dependency properties, properties from one activity can be automatically bound to properties on other activities.
  • Workflow Navigation – From within an activity, you can navigate the hierarchy of a workflow process in order to find specific instances of other activities that may have information you need.

These features give you multiple options for moving activity data around within a workflow. However, you must be careful in your implementation since, for example, workflows allow parallel processing and you must be sure to get to the activity instance that you really want. MSDN published a very useful article about these and other related issues here.

To me, the best option is to use declarative binding, for two reasons. The first reason is that declarative binding eliminates any ambiguity about which activity instance you are binding to.

The second reason is to abstract your activity objects from your workflow. When navigating the hierarchy of the workflow, you are making assumptions about how the workflow is structured. Using bindings on properties allows the activity to be more stand-alone and more likely to be reusable. And of course, it allows easier unit testing of the activity.

Older Posts »

Blog at WordPress.com.