Electron ES6 Aurelia MongoDB

November 27, 2015

For a thousand reasons, I wanted to entitle this post, “The Bleeding Edge is Bloody.”

If you are not familiar with Electron, ES6, Aurelia, or MongoDB, this post may not make much sense to you.  However, you can read the post, and head over to the github repro and browse the code.  You’ll see that its clean and simple.  Actually wrote this using the Atom editor on my Mac.  FYI:  Atom is an Electron app.

There are links to resources in the github repro README.md for getting up to speed on these technologies if you have not seen them before or are just getting ramped up.

Introduction

It’s taken me 3 long weeks, giving up weekends and nights to finally figure out how to use Electron, ES6, Aurelia, and MongoDB in a Thick-Client application.  This blog post starts with my painful journey and then dives into how easy the solution really is.  Truest me, its so easy now that I know how to put the pieces together.

Where do I start?  Why did I need to write an Electron app?

I’ve used my Crank Meta-data Code Generation App for years to radically speed up development time building .NET Applications.  It generates all stored procs, class entities, XAML forms, XAML Infragistics Data Grids, SQL Server TVP’s, code snippets, etc..

Now that I’m doing a good bit of development on Mac (yes, Karl is still writing C# on an almost daily basis) I wanted to leverage the same tools I’ve had for years on Windows.

The requirements for my new Crank App are:

  1. Desktop app
  2. Run on Windows
  3. Run on Mac
  4. Run on Linux
  5. Local database that runs on the above platforms. 
  6. Works for a single developer or a team of developers.
  7. Allow team developers to work on a machine of their choosing: Windows, Mac, or Linux.
  8. No investment cost for anything including the data store and this app.
  9. Not a SAS app that requires me to host the application or user data.
  10. No software license fees, keys, etc.  (I didn’t want this headache)
  11. (Fine print: to get some dollars for all my time and other investments, I’ll publish a book and videos on Leanpub. The book is the instruction manual for the application and code generation.)

I chose Electron because in my opinion, it is the best tool for available writing and maintaining a cross-platform apps.  I allows me to use my HTML, CSS, and JavaScript skills I have.  Same code-base for all platforms.

I chose ES6-ES2015 because it is the latest JavaScript standard that enables me to write clean code using the latest features of the language like Promises.

I chose Aurelia because I believe that frameworks should not get in the way of the developer and not require constant ceremony code to leverage their features and capabilities. 

I’ve written production apps using Ionic, AngularJS, and Angular Material.  These apps were all successful, customers happy.  BUT, the development cost and in some cases, the learning curve was high when you didn’t follow the frameworks prescribed happy path.  When I compare the how much less code and ceremony I have to endure then writing an Aurelia apps compared to an AngularJS or AngularJS 2 app, I’m so glad Rob Eisenburg and his team wrote Aurelia.  Is Aurelia perfect? Not yet, but for a Beta 1 product, I’m having zero issues and outstanding support getting help.

I chose MongoDB because it runs on Windows, Mac, and Linux.  It can run on premise or cloud servers.  I need to support a wide range of install scenarios ranging from, single user on Mac or Windows, to large development teams with developers on various platforms.  MongoDB provides this capability. 

Data Store Journey

Initially, I had selected MongoDB but couldn’t get it to work inside an Aurelia app.  I kept getting a missing Kerberos.js file when System.js was trying to load the MongoDB ES6 module.  I spent a lot of time trying to get this to work to no avail.

I then looked at Azure DocumentDB.  I really like DocumentDB.  Simple SQL like syntax for querying the database.  It didn’t have a ES6 module for accessing the database so I wrote my own in ES6 using Promises.  I was ready to go with this but then finally figured out how much it really costs.  $25 per month, per collection.  This was an immediate non-starter for my scenario.  Great service with very cool capabilities, but too costly for this small app.

I then looked at Firebase. Like DocumentDB, Firebase simply rocks.  A 1GB or less production database is only $5 per month. No other cloud service even comes close to this number.  My initial pain points was figuring out how to write ES6 code and leverage Promises.  I wrote myself a ES6 wrapper and got this working, but the cost thing for the user was nagging me.

Giving up my Black Friday day off and motorcycle riding, I when back to give MongoDB one more try.  It was by accident that I finally figured out that System.js module loading was the problem, and not Electron or Aurelia.

Why Not Just Use a Web Service?

Yep, several developers asked me the same question. 

In the current development world, there are many requirements, scenarios and solutions for them.  That are no silver bullets, or one size fits all.  The cloud is not the answer for every application.

I like Thick-Client, two-tier applications and write them when appropriate.

Also, didn’t want to write a webapi or MEAN Stack Express api for this app. I could of and almost did. But, that’s a lot more code I have to write and maintain without any payback whatsoever. 

The Application and Solution

The application is hosted on my github Oceanware account.

I’ve provided an easy to follow readme to get you up and running on Mac or Windows.  I need to spin up a Ubuntu VM for testing on Linux.  Have not had the time yet.  If you’re on Linux, this app will run and build on that platform as well (thanks to Electron and Chromium).

The application is the simplest Electron, Aurelia app I could write. When you run the app, you’ll see a brief spinner and message and then the Home view.

“We are connected” is your success message.  You have connected to you MongoDB server from with an Electron, ES6, Aurelia app.

 screenshot

MongoService

This class was the reason I was struggling for 3 weeks.  Before I found the solution I was using the imports statement to bring in the mongodb module.  You see an example of using imports in the next section below.

This simple starter code is straight from the MongoDB documentation that is written in ES5.

Below I’ve crafted an ES6 class that first brings in mongodb using the familiar node.js required(…) method instead of using imports.

The testConnection method leverages the ES6 Promises, wrapping the MongoClient.connect method. 

IMO: Promises make the method consumer code much easier to read and write, i.e. no callback hell.

 export default class MongoService {  
  constructor() {  
   this.MongoClient = require('mongodb').MongoClient;  
  }  
  testConnection() {  
   return new Promise((resolve, reject) => {  
    this.MongoClient.connect("mongodb://localhost:27017/test", function(err, db) {  
     if(!err) {  
      resolve("We are connected");  
     } else {  
      reject(err);  
     }  
    });  
   });  
  }  
 }  
Home

The below Home class is an example of an Aurelia view model. Except for the inject feature, there is no Aurelia framework goo or ceremony, just ES6.

Modules are imported into a class by using the import statement.  The System.js module loader does all the heavy lifting.  As I’ve stated above, I tried using import and System.js to bring in mongodb, but it always failed.

The constructor takes a single argument that is injected in.  I’m injecting in the above MongoService.

The activate method is one that you would expect any navigation or routing framework to have, but not all do.  This method is invoked by the Aurelia router as part of the routing lifecycle.

You can see my super simple code for calling testConnection.  Once you’ve used Promises, you love and use them.  Clean code.

 import {inject} from 'aurelia-framework';  
 import MongoService from './mongoService'  
 @inject(MongoService)  
 export class Home {  
   constructor(db) {  
    this.db = db;  
    this.title = "Home";  
    this.connectionResults = "";  
   }  
   activate() {  
    this.db.testConnection()  
     .then((promise) => this.connectionResults = promise)  
     .catch((err) => this.connectionResults = err);  
   }  
 }  
MongoDB Node Module

An Electron App is actually simple once you understand the folder structure.  Everything in the root folder belongs to the Electron portion of your application.

Everything in the /src folder belongs to the app that Electron is hosting, in our example, its an Aurelia app.

You’ll quickly notice that there is a package.json file in the root and /src folders.  The one in the /src folder is for application dependencies that can’t be loaded using jspm from the root folder.  MongoDB is the only node module I’ve had to load in this manner.  That’s why I’ve got two package.json files.

screenshotDirectory

Close

I know that I have not done any application how to teaching, or framework explanations.  The real purpose of this blog post was to demonstrate that you can write an Electron, ES6, Aurelia, and MongoDB Thick-Client application.

Have a great day,

Just a grain of sand on the worlds beaches.


Tucking ViewModels Beneath Views in Solution Explorer in Visual Studio 2013

October 30, 2013

VMTuckedAway

Back when I was using Visual Studio 2010 a few weeks ago I had a cool macro that would allow me to select a view and viewmodel file in the Solution Explorer, run the macro and then the viewmodel would be nested nicely under the view as pictured above.

Well… Beginning with Visual Studio 2012 (which I skipped over), Visual Studio no longer support macros.  Kind of a bummer since many developers wrote macros and depended on them for automating tasks, etc.

The good news is, writing a Visual Studio 2013 VSPackage is not really that difficult and the DTE macro code can pretty much be copied and pasted and with a little editing you’re up and running again. With the VSPackage you also get the benefit of not watching that spinning and dancing macro icon that would occasionally appear in the Windows Taskbar which caused your macro to freeze for awhile.

Background

I like to tuck my viewmodels beneath the view.  To me, it makes for nice organization and condenses the number of top level files I have to view in the Solution Explorer.

Most of my viewmodels have a one-to-one relationship with its view.  For those that don’t, I don’t tuck them beneath.

I also like to name my views and viewmodels according to the above pattern. For me, it keeps things clean and consistent.

Introducing Visual Studio 2013 Extension View Model Tuck Away

The below image was captured from the “Extensions and Updates…” dialog after I installed the VSPackage.

ExtensionManager

Operation

The digitally signed package available on the Visual Studio Gallery operates like this:

  • Developer selects a view and viewmodel file in the solution explorer
  • Developer runs the ViewModel Tuck Away command

The VSPackage will verify that:

  • Two files have been selected in the Solution Explorer
  • That one file has a .xaml file extension and contains the word “view”
  • That one file has a .cs file extension and contains the word “viewmodel”

The VSPackage will then tuck the selected viewmodel beneath the selected view.

During the execution of the VSPackage command, the Visual Studio Status Bar text will be updated to reflect the success or failure of the command.

I wrote the VSPackage this way to enforce naming standards at my work and home projects.

I know that some (many) may not like this.  No problem, the source is on GitHub (see Source below) and you can very easily modify it, rebuild the solution and double click on your new VSIX file.

Surfacing the VSPackage Command in Visual Studio 2013

You can surface the VSPackage Command in three ways:

  • Tools Menu, ViewModel Tuck Away
  • Add command to a Toolbar
  • Provide shortcut key for command (not really practical since you used the mouse to select the two files, may as well click a Toolbar icon or text.)

By default the command is added to the Tools Menu as seen below:

ToolsMenu

To add the command to a Toolbar follow the numbers below. #2 below allows you to select the Toolbar you want the command to appear on.  #7 allows you to position the command on the selected Toolbar.

AddCommandToToolBar

I added my command to the Standard Toolbar and moved it to the right side as pictured below.  For me, that little icon will do.

ToolBarSetup

However you may want to see the commands text as pictured below. To do this, use the above “Modify Selection” ComboBox just below #7 above and select “Image and Text”.

ToolBarSetupWithText

What is a VSPackage and How Did You Do This?

You can read all about Visual Studio 2013 VSPackages here. According to this MSDN article:

“VSPackages are software modules that extend the Visual Studio integrated development environment (IDE) by providing UI elements, services, projects, editors, and designers.”

To author your own VSPackage you’ll need to download and install the VS 2013 SDK from here, look towards the bottom of the page in the “Additional Software” section for Visual Studio SDK. The documentation for the SDK is here.

After installing the SDK you’ll get some new templates (very good I might add). To fire up a new VSPackage follow the numbers.

NewVSPackage

I strongly suggest studying the default VSPackage that the template creates.  I adds a Tools menu command that displays a message box.  Simple, but there is a lot of plumbing code it creates for you that you pretty much don’t have to fool with. The VSPackage only has a few files of “plumbing” making it easy to understand how it all fit together.

The below code is the core functionality required to identity two selected items, figure out which is the view and viewmodel and then set the dependency.

Of interest is how much power VSPackages have access to. For example the line of code: var dte = (DTE)GetService(typeof(DTE)); provides access to the DTE.  Most macro programmers are already familiar with the power and features exposed by the DTE. Once your command is invoked, you fire up the DTE and code away.  Not a bad upgrade path from macros or Visual Studio Add-Ins which are depreciated in VS 2013.

In a similar fashion this line of code: var uiShell = (IVsUIShell)GetService(typeof(SVsUIShell)); provides the developer access to the IVsUIShell capabilities.

As I mentioned above, you can use your own custom logic for determining which selected file is the parent and which will be the new child of the parent.  There are many techniques for determining this, like I said I chose to enforce naming standards for my views and viewmodels.

Boolean IsView(String targetFileName) {
    var extension = Path.GetExtension(targetFileName);
    return !String.IsNullOrWhiteSpace(extension) && extension == ".xaml" && 
            targetFileName.IndexOf("view") > -1;
}

Boolean IsViewModel(String targetFileName) {
    var extension = Path.GetExtension(targetFileName);
    return !String.IsNullOrWhiteSpace(extension) && extension == ".cs" && 
            targetFileName.IndexOf("viewmodel") > -1;
}

/// <summary>
/// This function is the callback used to execute a command when the a menu item is clicked.
/// See the Initialize method to see how the menu item is associated to this function using
/// the OleMenuCommandService service and the MenuCommand class.
/// </summary>
void MenuItemCallback(Object sender, EventArgs e) {
    try {
        //get access to the familar DTE object.
        var dte = (DTE)GetService(typeof(DTE));

        if (dte.SelectedItems.Count == 2) {
            String fileNameOne = dte.SelectedItems.Item(1).ProjectItem.FileNames[1].ToLower();
            String fileNameTwo = dte.SelectedItems.Item(2).ProjectItem.FileNames[1].ToLower();
            ProjectItem viewProjectItem;
            ProjectItem viewModelProjectItem;

            //need to figure out which select items are the view and viewmodel
            if (IsView(fileNameOne) && IsViewModel(fileNameTwo)) {
                viewProjectItem = dte.SelectedItems.Item(1).ProjectItem;
                viewModelProjectItem = dte.SelectedItems.Item(2).ProjectItem;
            } else if (IsView(fileNameTwo) && IsViewModel(fileNameOne)) {
                viewProjectItem = dte.SelectedItems.Item(2).ProjectItem;
                viewModelProjectItem = dte.SelectedItems.Item(1).ProjectItem;
            } else {
                dte.StatusBar.Text = "Must select a view.xaml file and a viewmodel.cs " +
                                     "file in the solution explorer.";
                return;
            }

            //line of code that creates the dependency between files.
            viewProjectItem.ProjectItems.AddFromFile(viewModelProjectItem.FileNames[1]);

            //show the tucked into viewmodel
            viewProjectItem.ExpandView();
            dte.StatusBar.Text = "Your viewmodel is now dependent on its view.";
        } else {
            dte.StatusBar.Text = "Must select a view.xaml file and a viewmodel.cs file in " +
                                 "the solution explorer.";
        }
    } catch (Exception ex) {
        var uiShell = (IVsUIShell)GetService(typeof(SVsUIShell));
        var clsId = Guid.Empty;
        Int32 result;
        var message = String.Format("Exception occurred in VmTuckAway: {0}", ex);
        ErrorHandler.ThrowOnFailure(uiShell.ShowMessageBox(
            0,
            ref clsId,
            "VmTuckAway Exception",
            message,
            String.Empty,
            0,
            OLEMSGBUTTON.OLEMSGBUTTON_OK,
            OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST,
            OLEMSGICON.OLEMSGICON_INFO,
            0, // false
            out result));
    }
}

VSIX Manifest Files and VSPackage Assets

If you want to make your own VSPackages you’ll also want to understand the, “source.extension.vsixmanifest” file and how to properly edit it.

You need to read the VSIX Extension Schema 2.0 Reference found here. Very easy read with good examples.

One tip I remembered the hard way was the file property “Include in VSIX” which must be True for VSPackage assets you include in the “source.extension.vsixmanifest.”

These Large and Small icons provide a nice UI for your VSPackage in the “Extensions and updates…” dialog.

IncludeInVSIX

GitHub Source

The source code can be downloaded from GitHub here:

https://github.com/Oceanware/VmTuckAway

Download Signed VSPackage Extension on Visual Studio Gallery

http://visualstudiogallery.msdn.microsoft.com/cbe8ae32-555e-4e10-91b9-ee0d57bab037

Close

Hopes this helps someone and have a great day,

Just a grain of sand on the worlds beaches.


Change and Deleted Item Tracking within the Infragistics DataGrid

July 2, 2012

Now that I’m back developing production Line of Business (LOB) applications again, I wanted to share some techniques and challenges that I’ve overcome in our WPF UI.

Our company uses the Infragistics NetAdvantage for WPF product for our WPF UI. We have been very happy with the product, support response and updates. Currently we use the XamRibbon, and XamOutlookBar in our Prism shell; the XamDataChart in our Dashboard; and XamDataGrid in our data entry forms. Our application is in its infancy and as new requirements, features and capabilities are added we will be using more of the controls in the NetAdvantage for WPF full featured suite.

Introduction

For my first post, I thought I would cover an aspect of using the XamDataGrid (all data grids really) that is foundational to LOB applications.

What I’m referring to is tracking the users edits (inserts, updates and deletes) while working in a data grid and then providing the capability to utilize the same database connection to perform the updates, and optionally perform the updates as an atomic transaction.

There are use cases when a user changes data in a data grid, that those changes are immediately reflected back to the database.

Our scenario is one where the design requires that the user be able to make their edits and then commit all the changes all at once. An example of this would be a master-detail use case, when child rows are associated with a master record, e.g. customers, orders, order detail.

In this example, all other development concerns such as data validation, concurrency, actually reading from or writing to a database are not part of the example code; this enables the reader to focus on one technique for tracking inserts, updates, and deletes.

XamDataGrid Requirement

The Infragistics XamDataGrid requires that the data source implement IBindingList in order for the data grid to support adding rows.

Yes, there are techniques for dynamically adding data to your data source without having to do this, but I wanted to allow the data grid to perform its work in a natural way so our data source is derives from BindingList<T>.

Tracking Inserts, Updates and Deletes

When the user deletes a row in the XamDataGrid, the row is removed from the collection and is not longer visible in the UI.

This is where the below ChangeTrackingBindingList<T> comes into play. This class keeps track of the deleted rows by adding a deleted row to an internal collection and then exposing a method (GetDeletedItems) to return those deleted rows when required. If you look below, you’ll see I’ve overridden the RemoveItem method; the implementation adds the deleted row to the internal collection of deleted rows.

It also exposes a method (GetChangedItems) to return only those non-deleted rows that have been inserted or updated.

namespace GridEditing.Infrastructure {
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Linq;

    /// <summary>
    /// Represents the ChangeTrackingBindingList that tracks changes and deleted items.
    /// </summary>
    /// <typeparam name="T">
    /// T: The type of object to provide change tracking binding list services for.
    /// </typeparam>
    public class ChangeTrackingBindingList<T> : BindingList<T> where T : ITrackDirtyEntity {

        IList<T> _deletedItems = new List<T>();

        /// <summary>
        /// Initializes a new instance of the <see cref="ChangeTrackingBindingList{T}"/> class.
        /// </summary>
        public ChangeTrackingBindingList() {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ChangeTrackingBindingList{T}"/> class.
        /// </summary>
        /// <param name="list">The list.</param>
        public ChangeTrackingBindingList(IList<T> list)
            : base(list) {
        }

        /// <summary>
        /// Gets all items in the collection.
        /// </summary>
        /// <returns>
        ///   <see cref="IEnumerable{T}"/> that contains all items from this collection; 
        ///   includes deleted and non-deleted items.
        /// </returns>
        public IEnumerable<T> GetAllItems() {
            return this.Union(_deletedItems).ToList();
        }

        /// <summary>
        /// Gets items that have been changed in the collection.
        /// </summary>
        /// <returns>
        ///   <see cref="IEnumerable{T}"/> that contains items that have been changed; 
        ///   does not include deleted items.
        /// </returns>
        public IEnumerable<T> GetChangedItems() {
            return this.Where(i => i.IsDirty).ToList();
        }

        /// <summary>
        /// Gets the deleted items.
        /// </summary>
        /// <returns>
        ///   <see cref="IEnumerable{T}"/> that contains all items deleted from this collection.
        /// </returns>
        public IEnumerable<T> GetDeletedItems() {
            return _deletedItems;
        }

        /// <summary>
        /// Clears the items.
        /// </summary>
        protected override void ClearItems() {
            base.ClearItems();
            _deletedItems = new List<T>();
        }

        /// <summary>
        /// Sets the item's MarkedAsDeleted property to <c>true</c>. 
        /// Adds the item to the DeletedItems collection. Removes item from list.
        /// </summary>
        /// <param name="index">The index.</param>
        protected override void RemoveItem(Int32 index) {
            var item = this[index];
            _deletedItems.Add(item);
            base.RemoveItem(index);
        }
    }
}

Consuming the ChangeTrackingBindingList<T>

The below MainWindowViewModel exposes the Customers collection. The collection is initialized and customers inserted in the constructor. (please do not populate your collections in your constructors, this is demo-only code)

You’ll notice that after loading the customers, I loop through the collection and set the IsDirty property to false. Your base class for your entity objects should handle this for you, so that when an object is populated from a database, the object is returned from the service layer in a non-dirty state to provide accurate tracking in the UI layer. The below example is over simplified on purpose to show how the view model will process the data once the user saves their changes.

The most important method below is the SaveExecute method that is invoked when the user clicks the Save button. The CanSaveExecute method determines if the collection has been changed or not. Any change to the collection will cause the Save button to be enabled.

Please Note: the code in the SaveExecute method would 99.9999% of the time actually be executing in a service layer. The service layer method would receive the ChangeTrackBindingList<T> as an argument and would use a data layer to process the changes.

Within the SaveExecute method we can see the workflow:

  • Deleted items are removed from the database.
  • Then the inserted or updated items are committed to the database.
  • The view model would then reload the inserted and changed data into the data grid. This reloading refreshing the timestamps that play the role in concurrency and identity columns are populated after an item is inserted and reloaded from the database.
namespace GridEditing {
  using System;
  using System.Diagnostics;
  using System.Linq;
  using System.Windows.Input;
  using GridEditing.Infrastructure;
  using GridEditing.Model;

  public class MainWindowViewModel : ObservableObject {
    ChangeTrackingBindingList<Customer> _customers;
    ICommand _saveCommand;
    Boolean _customersDirty;

    public ChangeTrackingBindingList<Customer> Customers {
      get { return _customers; }
      set {
        _customers = value;
        RaisePropertyChanged("Customers");
      }
    }

    public ICommand SaveCommand {
      get { return _saveCommand ?? (_saveCommand = new RelayCommand(SaveExecute, CanSaveExecute)); }
    }

    public MainWindowViewModel() {
      // load from the service layer
      var list = new ChangeTrackingBindingList<Customer>();
      list.Add(new Customer { FirstName = "Josh", LastName = "Smith", Id = 1 });
      list.Add(new Customer { FirstName = "Sacha", LastName = "Barber", Id = 2 });
      list.Add(new Customer { FirstName = "Brian", LastName = "Lagunas", Id = 3 });
      list.Add(new Customer { FirstName = "Karl", LastName = "Shifflett", Id = 4 });
      foreach (var customer in list) {
        customer.IsDirty = false;
      }
      this.Customers = list;
      this.Customers.ListChanged += (s, e) => _customersDirty = true;
    }

    Boolean CanSaveExecute() {
      return _customersDirty;
    }

    void SaveExecute() {
      // call the service layer, passing the DeleteMarkingBindingList<Customer>
      // the service layer will loop through each item, and either insert it, update it, delete it, 
      // or discard.
      // this is demo code only, but demonstrates the workflow

      foreach (var customer in this.Customers.GetDeletedItems()) {
        // if the Id property is zero then the service layer does not have to do anything
        // if the Id property is greater than zero, the service layer will delete the item.
        // simulate removing from the data base
        Debug.WriteLine(
          String.Format("Customer deleted: {0} {1}", customer.FirstName, customer.LastName));
      }

      foreach (var customer in this.Customers.GetChangedItems()) {
        if (customer.Id == 0) {
          // perform insert in service layer
          customer.Id = this.Customers.Max(c => c.Id) + 1;
          // simulate inserting into the data base
          Debug.WriteLine(
            String.Format("Customer inserted: {0} {1}", customer.FirstName, customer.LastName));
        } else {
          // perform update in service layer
          // simulate updating the data base
          Debug.WriteLine(
            String.Format("Customer updated: {0} {1}", customer.FirstName, customer.LastName));
        }
      }

      // reload the updated records from the database
      // simulate getting all records from the database

      _customersDirty = false;
    }
  }
}

Running the Application

When the application is initially spun up, the Save button will be disabled since no inserts, updates or deletes have taken place.

DisabledButton

After a row has been inserted, the Save button is enabled.

EnabledButton

To the initial data, the following changes have been made.

ChangesMade

When the Save button is clicked, notice the Debug output window. Each of the requested operations are performed; these operations are easily driven by the ChangeTrackingBindingList<T>.

DebugOutputWindow

After the Save is completed, the UI will be updated as below, the Id field has been populated and the Save button is disabled again.

UpdatedRecords

UI XAML – XamDataGrid Tips

The Infragistics XamDataGrid really makes it easy for developers to deliver a full-featured data grid editing experience. This example does not even scratch the surface of the many capabilities of the data grid. In future posts I’ll demonstrate some real-world scenarios with respect to the XamDataGrid’s UI capabilities, for now, let’s stick with tracking the users edits and updating the database.

Tips

1.  By default the XamDataGrid will set string properties to null if the user enters a blank or empty string in the data grid. This is not the behavior I want, instead I need an empty string. This is accomplished using a no-op converter.

Infragistics has made applying the no-op converter a snap by setting ValueToTextConverter property on the XamTextEditor using a style as I’ve done below. In the XamDataGridNullStringPreventionConverter’s Convert and ConvertBack methods I simply return value. The causes the XamDataGrid to set string properties to an empty string instead of the null value.

2.  Take a look down at the XamDataGrid.FieldSettings, you’ll notice CellClickAction has been set to EnterEditModeIfAllowed. The user no longer has to double click a cell to get into edit mode, simply clicking the cell, will put the cell in edit mode.

3.  Making an Id field read-only is straightforward. Have a look at the Id field in the XamDataGrid’s FieldLayout. By setting Id columns FieldSettings.AllowEdit to false, the cells are read-only.

<Window 
    xmlns:local="clr-namespace:GridEditing"
    xmlns:infrastructure="clr-namespace:GridEditing.Infrastructure"
    x:Class="GridEditing.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Data Grid Editing and Deleting" 
    Height="341" 
    Width="608" 
    ResizeMode="NoResize"
    xmlns:igDP="http://infragistics.com/DataPresenter" 
    xmlns:igEditors="http://infragistics.com/Editors">
  <Window.DataContext>
    <local:MainWindowViewModel />
  </Window.DataContext>
  <Window.Resources>
    <infrastructure:XamDataGridNullStringPreventionConverter 
      x:Key="XamDataGridNullStringPreventionConverter" />
  </Window.Resources>
  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto" />
      <RowDefinition Height="*" />
      <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <TextBlock Text="Infragistics Data Grid Editing and Deleting" Margin="11" FontSize="18" />
    <igDP:XamDataGrid 
      Grid.Row="1" Margin="11" 
      DataSource="{Binding Path=Customers}" IsNestedDataDisplayEnabled="False">
      <igDP:XamDataGrid.Resources>
        <Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type TextBox}}" >
          <Setter Property="Margin" Value="0" />
        </Style>
        <!--this style is required to prevent null strings from being sent to the model-->
        <Style TargetType="{x:Type igEditors:XamTextEditor}" >
          <Setter 
            Property="ValueToTextConverter" 
            Value="{StaticResource XamDataGridNullStringPreventionConverter}" />
        </Style>
      </igDP:XamDataGrid.Resources>
      <igDP:XamDataGrid.FieldSettings>
        <igDP:FieldSettings CellClickAction="EnterEditModeIfAllowed" />
      </igDP:XamDataGrid.FieldSettings>
      <igDP:XamDataGrid.FieldLayoutSettings>
        <igDP:FieldLayoutSettings 
                    SupportDataErrorInfo="None"
                    DataErrorDisplayMode="None" 
                    AutoGenerateFields="False"
                    AddNewRecordLocation="OnBottom" 
                    AllowAddNew="True" 
                    AllowClipboardOperations="All" 
                    AllowDelete="True" 
                    ExpansionIndicatorDisplayMode="Never" />
      </igDP:XamDataGrid.FieldLayoutSettings>
      <igDP:XamDataGrid.FieldLayouts>
        <igDP:FieldLayout>
          <igDP:FieldLayout.SortedFields>
            <igDP:FieldSortDescription FieldName="Id" Direction="Ascending" />
          </igDP:FieldLayout.SortedFields>
          <igDP:Field Name="Id">
            <igDP:Field.Settings>
              <igDP:FieldSettings AllowEdit="False" />
            </igDP:Field.Settings>
          </igDP:Field>
          <igDP:Field Name="FirstName" Width="200" />
          <igDP:Field Name="LastName" Width="200" />
        </igDP:FieldLayout>
      </igDP:XamDataGrid.FieldLayouts>
    </igDP:XamDataGrid>

    <Button 
      Width="65" Margin="7" Grid.Row="2" Content="Save" 
      Command="{Binding Path=SaveCommand}" HorizontalAlignment="Right"/>

  </Grid>
</Window>

Download

After downloading change the extension from .zip.DOC to .zip. This is a requirement of WordPress.

Download Demo Application (25K)

Requirements

To run the demo application, you’ll need to get the Infragistics NetAdvantage for WPF. A demo version is available here.

Close

There are business frameworks like CSLA that have trackable objects and handle the above scenarios I’ve shown. Currently, I prefer to the simplest solution possible and use the above for tracking user changes within the XamDataGrid.

You can see the amount of very simple code to enable this scenario is small, easy to understand and implement.

Have a great day,

Just a grain of sand on the worlds beaches.


Simplifying Prism WPF Navigation – Synchronous Navigation Confirmation

September 5, 2011

Microsoft patterns & practices Prism 4 library and guidance gives WPF and Silverlight developers a very solid foundation for creating business applications. Adopting Prism patterns and library features guides the developer towards creating applications that: can evolve over time, are not tightly coupled, can be independently developed across a large team, are Blendable, and testable.

New to Prism 4 was the Navigation API’s. The use of the Navigation API’s greatly simplifies application development because the Navigation API takes over the responsibility of object creation and siting objects in the target region.

The Prism library is designed to support applications that can target WPF and Silverlight with good code reuse. While not all developers need this capability, a lot of thought and design decisions were made to fully support this scenario.

One API that took this capability into account while being designed is the Navigation API; specifically the confirmation of a navigation request. Objects that implement IConfirmNavigationRequest have the option to veto a navigation request. In order to support the limitation that Silverlight does not allow blocking dialog boxes, the IConfirmNavigationRequest.ConfirmNavigationRequest method had to be written so that Silverlight objects could participate in vetoing a navigation request without a blocking dialog.

Without going into all the details (you can read them here), objects that implement IConfirmNavigationRequest are required to invoke the callback in the ConfirmNavigationRequest method arguments. Objects are free to implement any vetoing code they want as long as the callback is invoked.  In practice the navigation request is halted until the callback is invoked. This design enables Silverlight developers to implement a variety UI solutions for prompting the user, for example the Prism Interaction Request.

I’ve found that I’m my WPF development that I had to jump through the above hoops just to have navigation confirmation, when in fact WPF has out of the box support for modal, UI blocking dialogs. In an effort to simplify my WPF applications I’ve created a replacement for the Prism RegionNavigationService that allows for synchronous navigation confirmation.

The implementation is very straightforward and only requires that you add one class and one interface to the Prism library and recompile it.

The RegionNavigationSynchronousService is the replacement for the stock RegionNavigationService. This class uses the new synchronous (blocking) navigation confirmation interface IConfirmNavigationRequestSynchronous, listed below.

public interface IConfirmNavigationRequestSynchronous : INavigationAware
{
    /// <summary>
    /// Determines whether this instance approves being navigated away from.
    /// </summary>
    /// <param name="navigationContext">The navigation context.</param>
    Boolean ConfirmNavigationRequestSynchronous(NavigationContext navigationContext);
}

By default, Prism automatically registers the RegionNavigationService in the container as part of the bootstrapping pipeline. However, we want to use the WPF friendly synchronous confirmation service, RegionNavigationSynchronousService.

All that is required is to override the ConfigureContainer method in your bootstrapper and register RegionNavigationSynchronousService as I’ve done below.

namespace TestBench {
    using System.Windows;
    using Microsoft.Practices.Prism.Modularity;
    using Microsoft.Practices.Prism.Regions;
    using Microsoft.Practices.Prism.UnityExtensions;
    using Microsoft.Practices.Unity;
    using TestBench.Customers;

    class Bootstrapper : UnityBootstrapper {
        protected override IModuleCatalog CreateModuleCatalog() {
            var catalog = new ModuleCatalog();
            catalog.AddModule(typeof (CustomersModule));
            return catalog;
        }

        protected override DependencyObject CreateShell() {
            var shell = this.Container.Resolve<ShellView>();
            Application.Current.MainWindow = shell;
            Application.Current.MainWindow.Show();
            return shell;
        }

        protected override void ConfigureContainer() {
            base.ConfigureContainer();

            // register the new navigation service that uses synchronous navigation 
            // confirmation instead of the async confirmation.
            this.Container.RegisterType(typeof(IRegionNavigationService), 
                typeof(RegionNavigationSynchronousService));
        }
    }
}

Below is a very simple implementation.

PLEASE do not put MessageBox code in your view models!  This is for demo purposes only and to keep the code simple.  Please use a dialog service that abstracts the UI dialog away from the view model.

The below code is from the included download and is in the CustomerMaintenanceViewModel. The below ConfirmNavigation property allows the demo to confirm or not but is not part of the Navigation API. This method returns true or false to continue the navigation request or not.

public Boolean ConfirmNavigationRequestSynchronous(NavigationContext navigationContext) {
    if (this.ConfirmNavigation) {
        if (MessageBox.Show("Close form and navigate?", "Confirm Navigation", 
            MessageBoxButton.OKCancel, MessageBoxImage.Question) == MessageBoxResult.OK) {
            return true;
        }
        return false;
    }
    return true;
}

Demo Application

SyncDemo

Very simple Prism application that demonstrates confirming navigation requests and region lifetime. The status bar indicates the views in the ContentRegion on the right.  As you open or close views their name will be displayed; for the Customers, the customer number will be displayed.

Crack the code open, you’ll have this down in a few minutes.

Download

As always, don’t forget to “Unblock” the zip file after downloading from the Internet before you unzip it.

Synchronous Prism Navigation (177KB)

The download includes a sample Prism solution that your can run without modifying your Prism library code.  I’ve included a \Lib folder in the solution with pre-built, modified Prism library code.

I’ve also included two files in the \PrismSource folder that you can add to your Prism library. Simply copy these two files into the below folder and recompile Prism. You now have the option to use a simpler navigation confirmation API in your WPF projects. 

This only works in WPF and NOT Silverlight.  If you have the requirement to share code between WPF and Silverlight this will not work because Silverlight requires the navigation confirmation to be async.

SyncNav

Have a great day,

Just a grain of sand on the worlds beaches.


Various Clients and Forms Authentication

April 25, 2011

Conceptual

Scenarios

In each of the below scenarios, forms authentication is used to secure access to the ASP.NET MVC endpoints and WCF services.

  • WPF accessing WCF services
  • WPF accessing MVC endpoints
  • Windows Phone 7 accessing WCF services
  • Desktop and mobile browsers accessing ASP.NET MVC website pages
  • jQuery accessing MVC endpoints

While I have not included the code for a Windows Phone 7 to access MVC endpoints that is a supported scenario as well.

Requirements

  • Visual Studio 2010
  • SQL Express 2008
  • Windows Phone 7 tools
    • If you don’t have these and don’t want to see this project, you can just remove the Windows Phone 7 project from the solution.

Background

I’ve been working on Stuff v2; a movie, game, and books application. Its primary use case is, "I’m at the store and don’t remember if I have a particular movie, game, or book. I need to determine if I have it; if not, then check the online price and ratings before making the purchase."

Given the varied application clients and devices, ASP.NET forms authentication seemed like the natural choice for authentication for the website, MVC3 JSON endpoints, and WCF services.

The reason I have varied client software and devices is more of a learning experience than an application requirement. I have other applications I want to write that will need to access the application from all my devices.

When I started programming the WPF client, I ran into a stone wall with respect to WPF accessing WCF services that are secured by forms authentication. This blog post is about getting over that stone wall.

Identifying the Problem Space

At the end of the day, the problem that needs solving is managing the forms authentication cookie or ticket.

Managing means, that after authenticating, the client must be able to retrieve the ticket returned in the response and include it in future requests to secured resources.

As you will see, client API’s vary across scenarios not only in coding patterns but in complexity as well.

Regardless of which client is accessing resources that require forms authentication, the following steps outline the required workflow:

  • Log in
  • Cache the ticket returned in the response
  • Include the ticket in subsequent requests

Setting up Forms Authentication

When I created the ASP.NET MVC3 application, VariousClients.Web, I used the MVC3 Internet template with the Razor view engine. This template sets up forms authentication and provides a pretty good out-of-box SQL Express membership system for you.

The below snippet from the web.config shows a few required changes:

<authentication mode="Forms">
    <!-- cookieless="UseCookies" is required by non-browser clients
                to authenticate using forms authentication-->
    
    <!-- production applications, change to requiresSSL="true"-->
    <forms timeout="2880" cookieless="UseCookies" loginUrl="~/Account/LogOn"
            requireSSL="false" />
</authentication>

Setting up the AuthenticationService

The System.Web.ApplicationServices.AuthenticationService is a built-in service that you can expose as a service endpoint on your website. This service exposes log in, log out methods for clients that access WCF endpoints requiring forms authentication. This service uses the membership provider defined in the web.config. After logging in, the service returns a ticket in the response, similar to forms authentication log in.

Adding the service is easy. First add a folder to the root of the website named, "Services". Into that folder, add a WCF service named Authentication.svc. Delete the generated service contract and code-behind files. Next replace the contents of the Authentication.scv file with the below code snippet.

<%@ ServiceHost Language="C#" Service="System.Web.ApplicationServices.AuthenticationService" %>

Now add the following to your web.config:

<system.web.extensions>
    <scripting>
        <webServices>
            <!-- for production applications, change to requiresSSL="true"-->
            <authenticationService enabled="true" requireSSL="false"/>
        </webServices>
    </scripting>
</system.web.extensions>

Rebuild your web application.

The Authentication.svc will now appear in the Add Service Reference dialog when adding service references in your client applications.

AddServiceReference

Browsers, Web pages, and MCV3 Controller Methods

Controller classes or controller action methods can be decorated with the Authorize attribute to require that client browsers or JavaScript accessing them be authenticated.

After logging in, the browser automatically manages the authentication ticket and includes it with all future requests to the website.

The below GetTime method requires authentication:

using System;
using System.Web.Mvc;

namespace VariousClients.Web.Controllers {

    public class CloudDataController : Controller {

        [Authorize]
        public JsonResult GetTime() {
            return Json(DateTime.Now.ToLongDateString(), JsonRequestBehavior.AllowGet);
        }
    }
}

WPF and MVC3 Controller Methods

The following section applies equally to WPF, Windows Forms, console, and test projects.

Server-Side

I’ve added a RemoteLogOn method to the AccountController that is used by non-browser clients when logging in. The method signature and implementation is slightly different from the LogOn method.

        [HttpPost]
#if (!DEBUG)
        [RequireHttps]
#endif
        public Boolean RemoteLogOn(LogOnModel model) {
            if(ModelState.IsValid) {
                if(Membership.ValidateUser(model.UserName, model.Password)) {
                    FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
                    return true;
                }
            }
            return false;
        }

CookieAwareWebClient

Remember, the purpose of the above RemoteLogOn is to get the ticket returned in the response. To simplify client programming, a CookieAwareWebClient wrapper can be used to execute WebClient requests while at the same time handling including the ticket in requests.

using System;
using System.Net;

namespace VariousClients.Common.Net {

    public class CookieAwareWebClient : WebClient {

        public CookieContainer CookieContainer { get; private set; }

        public CookieAwareWebClient()
            : this(new CookieContainer()) {
        }

        public CookieAwareWebClient(CookieContainer cookieContainer) {
            if (cookieContainer == null) throw new ArgumentNullException("cookieContainer");
            this.CookieContainer = cookieContainer;
        }

        protected override WebRequest GetWebRequest(Uri address) {
            if(this.CookieContainer == null) {
                throw new InvalidOperationException("CookieContainer is null");
            }
            var request = base.GetWebRequest(address);
            if (request is HttpWebRequest) {
                (request as HttpWebRequest).CookieContainer = this.CookieContainer;
            } return request;
        }
    }
}

Client-Side

void btnMvcLogIn_Click(Object sender, RoutedEventArgs e) {

    _cookieJar = new CookieContainer();
    var client = new CookieAwareWebClient(_cookieJar) { Encoding = Encoding.UTF8 };

    client.UploadValuesCompleted += (s, args) => {
        this.lblMvcResult.Content =
            args.Error == null ? Encoding.UTF8.GetString(args.Result) : args.Error.Message;
    };

    var nvc = new NameValueCollection { { "UserName", Credentials.UserName }, 
                                        { "Password", Credentials.Password }, 
                                        { "RememberMe", "true" } };
    client.UploadValuesAsync(
        new Uri("http://localhost:1668/Account/RemoteLogOn"), "POST", nvc);
}

void btnMvcGetData_Click(Object sender, RoutedEventArgs e) {

    this.lblMvcResult.Content = "calling cloud service...";
    var client = new CookieAwareWebClient(_cookieJar);

    client.DownloadStringCompleted += (s, args) => {
        this.lblMvcResult.Content = args.Error == null ? args.Result : args.Error.Message;
    };

    client.DownloadStringAsync(new Uri("http://localhost:1668/CloudData/GetTime"));
}

In the above code, the _cookieJar is scoped at module level. The important concept is to use the same instance of the CookieAwareWebClient for all calls because it manages the ticket for you. As an alternative, you could retrieve the CookieContainer value after logging in and pass it in the constructor when creating new CookieAwareWebClient instances.

The btnMvcGetData_Click code looks like a typical WebClient call. The CookieAwareWebClient makes calling endpoints secured by forms authentication painless.

WPF and WCF Service Methods

The following section applies equally to WPF, Windows Forms, console, and test projects.

WPF consuming WCF services secured with forms authentication requires a bit of extra code to manage the ticket because WCF does not provide a simple API to add or retrieve it when making service calls that use the generated proxies.

If you contrast the WPF and Windows Phone 7 calls to the same WCF service you’ll see exactly what I mean. The Windows Phone 7 proxy exposes a CookieContainer object making it easy to capture or include the ticket in all service calls.

Before you can call the AuthenticationService, you’ll need to add a service reference to the Authentication service. When adding the service reference, I changed the namespace to AuthenticationService like the below image:

AddServiceReference

FormsAuthenticationAssistant

The FormsAuthenticationAssistant is a façade for WPF clients making WCF service calls secured with forms authentication. It provides automatic ticket management for making service calls.

// Many thanks to Jonas Follesoe for this post:  
// http://jonas.follesoe.no/2008/09/12/wcf-authentication-services-silverlight-and-smelly-cookies/
// I learned how to extract the forms authentication cookie from the AuthenticationService and how to 
// reapply it subsequent service calls.

using System;
using System.Net;
using System.ServiceModel;
using System.ServiceModel.Channels;

namespace VariousClients.Common.ServiceModel {

    public class FormsAuthenticationAssistant {

        public String TicketCookie { get; private set; }

        public FormsAuthenticationAssistant() { }

        public FormsAuthenticationAssistant(String ticket) {
            if (String.IsNullOrWhiteSpace(ticket)) throw new ArgumentNullException("ticket");
            this.TicketCookie = ticket;
        }

        public Boolean Login(Func<Boolean> method, IContextChannel serviceInnerChannel) {
            if (method == null) throw new ArgumentNullException("method");
            if (serviceInnerChannel == null) throw new ArgumentNullException("serviceInnerChannel");

            using (new OperationContextScope(serviceInnerChannel)) {
                if (!method()) {
                    this.TicketCookie = null;
                    return false;
                }
                var properties = OperationContext.Current.IncomingMessageProperties;
                var responseProperty = 
                    (HttpResponseMessageProperty)properties[HttpResponseMessageProperty.Name];
                this.TicketCookie = responseProperty.Headers[HttpResponseHeader.SetCookie];
                return true;
            }
        }

        public T Execute<T>(Func<T> method, IContextChannel serviceInnerChannel) {
            if (method == null) throw new ArgumentNullException("method");
            if (serviceInnerChannel == null) throw new ArgumentNullException("serviceInnerChannel");
            if (String.IsNullOrWhiteSpace(this.TicketCookie)) {
                throw new InvalidOperationException(
                    "Currently not logged in. Must Login before calling this method.");
            }

            using (new OperationContextScope(serviceInnerChannel)) {
                var requestProperty = new HttpRequestMessageProperty();
                OperationContext.Current.OutgoingMessageProperties.Add(
                    HttpRequestMessageProperty.Name, requestProperty);
                requestProperty.Headers.Add(HttpRequestHeader.Cookie, this.TicketCookie);
                return method();
            }
        }

        public void Execute(Action method, IContextChannel serviceInnerChannel) {
            if (method == null) throw new ArgumentNullException("method");
            if (serviceInnerChannel == null) throw new ArgumentNullException("serviceInnerChannel");
            if (String.IsNullOrWhiteSpace(this.TicketCookie)) {
                throw new InvalidOperationException(
                    "Currently not logged in. Must Login before calling this method.");
            }

            using (new OperationContextScope(serviceInnerChannel)) {
                var requestProperty = new HttpRequestMessageProperty();
                OperationContext.Current.OutgoingMessageProperties.Add(
                    HttpRequestMessageProperty.Name, requestProperty);
                requestProperty.Headers.Add(HttpRequestHeader.Cookie, this.TicketCookie);
                method();
            }
        }
    }
}

The above Login method invokes the method passed in the method argument. If the log in is successful, the ticket is extracted from the response and cached in the TicketCookie property and true is returned.

The above two Execute methods invoke the method passed in the method argument and add the TicketCookie to the request.

Your application can interface with the FormsAuthenticationAssistant in one of two ways. One is to create a single instance of the FormsAuthenticationAssistant and use that instance for all service calls. This is how the demo WPF application is written.

Another is to save the value of the TicketCookie property after a successful log in, then create a new instance of FormsAuthenticationAssistant for each call and pass the saved TicketCookie value in the constructor.

Once you’re comfortable with routing your service proxy calls through the FormsAuthenticationAssistant, you’ll notice very little differences between using this façade and calling the service proxy methods directly. Don’t forget, in real-world applications you’ll want to make these service calls or calls to the façade asynchronous so the UI thread is not blocked.

Server-Side

The PrincipalPermission attribute can be used on WCF service methods to restrict access to authenticated users. This is similar to using the Authorize attribute on the MCV3 controller action methods. Follow the three steps below:

using System;
using System.Security.Permissions;
using System.ServiceModel.Activation;
using System.Threading;
using System.Web;

namespace VariousClients.Web.Services {

    //STEP 1 - required for interop with ASP.NET
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
    public class CloudData : ICloudData {

        public CloudData() {
            //STEP 2 - pass caller principal to executing threads principal 
            Thread.CurrentPrincipal = HttpContext.Current.User;
        }

        //STEP 3 - verify caller is authenticated
        [PrincipalPermission(SecurityAction.Demand, Authenticated = true)]
        public String GetTime() {
            return DateTime.Now.ToLongDateString();
        }
    }
}

Client-Side

In the below code, we will first log in against the AuthenticationService, cache the ticket, then call CloudData.GetTime.

void btnServiceLogIn_Click(Object sender, RoutedEventArgs e) {
    this.lblServiceResult.Content = "calling cloud service...";
    var authenticationService = new AuthenticationServiceClient();
    if(_faa.Login(() => authenticationService.Login(
                    Credentials.UserName, Credentials.Password, String.Empty, true),
                    authenticationService.InnerChannel)) {
        this.lblServiceResult.Content = "Log in sucessful.";
    } else {
        this.lblServiceResult.Content = "Bummer, log in not sucessful.";
    }
}

void btnServiceGetData_Click(Object sender, RoutedEventArgs e) {
    this.lblServiceResult.Content = "calling cloud service...";
    var cloudDataService = new CloudDataClient();
    try {
        this.lblServiceResult.Content = 
            _faa.Execute<String>(cloudDataService.GetTime, cloudDataService.InnerChannel);
    } catch(System.ServiceModel.Security.SecurityAccessDeniedException ex) {
        // three conditions could cause this
        //  1. client has never logged in
        //  2. ticket is expired
        //  3. instance of _faa was not the same instance used to log in
        // when this happens in your client code, re-authenticate to get a ticket
        this.lblServiceResult.Content = ex.Message;
    } catch(Exception ex) {
        this.lblServiceResult.Content = ex.Message;
    }
}

This above code is straightforward and very familiar to developers making service calls. The paradigm change is the forwarding of WCF service proxy calls through the FormsAuthenticationAssistant instance (_faa) which manages the ticket.

Windows Phone 7 and WCF Service Methods

Before you can call the AuthenticationService, you’ll need to add a service reference to the Authentication service. When adding the service reference, I changed the namespace to AuthenticationService like the below image:

AddServiceReference

For each service reference you add, you must also edit the ServiceReferences.ClientConfig file binding entry and set the enableHttpCookieContainer property to true as done below.

The Visual Studio tooling for adding service references chokes when the enableHttpCookieContainer property is set on a binding and displays a bizarre error message when adding or updating service references.

So… add all your service references, and then edit the ServiceReferences.ClientConfig file.

If you have to subsequently add, delete or modify a service reference, you’ll need to remove these properties, use the tooling and re-add the properties.

<bindings>
    <basicHttpBinding>
        <binding name="BasicHttpBinding_AuthenticationService" maxBufferSize="2147483647"
            maxReceivedMessageSize="2147483647" enableHttpCookieContainer="true">
            <security mode="None" />
        </binding>
        <binding name="BasicHttpBinding_ICloudData" maxBufferSize="2147483647"
            maxReceivedMessageSize="2147483647" enableHttpCookieContainer="true">
            <security mode="None" />
        </binding>
    </basicHttpBinding>
</bindings>

Calling the AuthenticationService Login method is just like calling other WCF service methods with a minor exception; the CookieContainer property must be set to a new instance of the CookieContainer class prior to making the call. If you forget to populate this property, the ticket will not be returned in the response.

void btnLogin_Click(Object sender, RoutedEventArgs e) {
    this.tbResult.Text = "Attempting to log in...";
    _cookieJar = new CookieContainer();
    var authenticationService = new AuthenticationServiceClient();
    authenticationService.CookieContainer = _cookieJar;
    authenticationService.LoginCompleted += (s, args) => {
        if(args.Error == null) {
            this.tbResult.Text = "Login sucessful.";

            // CookieJar can be persisted without an exception being thrown
            // either when toomstonned or shutdown.  
            // Yes, it can be read from the below stores also.
            // If required for your application, move this code to the appropriate location
            // for launching, closing, activating, and deactivating.
            //
            // uncomment to test
            // PhoneApplicationService.Current.State.Add("CookieJar", _cookieJar);
            // IsolatedStorageSettings.ApplicationSettings.Add("CookieJar", _cookieJar);

        } else {
            this.tbResult.Text = "Login failed: " + args.Error.Message;
        }
    };
    authenticationService.LoginAsync(Credentials.UserName, Credentials.Password, String.Empty, true);
}

In the above code I’ve created a module level variable to hold the CookieContainer (_cookieJar). The same CookieContainer instance must be passed in subsequent calls to other WCF service endpoints.

With Windows Phone 7 programming core concepts like tombstoning and launching must be addressed. In the above code snippet you’ll see a section of comments showing the CookieContainer persisted to storage. Based on your application needs and security requirements, you’ll need to determine when and how to persist your CookieContainer so that your users will not have to repeatedly log in.

void btnGetServiceData_Click(Object sender, RoutedEventArgs e) {
    this.tbResult.Text = "Calling service...";
    var cloudDataService = new CloudDataClient { CookieContainer = _cookieJar };
    cloudDataService.GetTimeCompleted += (s, args) => {
        if(args.Error == null) {
            this.tbResult.Text = args.Result;
        } else {
            if(args.Error is System.ServiceModel.Security.SecurityAccessDeniedException) {
                // three conditions could cause this
                //  1. client has never logged in
                //  2. ticket is expired
                //  3. the _cookieJar instance is not the same instance used when logging in
                // when this happens in your client code, re-authenticate to get a ticket
                this.tbResult.Text = args.Error.Message;
            } else {
                this.tbResult.Text = "Error: " + args.Error.Message;
            }
        }
    };
    cloudDataService.GetTimeAsync();
}

In the above code, the _cookieJar is assigned to the CookieContainer property. Contrast this API with the WPF API for the same call, this API is much simpler. It would be nice to have this exposed in the desktop API’s as well.

Running the Application

  • Set the VariousClients.Web project as the startup project.
  • Run the application (this will create your membership SQL Express database.)
  • When the below page is displayed, click the Register link.

RunningFirstTime

  • When completing the Registration page, you need to use the user name and password specified in the below Credentials class. This class provides the user name and password for the WPF and Windows Phone 7 projects when making service calls.

Credentials

  • After completing a registration page you will be redirected to the page that demonstrates making a jQuery AJAX call to a secured MVC endpoint.
  • To run the WPF and Windows Phone 7 applications, set them as the startup projects.

Download

The demo solution can be downloaded from my Sky Drive:

Various Clients Solution (1.7 MB)

Close

Hope this helps when writing your own desktop, phone and web applications that use forms authentication.

Have a great day,

Just a grain of sand on the worlds beaches.


Removing Repetitive Boiler Maker Code From View Models

November 17, 2010

While I was writing the BBQ Shack, I noticed that view model code calling into the business layer was repetitive across view models. The pattern I used was simple and widely accepted; background worker to make the asynchronous calls to the business layer.

The below code snippet was typical.  Background worker, exception checking, call to business layer, successful result code path, and exception result code path.

Old Pattern

void BackgroundWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) {
    e.Result = _iBLLItemCategory.Select(Convert.ToInt32(e.Argument));
}


void BackgroundWorker_RunWorkerCompleted(object sender, 
    System.ComponentModel.RunWorkerCompletedEventArgs e) {
    if (e.Error == null) {
        if (e.Result != null) {
            this.ic_ItemCategory = (ic_ItemCategory)e.Result;

        } else {
            _iViewModelUIService.MessageDialog("Data Error", 
                "Record Not Found",     
                string.Format("Item Category key: {0} not found", _objLoadKey.ToString));
        }

    } else {
        _objIViewModelUIService.ExceptionDialog(e.Error);
    }
}

I kept thinking about this, always looking for a better pattern.  I also wanted a pattern that only required a single line of code and still gave me all of the same benefits of the above code.

New Pattern

All of the above code condenses nicely down to one line of code that provides all of the above “old pattern” features.

The below repository method signatures describe the pattern.  The pattern for the method signatures is zero or more parameters, followed by the same last two; Action<T> resultCallback and Action<Exception> errorCallback.

public interface IItemRepository {

    void GetAll(Action<IEnumerable<Item>> resultCallback, Action<Exception> errorCallback);

    void Get(Int32 itemId, Action<Item> resultCallback, Action<Exception> errorCallback);

    Item Create();
}

The successful result of the method call is always calls the resultCallback delegate.

The unsuccessful result of the method call is always calls the errorCallback delegate.

This pattern is incredibly simple and leads to much cleaner code in the view model.  The errorCallback is a method in the view model base class.  This method can use a variety of techniques for displaying the error message to the user.

void LoadItems() {
    _itemRepository.GetAll(result => { this.DataItems.Source = result; }, this.DisplayException);
}

Repository Implementation – Bring on the Task Parallel Library (TPL)

The below implementation leverages the TPL Futures pattern.  Use of the TPL is optional, I’m using it here in this WPF application.  The TPL is not available in Silverlight 4 applications yet.  For Silverlight applications, change the implementation to use the common Silverlight asynchronous pattern.

The “pattern” is described in the above IItemRepository interface.  Like all interface contracts, implementation specifics can be platform specific and up to the developer.

Strongly recommend you read this book on MSDN: Parallel Programming with Microsoft .NET.  The book can also be purchased from Amazon here.  There is a book and Kindle version available.

[Export(typeof(IItemRepository))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class ItemRepository : IItemRepository {

    readonly ThePhoneCompanyEntities _dataService;

    [ImportingConstructor]
    public ItemRepository(DataServiceFacade dataServiceFacade) {
        _dataService = dataServiceFacade.DataService;
    }

    void IItemRepository.GetAll(Action<IEnumerable<Item>> resultCallback,
        Action<Exception> errorCallback) {

        // This code can be refactored into a generic method
        // I left it this way to help with the learning process 
        Task<RepositoryResult<IEnumerable<Item>>> task =
            Task.Factory.StartNew(() => {
                try {
                    return new RepositoryResult<IEnumerable<Item>>(
                                _dataService.Items.ToList(), null);
                } catch(Exception ex) {
                    return new RepositoryResult<IEnumerable<Item>>(null, ex);
                }
            });

        task.ContinueWith(r => {
            if(r.Result.Error != null) {
                errorCallback(r.Result.Error);
            } else {
                resultCallback(r.Result.Package);
            }
        }, CancellationToken.None, TaskContinuationOptions.None,
            TaskScheduler.FromCurrentSynchronizationContext());
    }

    void IItemRepository.Get(Int32 itemId, Action<Item> resultCallback, 
        Action<Exception> errorCallback) {

        if(itemId != 0) {

            // This code can be refactored into a generic method
            // I left it this way to help with the learning process 
            Task<RepositoryResult<Item>> task =
                Task.Factory.StartNew(() => {
                    try {
                        return new RepositoryResult<Item>(
                            _dataService.Items.Where(
                                i => i.ItemID == itemId).FirstOrDefault(), null);
                    } catch(Exception ex) {
                        return new RepositoryResult<Item>(null, ex);
                    }
                });

            task.ContinueWith(r => {
                if(r.Result.Error != null) {
                    errorCallback(r.Result.Error);
                } else {
                    resultCallback(r.Result.Package);
                }
            }, CancellationToken.None, TaskContinuationOptions.None,
                TaskScheduler.FromCurrentSynchronizationContext());
        } else {
            resultCallback(((IItemRepository)this).Create());
        }
    }

    // I always create my entity objects in the business layer and never in the presentation layer
    Item IItemRepository.Create() {
        return new Item();
    }
}

Key Points

  • Call that does the actual work is wrapped in a try catch block.
  • The result of the asynchronous operation is wrapped in a RepositoryResult object.
  • The RepositoryResult is used to drive invoking the resultCallback or the errorCallback on the calling thread. The TaskScheduler.FromCurrentSynchronizatikonContext method call in the task.ContinueWith ensures the two callbacks are invoked on the calling thread. 

Download

This code is from one of my presentations at the patterns & practices 2010 Symposium. 

The article and video is here: http://blogs.msdn.com/b/kashiffl/archive/2010/10/21/patterns-and-practices-2010-symposium-content.aspx

This code can be downloaded from the article.

Close

I hope that this short post encourages you to look for possible refactorings in your own code and to take a look at the TPL. 

Have a great day,

Just a grain of sand on the worlds beaches.


In the Box – MVVM Training

November 7, 2010

Updated 12 Nov 2010

InTheBox

What is In the Box?

In the Box is a high quality, multi-media training that is consumed within Visual Studio 2010.  Content is navigated and delivered using a next generation computer based training (CBT) experience, the Visual Studio 2010 Feature Extension.

In the Box, is a brand name for a series of CBT Feature Extensions I’ll release that are listed in the Visual Studio 2010 Add New Project dialog; see below image.  This release is MVVM Training, the next will be Prism Training.

In the Box features:

  • Visual Studio 2010 Feature Extension
  • Content delivered as text, code, images, diagrams, video, or hyperlinks to the Internet
  • Hierarchical navigation tool window for content navigation
  • Content is viewed inside Visual Studio 2010 tool windows
  • No additional downloads or dependencies; all content is in the box.  (except online videos)
  • Installed and updated from the Visual Studio Gallery
  • Managed (disabled or uninstalled) using Visual Studio Extensions Manager (see bottom of this page)
  • Authored using Microsoft Word and the Instant Feature Builder

What is in this release of In the Box?

Please watch this video as it will answer most of your questions.

This installment of In the Box contains in-depth MVVM Training that includes an eleven assembly example solution with projects targeting developers at different levels of experience

Who is the target audience?

  • If you have never used MVVM before, this training is for you. 
  • If you have been using MVVM for a while and want to learn more, this training is for you. 
  • If you are an expert, you will enjoy the MVVM Technical Description and MVVM Scenarios content.

What are the requirements to install In the Box?

Visual Studio 2010 Professional, Premium, or Ultimate.

Expression Blend 4 WPF SDK – Free download (see below comment on SDK) (Note: this is installed with Blend also)

For those developers using Visual Studio 2010 Express or Visual Studio 2008, you can use the links in the below download section to download a .mht version of the content along with the sample solution source.  The .mht file can be viewed in your browser by double clicking the file in Windows Explorer.

Please note: I have not tested this solution with the Express version.

Windows XP and Windows Server 2003 Users

In order to run Feature Builder on either XP or Server 2003 you must create an environment variable named LocalAppData (set by default on Windows Vista and Windows 7).

How to Add LOCALAPPDATA variable in Windows XP:

  • Right-click on the My Computer icon and choose Properties
  • Click Advanced
  • Click Environment Variables
  • Under User variables section, click New
  • In the Variable name: field, type LOCALAPPDATA
  • In the Variable value: field, type %USERPROFILE%\Local Settings\Application Data

How do I install In the Box?

Click on this Visual Studio Gallery Link and install from there.

After I install it, how do I get started?

Open Visual Studio 2010; File, New Project, Visual C#, In the Box, MVVMTraining.

NewProject

Videos

In the Box – MVVM Training Introduction video

You can view the above video using the web browser plug in, or you can download and watch an HD version of the video.  To download the HD version of the video, locate the, “About this video” section (lower right side of web page) and join Vimeo; it’s free and very quick to join.

Vimeo

Downloads

In the Box – MVVM Training on the Visual Studio Gallery

Visual Studio Express and Visual Studio 2008 Developers download the written content and sample application from my:

SkyDrive click here to download.

Note the link is to a folder; you will want to download both .zip files in the folder.

Expression Blend 4 SDK Requirement

I received a report from a developer having a problem with two references to the Expression Blend 4 SDK in two of the included projects.

I thought for awhile about including just these two DLL’s, but thought that would be a disservice, only providing two DLL’s.  I have opted to add the Expression Blend 4 SDK as a requirement because a high percentage of developers already have the SDK installed by Blend or by downloading it.

Have a great day,

Just a grain of sand on the worlds beaches.


Follow

Get every new post delivered to your Inbox.

Join 255 other followers