Business Apps Example for Silverlight 3 RTM and .NET RIA Services July Update: Part 17: Evolving an Application

More from my Mix09 talk “building business applications with Silverlight 3”.  So far in this series we have looked at how to build out an new application using Silverlight 3 and .NET RIA Services… but now let’s look at how to evolve an existing SL3\RIA Services application… after all, for most of us that is our day-to-day jobs.  It is rare that we get to start with a green field application where we have full control.  Being able to deal with maintenance of an application takes a framework from being a cool demo toy to being an important tool!

For the context, you can watch the original  video of the full session  and catch up on the Full series

The demo requires (all 100% free and always free):

  1. VS2008 SP1 (Which includes Sql Express 2008)
  2. Silverlight 3 RTM
  3. .NET RIA Services July '09 Preview

Also, download the full demo files .

For this part, we will look at to interesting evolutions of the standard SuperEmployee app we have been building… The first is adding a new column to the table we have been working with and the second is to add a whole new table with additional data associated with each SuperEmployee.

A fair bit of the details of how the evolution workflow is accomplished is dependent on what DAL you use.  Because .NET RIA Services is DAL agonistic, this part of the evolution story can change a bit.  For this walkthrough I will show the story with Entity Framework and will leave it as an exercise for the reader to map it to your own DAL.

Adding a new Column

Ever since I cooked up the SuperEmployee schema 6 months ago I felt that it was really missing the most important aspect of being a super hero. That is knowing what his or her power is.  I mean, i have to know their super power to hire them right?  So let’s look at adding “Superpower” to the database.

Let’s open nortwind.mdf from the app_data directory and select Open Table Definition

image 

Then add a new column called “SuperPower” and set it type to be nvarchar(MAX)

image

OK – we have the database scheme updated, now we need to update our model.  To do that open northwind.edmx and select “Update Model from Database”

image

Select Refresh

image

And we see our model is updated!

image

Now, simply build and we can access this new property from the web server in the DomainService if we needed to write some application logic that deals with it or we can access it directly from the client.

image

To start with, let’s add SuperPower to the list of fields the DataForm is handling for us…  This is just an update to the DataForm we talked about way back in Part 2: Rich Data Query

 <dataControls:DataField>
     <TextBox Text="{Binding SuperPower, Mode=TwoWay}" />
 </dataControls:DataField>

And of course the display works great

image

But I can now add a value to several SuperHeros and hit submit and see the values reflected in the database.

image

Now let’s add just a bit of validation to our new member.. In the SuperEmployeeDomainService.metadata.cs file add

 [StringLength(25,MinimumLength=3,
     ErrorMessage = "Superpower's should be between 3 and 25 characters long")]
 public string SuperPower;

build and run and we get get validation!

image

Let’s roll back to our Astoria service and our WinForm client from Part 5… what do we need to do to update those?  Well, not much.  The Astoria service is all dynamically generated based on the model and the DataGridView is also dynamic in this case, so all we need to do is update the service reference and we are good to go!

image

And the app runs great… an easy way to enter lots of data quickly..

image

What I showed so far is how easy it is to evolve your entity to have an addition field.  Let’s now look at adding a whole new entity.

Adding a New Associated Entity

For this example, i’ll add some contact information for the SuperHero agent so that we can get up with the SuperHero we decide to hire.   The first step is to add a table to our database to store the contact information

image

Then we need to associate it with the SuperEmployees table via an foreign key. 

image

Now we can update our Entity Framework model

image

We want to select the new Contact class

image

And update our SuperEmployee class to get the FK for the Contact

image

That gives us this model

image

do a full build and everything should be good.

Now let’s integrate the Contact table into our app. 

First we need to add the logic to our DomainService. 

    1: public IQueryable<SuperEmployee> GetSuperEmployees()
    2: {
    3:     return this.Context.SuperEmployeeSet
    4:                .Include("Contact")
    5:                .Where(emp=>emp.Issues>100)
    6:                .OrderBy(emp=>emp.EmployeeID);
    7: }

Notice in line 4, we told EF to include the associated Contact item when we pull each SuperEmployee from the database.

Now in SuperEmployeeMetadata.cs we need to add some metadata to tell RIA Services to include the contact information in data it sends to the client

 [Include]
 public Contact Contact;

Now, we may want to edit this contact information, so let’s add methods to the DomainService to add new and update our Contact entity.

 public void InsertContact(Contact contact)
 {
     this.Context.AddToContact(contact);
 }
  
 public void UpdateContact(Contact currentContact)
 {
  
     this.Context.AttachAsModified(currentContact,
                                   ChangeSet.GetOriginal(currentContact));
 }

Great.  The server side is done, now let’s go to the client..  Here what I want to do is add some UI to the details dataform to show the contact information.

Inside the DataForm in Home.xaml add a button to edit the contact information.

 <Button Content="Edit Contact Information..." 
         Width="205" Height="28"
         Margin="15,10,0,0" HorizontalAlignment="Left"
         Click="EditContact_Click" >
 </Button>

image

And we need to handle the click event.  Here we create a ChildWindow to display a form to view\edit the contact information.

 private void EditContact_Click(object sender, RoutedEventArgs e)
 {
     var emp = dataForm1.CurrentItem as SuperEmployee;
     if (emp.Contact == null)
         emp.Contact = new Contact();
     var w = new EditContactWindow(emp.Contact);
     w.Show();
     w.Closed += EditContact_Closed;
  
 }
  
 void EditContact_Closed(object sender, EventArgs e)
 {
     var win = sender as EditContactWindow;
     var emp = dataForm1.CurrentItem as SuperEmployee;
     if (win.DialogResult == true)
     {
         emp.Contact = win.Contact;
  
     }
 }

Add a new ChildWindow and call it EditContactWindow().  In the codebehind set it up in code behind. 

 public partial class EditContactWindow : ChildWindow
 {
     public Contact Contact;
     public EditContactWindow(Contact contact)
     {
         InitializeComponent();
         Contact = contact;
         this.LayoutRoot.DataContext = Contact; 
     }
  
     private void OKButton_Click(object sender, RoutedEventArgs e)
     {
         this.DialogResult = true;
         dataform1.CommitEdit();
     }
  
     private void CancelButton_Click(object sender, RoutedEventArgs e)
     {
         this.DialogResult = false;
         
     }
 }

And the Xaml is simply a DataForm to display the results:

 <dataFormToolkit:DataForm   x:Name="dataform1" CurrentItem="{Binding}">
     <dataControls:DataForm.EditTemplate>
         <DataTemplate>
             <StackPanel>
                 <dataControls:DataField>
                     <TextBox Text="{Binding ContactName, Mode=TwoWay}" />
                 </dataControls:DataField>
                 <dataControls:DataField>
                     <TextBox Text="{Binding ContactTitle, Mode=TwoWay}" />
                 </dataControls:DataField>
                 <dataControls:DataField>
                     <TextBox Text="{Binding Address, Mode=TwoWay}" />
                 </dataControls:DataField>
                 <dataControls:DataField>
                     <TextBox Text="{Binding City, Mode=TwoWay}" />
                 </dataControls:DataField>
                 <dataControls:DataField>
                     <TextBox Text="{Binding Region, Mode=TwoWay}" />
                 </dataControls:DataField>
                 <dataControls:DataField>
                     <TextBox Text="{Binding PostalCode, Mode=TwoWay}" />
                 </dataControls:DataField>
                 <dataControls:DataField>
                     <TextBox Text="{Binding Phone, Mode=TwoWay}" />
                 </dataControls:DataField>
             </StackPanel>
         </DataTemplate>
     </dataControls:DataForm.EditTemplate>
 </dataFormToolkit:DataForm>     

And it looks great!

image

Notice, back on the server in SuperEmployeeDomainService.metadata.cs, i did give it some metadata to control how the Contact is displayed.  This is where I would put validation as well. 

 [MetadataTypeAttribute(typeof(Contact.ContactMetadata))]
 public partial class Contact
 {
  
     internal sealed class ContactMetadata
     {
  
         // Metadata classes are not meant to be instantiated.
         private ContactMetadata()
         {
         }
  
         
         [Display(Name="Name")]
         public object ContactName;
         
         [Display(Name = "Title")]
         public object ContactTitle;
  
         public object Address;
         
         public object City;
         
         [Display(Name = "State")]
         public object Region;
         
         [Display(Name = "Zip Code")]
         public object PostalCode;
  
         public object Phone;
  
     }
 }

We are done!

In this part we looked at how to incrementally evolve a RIA Services + Silverlight application.  I showed how to add a new column to an existing table and how to add a whole new table.

Enjoy!