Archive of posts filed under the RIA Services category.

Unit Testing Silverlight Applications – UI testing and Test Automation

In my last post I am going to cover the last two topics I set out to address.  The first topic is UI testing.  What unit test frameworks lack sometime is the actually UI testing.  What I mean by that is for example, click on a button, then see what happens.  In case of Silverlight, Silverlight framework itself has a number of helper classes to help us achieve this goal.  For example, ButtonAutomationPeer class allows us to drive button actions from within unit tests.  Also, Microsoft Silverlight unit testing framework allows developers to insert UI components onto unit testing surface.  Let me illustrate both concepts via an example.  Here is my code for such unit test”

[TestMethod]
[Asynchronous]
[Description("Asynch Get Companies in UI")]
[Tag("Asynch")]
public void TestUIGetCompanies()
{
 
    var companyVM = new CompanyModule.ViewModels.CompanyListViewModel(
        new Microsoft.Practices.Composite.Events.EventAggregator());
    CompanyListView companyV = new CompanyListView();
    companyV.DataContext = companyVM;
    TestPanel.Children.Add(companyV);
    ButtonAutomationPeer buttonPeer = 
        new ButtonAutomationPeer(companyV.FindName("GetCompaniesButton") as Button);
    DataGrid grid = companyV.FindName("CompanyGrid") as DataGrid;
    IInvokeProvider buttonInvoker = (IInvokeProvider)buttonPeer;
 
    IGridProvider gridPeer = new DataGridAutomationPeer(grid);
 
    EnqueueCallback(() => buttonInvoker.Invoke());
    EnqueueConditional(() => { return companyVM.CompanyList != null; });
    EnqueueCallback(
        () => Assert.IsTrue(companyVM.CompanyList.Count() > 0, "Should have data"),
        () => Assert.IsTrue(grid.Columns.Count == 3, "Should have 3 columns"),
        () => Assert.IsTrue(gridPeer.RowCount > 0, "Should have rows"));
    EnqueueTestComplete();
}

 

Let’s trace down what is going on in this test.  First of all, I am creating view model class.  Since I do not have a bootstrapper, I manually create EventAggregator myself.  Next, I am create a view to put my view model into.  I create view, then put my view model into the data context of my view.  Nest important step is to add my view to unit te3swt harness.  This is accomplished by adding my view to test panel.  My screenshot below illustrates where test panel is in terms of test runnier UI.  I added hand cursor to illustrate the test panel referred to in UI below as “Test Stage.”  As I run the unit tests, I will actually see my view there in the test stage.  Just like I added the view, I can remove it from test stage.

image

Next I am setting up code to invoke a button.  I am creating new instance of ButtonAutomationPeer class.  Of course, I need to find a button to attach the automation peer to, and I am using FindName method to find an i9nstance of my button.  To use this functionality I had to name my button.  Then, I am setting up an automation peer for my grid as well.  In general, most user input controls in Silverlight framework have matching automation objects.  In my case, I am testing the results of my button invocation directly against the grid, making sure that the grid has correct number of rows and columns.  I can also combine this type of testing with mocking,  Here is the unit test for this case:

[TestMethod]
[Asynchronous]
[Description("Asynch Get Companies in UI With Mocking")]
[Tag("Asynch")]
public void TestUIGetCompaniesWithMock()
{
    var companyVM = new Mock<ICompanyListViewModel>();
    companyVM.SetupGet(testVM => testVM.CompanyList).
        Returns(new ObservableCollection<Company>(new[] {
        new Company(){
            CompanyID = Guid.NewGuid(), DateAdded = DateTime.Now, CompanyName = "some random company"},
        new Company(){ 
            CompanyID = Guid.NewGuid(), DateAdded = DateTime.Now.AddDays(-1), CompanyName = "another company"}}));
    companyVM.SetupGet(testVM => testVM.GetCompaniesCommand).
        Returns(new DelegateCommand<object>((o) =>
        companyVM.Raise(vm => 
            vm.PropertyChanged += null, new System.ComponentModel.PropertyChangedEventArgs("CompanyList"))));
 
 
    CompanyListView companyV = new CompanyListView();
    companyV.DataContext = companyVM.Object;
    TestPanel.Children.Add(companyV);
    ButtonAutomationPeer buttonPeer = new ButtonAutomationPeer(companyV.FindName("GetCompaniesButton") as Button);
    DataGrid grid = companyV.FindName("CompanyGrid") as DataGrid;
    IInvokeProvider buttonInvoker = (IInvokeProvider)buttonPeer;
 
    IGridProvider gridPeer = new DataGridAutomationPeer(grid);
 
    EnqueueCallback(() => buttonInvoker.Invoke());
            
    EnqueueCallback(
        () => Assert.IsTrue(companyVM.Object.CompanyList.Count() > 0, "Should have data"),
        () => Assert.AreEqual(3, grid.Columns.Count, "Should have 3 columns"),
        () => Assert.AreEqual(2, gridPeer.RowCount, "Should have rows"));
    EnqueueTestComplete();
}

 

In the case above I am mocking property changed event by invoking it when a button is clicked, causing UI to re-bind.  The rest of testing code is very similar to actual invocation of server side code.

The last topic I would like to cover is StatLight framework.  You can take a closer look at the framework here.  This framework allows developers to invoke unit tests written with Microsoft unit testing framework for Silverlight from a command line and also read in results of such runs.  Once you download and unzip the DLLs into a folder, you will need to do one more thing – Unblock those DLLs in the properties of each file in Windows explorer.  Also, if you want to test server side code using StatLight, you will need to convert your web site to use local IIS inste4ad of Cassini (Visual Studio built-in IIS Server).  You will also need to put clientaccesspolicy.xml into the root of your IIS in order to avoid cross domain exceptions.  Using the StatLight framework is very simple.  Simply call StatLight.exe and pass XAP file on command line that contains your unit tests.  Here is a screenshot of what it looks like:

image 

As you can see, your –x parameter is the only one you have to specify to test.  You will also see that unit test is launched at the same time.  Now, to test results you have to add one more parameter – r.  Here is what that command would look like:

image

Once you run this, you will end up with out.txt in the specified folder.  Here is the content of this file:

image

The last parameter I want to mention is ability to limit test run by tag attribute I covered in an earlier post.  To user this feature, you have to add –t command line attribute like so:  Here is what this command line look like:

image

You can download complete solution here.

Thanks.

Unit Testing Silverlight Applications – Mocking

In this post I will explore ability to mock various Silverlight classes in order to enable mocking in your unit tests.  There is a variety of mocking frameworks available, but I will pick one for my testing – Moq.  You can download the framework here,  Once this is done, I need to add some references to my Silverlight unit test project.

Moq requires three assemblies available in the folder where Moq is installed:

image

If we are mocking view model or another object relying on Prism, we also need to add Prism references

image

Now we are ready to mock!

Let’s start by doing something simple.  I will mock my view model.  The only property I really want to test is my model property, which in my case exposes list of companies.  TO do so, I just need to setup property getter that contains my model.  The getter will be called when first accessed.  Here is what we have:

[TestMethod]
[Tag("Mocking")]
[Description("VM Mocking")]
public void TestMocking()
{
    var vm = new Mock<ICompanyListViewModel>();
    vm.SetupGet(testVM => testVM.CompanyList).
        Returns(new ObservableCollection<Company>(new[] {
            new Company()
                { CompanyID = Guid.NewGuid(), DateAdded = DateTime.Now, CompanyName = "some random company"},
            new Company()
                { CompanyID = Guid.NewGuid(), DateAdded = DateTime.Now.AddDays(-1), CompanyName = "another company"}}));
    Assert.AreEqual(2, vm.Object.CompanyList.Count, "Mocking failed");
 
}

 

I am only mocking view model in this case.  With Moq framework, as it is the case with most mocking frameworks, you are required to have interfaces in order to mock.  In case of RIA Services, this is an issue because client side classes are sealed and do not implement interfaces.  If you are using your own WCF service with custom classes, you can avoid this small issue.  You can also mock events as well.  This is how I would mock property changed event in my view model:

companyVM.SetupGet(testVM => testVM.GetCompaniesCommand).Returns(new DelegateCommand<object>((o) =>
    companyVM.Raise(vm => vm.PropertyChanged += null, new System.ComponentModel.PropertyChangedEventArgs("CompanyList"))));

 

As you can see, mocking is pretty easy concept, and the web page for Moq has quick start guide to help you get started.  Feel free to use a different mocking framework as well.  In the next post I will show how to unit test user interface using Silverlight unit testing framework.

Thanks

Unit Testing Silverlight Applications – Asynchronous Testing

In this post I will describe how to create asynchronous unit tests for Silverlight applications.  First of all, I’d like to point out that any communication with a server component in Silverlight must be asynchronous.  Reason being is that if communication was synchronous and the remote service was not responding, browser which host Silverlight application would lock up.  As you can see, asynchronous communication with a RIA Services WCF service is very important in our case. 

Let’s take a look on how to write a test that would assert that we can successfully get a list of companies from the server.  To do so, we must first take a look on how to write such a test.  This can be accomplished using asynchronous constructs available in Silverlight unit testing framework.  First of all, we have to change inheritance for our test class.  When you create a new test class, it does not inherit from any class, and we have to change this first of all.  Here is what old test class declaration look like:

[TestClass]
public class Tests

Here is what we are going to change it to:

[TestClass]
public class Tests : Microsoft.Silverlight.Testing.SilverlightTest

Now we must decorate our test method with appropriate attribute to signal to test running harness that it is about to execute asynchronous test:

[TestMethod]
[Asynchronous]
[Description("Asynch Get Companies")]
[Tag("Asynch")]
public void TestGetCompanies()

Now, let take a deeper look into framework itself to see the key methods that we can use in base class – SilverlightTest.

First one is EnqueueConditional.  This method is designed to let unit test wait until condition specified as an argument to EnqueueConditional is met.  It is very useful when you want to wait until a method call to the service is completed.  Here is how I would use this method:

        [TestMethod]
        [Asynchronous]
        [Description("Asynch Get Companies")]
        [Tag("Asynch")]
        public void TestGetCompanies()
        {
            bool isLoaded = false;
            Exception error = null;
            IEnumerable<Company> results = null;
            CompanyDomainContext context = new CompanyDomainContext(new Uri(@"http://localhost:4988/SilverlightTestingDemo-Web-CompanyDomainService.svc", UriKind.Absolute));
            var query = context.GetCompaniesQuery();
 
            context.Load<Company>(query, (result) =>
            {
                isLoaded = true;
                results = result.Entities;
                error = result.Error;
            }, null);
            EnqueueConditional(() => isLoaded);

 

What I am doing above is declaring a Boolean variable isLoaded that would signal to the framework to continue the test once data has been loaded.  I am setting this variable to true in the lambda expression callback that is called one server method’s execution is completed – (result)=> expression.

Next interesting method is EnqueueCallback.  This method simply declares multiple or in Action delegates that will be executed one they are popped off execution stack when harness processes test method calls.  What I mean is that the harness will execute all Action delegates that EnqueueCallback declares when it proceeds with running a test method once condition that  EnqueueConditional declares is met. If there are no such calls in the method body and there are no calls to EnqueueDelay that would also pause execution, then EnqueueCallback has no meaning, and asynchronous test essentially becomes synchronous.  Here is what we have so far:

        [TestMethod]
        [Asynchronous]
        [Description("Asynch Get Companies")]
        [Tag("Asynch")]
        public void TestGetCompanies()
        {
            
            bool isLoaded = false;
            Exception error = null;
            IEnumerable<Company> results = null;
            CompanyDomainContext context = new CompanyDomainContext(new Uri(@"http://localhost:4988/SilverlightTestingDemo-Web-CompanyDomainService.svc", UriKind.Absolute));
            var query = context.GetCompaniesQuery();
 
            context.Load<Company>(query, (result) =>
            {
                isLoaded = true;
                results = result.Entities;
                error = result.Error;
            }, null);
            EnqueueConditional(() => isLoaded);
            EnqueueCallback(
                () => Assert.IsTrue(results.Count() > 0, "Should have data"),
                () => Assert.IsNull(error, "Should have no errors"));

 

The last important method is EnqueueTestComplete.  This method puts Test Completed signal onto the execution stack. This method is typically the very last statement of the test method body.  Here is our final version of the method:

        [TestMethod]
        [Asynchronous]
        [Description("Asynch Get Companies")]
        [Tag("Asynch")]
        public void TestGetCompanies()
        {
            
            bool isLoaded = false;
            Exception error = null;
            IEnumerable<Company> results = null;
            CompanyDomainContext context = new CompanyDomainContext(new Uri(@"http://localhost:4988/SilverlightTestingDemo-Web-CompanyDomainService.svc", UriKind.Absolute));
            var query = context.GetCompaniesQuery();
 
            context.Load<Company>(query, (result) =>
            {
                isLoaded = true;
                results = result.Entities;
                error = result.Error;
            }, null);
            EnqueueConditional(() => isLoaded);
            EnqueueCallback(
                () => Assert.IsTrue(results.Count() > 0, "Should have data"),
                () => Assert.IsNull(error, "Should have no errors"));
            EnqueueTestComplete();
        }

 

You can download the complete code here.  In the next post I will cover mocking for synchronous and asynchronous unit tests.

Thanks..

Unit Testing Silverlight Applications

I am starting a short series of posts on unit testing of Silverlight applications.  I will be using the following set of technologies. 

  • Microsoft Silverlight Unit testing framework that ships with Silverlight tool kit
  • Moq – mocking framework that supports Silverlight 4
  • StatLight – unit testing automation framework that will allow for unattended / automated running unit tests

I am going to start with a simple Silverlight application that is using RIA services and Prism.  First things first, let’s create unit test project.  Project template will be installed when you install the Silverlight toolkit.  Here is what add project dialog looks like.  The template is located under Silverlight tab

image

Here are the setting for the next dialog:

image

Once project is created, a simple test class is also created.  If you ever worked with Microsoft unit testing in Visual Studio, you will find the API quite familiar, including all key classed, attributes and namespaces.  I am going to create a simple synchronous test first, testing my RIA services class to ensure that validation rules are setup properly.  To do so, I first add a reference to my simple Company module to my Silverlight unit test project.  Since I am also testing RIA services classes, I will add two more references: System.ComponentModel.DataAnnotations and System.ServiceModel.DomainServices.Client.dll.  Now with this our of the way, here is what my test class and method look like:

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Silverlight.Testing;
using System.Collections.ObjectModel;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SilverlightTestingDemo.Web;
 
namespace SilverlightUnitTest
{
    [TestClass]
    public class Tests
    {
        [TestMethod]
        [Tag("Synch")]
        [Description("Rules")]
        public void TestCompanyRules()
        {
            Company company = new Company();
            company.CompanyName = "test";
            Assert.AreEqual(0, company.ValidationErrors.Count, "Should be valid");
            company.CompanyName = string.Empty;
            Assert.AreEqual(1, company.ValidationErrors.Count, "Should be in valid");
        }
    }
}

 

Let me go over the attributes I used.  TestClass attribute is what signals to Unit testing framework as to what classes contain unit tests.  TestMethod attribute identifies methods that represent a single unit test.  Tag attribute is used by unit test runner UI to filter out what tests based on attributes will be run.  Description attribute data will be shown in runner UI when unit tests are run. Assert class is used to assert test results.  For example, in my first Assert statement I make sure that Company instance is valid once name is set.

When I run unit tests after setting up unit test application’s web site as start up project, here is what I see.

image

Test are setup to auto-run by default.  You will see the countdown label, and if you click on count down label, you will have a chance to select specific tests to run.

image

After tests are run, you will see test run summary in the top left cornet of the screen.  In my case I see that I had total of 1 test, and 2 test has passed.  if a test fails, you will see that in the test run summary.  You can download solution so far here.  In my next post I will cover asynchronous testing in Silverlight application.

Thanks.

WCF RIA Services Validation

Today I am continuing exploring WCF RIA Services.  I am going to discuss validation in RIA Services.

Any business application needs to validate user input.  I am going to start with a simple case.  In my sample application a user can enter a company information.  I would like to setup RIA Services to force the user to enter a company name before he or she can save the company.  In order to do so I am going to use Data Annotations.  First of all, I need to add a reference to System.ComponentModel.DataAnnotations DLL to all my projects.  RIA Services project should already have the reference. 

Now I need to edit my metadata file that corresponds to my data model under .NET version of RIA Services.  In my case it is RolodexDomainService.metadata.cs file in SilverlightRIAServicesLibrary.Web project.  In that file I need to find Companies class, then location company name property, then decorate the property with required attribute.  At the same time we will also put a limit on how many characters a user can enter:

[Required]

[StringLength(30)]

public string CompanyName;

 

If we want to see the results of our changes, we can run the application, then clear company name for an existing comp-any, then tab out of company name field.  Here we get visual feedback that our input was not valid:

image

Now, let’s create custom validation.  Fir example, I am going to force the date added to be no less that 1/1/2000.  To do this I will write a custom validation class like so:

public static class ValidationHelper

{

    public static ValidationResult DateAddedCheck(DateTime date)

    {

        ValidationResult result;

        if (date > new DateTime(2000, 1, 1))

        {

            result = new ValidationResult("");

        }

        else

        {

            result = new ValidationResult("Date is not valid");

        }

 

        return result;

    }

}

Validation method that I wrote is a static member.  It follows a specific signature that RIA Services requires.  For example, it returns Validation Result that contains specific information that validation infrastructure of RIA Services can parse.  Overall code is fairly simple.

In order to make this class available on the Silverlight side, I must include my validation class into Silverlight version of RIA Services library – SilverlightRIAServicesLibrary project in my case.  In order not to duplicate the code, I will include the same class as a link.  I right-click on the project, choose add existing item, navigate to my class, then click on down arrow on Add button, and select Add As Link.  Now I have the same validation on both .NET and Silverlight side.  To confirm, I just build and run the application.  Here is what my DateAdded attributes look like:

[Required]

[CustomValidation(typeof(ValidationHelper), "DateAddedCheck", ErrorMessage = "Date is not valid")]

public DateTime DateAdded;

On important thing to mention that in  order to see validation error in UI, we need to update our Binding to look as following:

<TextBox x:Name="CompanyNameTextbox" Grid.Column="1" Grid.Row="0" HorizontalAlignment="Stretch" Margin="6,6,6,6" Text="{Binding Path=CompanyName, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnExceptions=True}" VerticalAlignment="Center"/>

There is one important thing to notice.  If you look at Silverlight side generated code, you will notice that validation occurs before the property value is set.  As a result, you cannot rely on property value being set if a validation exception is thrown.

Now, let’s look at saving code that we had to modify in order to validate user input.  Here is what it looks like:

try

{

    Validator.ValidateObject(this.Model, new ValidationContext(this.Model, null, null), true);

    if (this.Model.HasChanges)

    {

        ShowPleaseWaitMessage();

        _context.SubmitChanges(HandleSave, null);

    }

}

catch (ValidationException ex)

{

    MessageBox.Show(ex.Message);

}

Not really the cleanest code because we have to rely on exceptions being thrown. 

I covered all key area of validation, but there is more for you to explore.  Please let me know if you have any questions.

RIA Services (Cont.)

In this post I will cover the update process.

I am going to recap where I left off in the last post on RIA Services.  The last step was to retrieved based on ID from a read only company object.  Once this object is obtained in the VIewModel, we can create a screen to edit a company.

Here is how I am getting the company by ID in the ViewModel class:

var

companyQuery = _context.GetCompanyQuery(_companyID);

 

_context.Load<Companies>(companyQuery, (o1) =>

 

{

 

    HidePleaseWaitMessage();

 

    if (o.Error != null)

 

    {

 

        ErrorHandler.HandleException(o.Error);

 

    }

 

    else

 

    {

 

        this.Model = o1.Entities.First() as Companies;

 

    }

 

}, null);

Now that I have my Model property set, I can create a user control to edit the company.  Here is the part of the XAML for the edit form that shows the binding:

<UserControl x:Class

=”Rolodex.Silverlight.Views.CompanyEditView”

 

    xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”

 

    xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”

 

    xmlns:controls=”clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data”

 

    xmlns:input=”clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls”

 

    xmlns:cal=”clr-namespace:Microsoft.Practices.Composite.Presentation.Commands;assembly=Microsoft.Practices.Composite.Presentation”

 

    xmlns:core=”clr-namespace:Rolodex.Silverlight.Core”

 

    >

 

    <UserControl.Resources

>

 

       

 

    </UserControl.Resources

>

 

    <Grid x:Name

=”LayoutRoot”>

 

        <Grid.RowDefinitions

>

 

            <RowDefinition Height

=”*”/>

 

            <RowDefinition Height

=”Auto”/>

 

        </Grid.RowDefinitions

>

 

        <Grid x:Name=”EditGrid” DataContext=”{Binding Model

}”>

 

            <Grid.RowDefinitions

>

 

                <RowDefinition Height

=”35″/>

 

                <RowDefinition Height

=”35″/>

 

                <RowDefinition Height

=”1*”/>

 

                <RowDefinition Height

=”35″/>

 

                <RowDefinition Height

=”1*”/>

 

                <RowDefinition Height

=”35″/>

 

            </Grid.RowDefinitions

>

 

            <Grid.ColumnDefinitions

>

 

                <ColumnDefinition Width

=”Auto”/>

 

                <ColumnDefinition Width

=”200″/>

 

                <ColumnDefinition Width

=”*”/>

 

            </Grid.ColumnDefinitions

>

 

            <TextBlock Text=”Company Name:” TextAlignment=”Right” HorizontalAlignment=”Right” Grid.Column=”0″ Grid.Row=”0″ Margin=”6,6,6,6″ VerticalAlignment

=”Center”/>

 

            <TextBox x:Name=”CompanyNameTextbox” Grid.Column=”1″ Grid.Row=”0″ HorizontalAlignment=”Stretch” Margin=”6,6,6,6″ Text=”{Binding Path=CompanyName, Mode=TwoWay}” VerticalAlignment

=”Center”/>

 

            <TextBlock Text=”Date Added:” TextAlignment=”Right” HorizontalAlignment=”Right” Grid.Column=”0″ Grid.Row=”1″ Margin=”6,6,6,6″ VerticalAlignment

=”Center”/>

 

            <input:DatePicker x:Name=”DateAddedTextbox” SelectedDate=”{Binding DateAdded, Mode=TwoWay}” Grid.Column=”1″ Grid.Row=”1″ HorizontalAlignment=”Stretch” Margin=”6,6,6,6″ VerticalAlignment=”Center”/>

So far, I have an instance of a Companies object that is bound to the data context of my user control.  At this point a user can edit company name and date added fields.  Logically, the next thing a user would want to do is to save his changed – the nerve of him :-)

So, let’s take a look at the Save command in our view model object.

 

protected

override void BeginSave()

 

{

 

    ShowPleaseWaitMessage();

 

    _context.SubmitChanges(HandleSave, null);

 

}

 

 

 

private

void HandleSave(SubmitOperation operation)

 

{

 

    HidePleaseWaitMessage();

 

    if (operation.Error != null)

 

    {

 

        ErrorHandler.HandleException(operation.Error);

 

    }

 

}

Save is a two step process because all communication in Silverlight is asynchronous.  We are starting the save process in BeginSave, and we are checking for errors in HandleSave methods respectively.  The key part to this process is client side context of RIA Services that keeps track of all the changes that the user is making after the object has been initially retrieved via a call to the server.  The key work is done by the domain service object – LinqToEntitiesDomainService<RolodexEntities> in our case.  We can look at this code generated by the RIA Services wizard by looking at RolodexDomainService.cs class in my case.  There is a bug in current CTP that causes update to parent and child objects not being flushed properly.  In my case I have Companies object that contains a list of ComanyContacts objects.  Generated code has check for EntityState in UpdateCompanyContacts method that I had to remove to get the process to work.  Here is the final version of key methods of the Domain service class:

public

void UpdateCompanies(Companies currentCompanies)

 

{

 

    this.ObjectContext.AttachAsModified(currentCompanies, this.ChangeSet.GetOriginal(currentCompanies));

 

}

public

void UpdateCompanyContacts(CompanyContacts currentCompanyContacts)

 

{

 

    this.ObjectContext.AttachAsModified(currentCompanyContacts, this.ChangeSet.GetOriginal(currentCompanyContacts));

 

}

So, after this step all parts of the process are working – I can update companies and contacts.

Please email me any questions,

RIA Services (Cont.)

I am going to continue exploring the topic of RIA services and Silverlight.  I thought I was going to get into updates in this post, but there are a few topics I would like to cover first.  I am going to try to get to updates in the next post.

Today, I am going to explore two more topics.  I am going to cover extending RIA Services DomainContext with custom classes and working on including child objects when fetching a parent object using Entity Framework and RIA Services.

 

First, I am going to extend my DomainContext with a custom class.  In my case I have Company Object/Table with a number of columns.  In my Silverlight application I would like to have a list of companies that shows only company name.  When a user selected one company, I would like to fetch full company object with company contacts child object.  If I were to use the method that the wizard built for me – public IQueryable<Companies> GetCompanies(),I would get the full object with all columns.  So, I am going to build a custom class that only has company ID and name:

    public class ReadOnlyCompany

    {

        [Key]

        public int CompanyId { internal set; get; }

        public string CompanyName { internal set; get; }

    }

 

I am going to add this class to RIA Service project on .NET side (SilverlightRIAServicesLibrary.Web project in my case).  As you notice I did not have to decorate my class with DataContract attribute or decorate properties with DataMember attribute.  We do not actually need to do this, as RIA Services will take care of all that for us.  I do however need to use Key attribute or I would get a compile time error.  Each object is required to have a key (primary key) property.  Next step is to write a method in DomainService (RoldexDomainService in my case) class.  Here is what this would look like:

        public List<ReadOnlyCompany> GetReadOnlyCompanies()

        {

            return (from oneCompany in this.ObjectContext.Companies

                    orderby oneCompany.CompanyName

                    select new ReadOnlyCompany()

                    {

                        CompanyId = oneCompany.CompanyId,

                        CompanyName = oneCompany.CompanyName

                    }).ToList<ReadOnlyCompany>();

        }

 

Even though I return List object here, but RIA Services will actually return ReadOnlyObservableCollection<ReadOnlyCompany> on Silverlight side.  You have to remember that when you cast return value for RIA Services call to a specific object.  Here is what this call would look like on Silverlight side:

        private void GetCompaniesList()

        {

            ShowPleaseWaitMessage();

            var query = _context.GetReadOnlyCompaniesQuery();

 

            _context.Load<ReadOnlyCompany>(query, LoadBehavior.RefreshCurrent, (o) =>

            {

                HidePleaseWaitMessage();

                if (o.Error != null)

                    ErrorHandler.HandleException(o.Error);

                else

                {

                    this.Model = o.Entities as ReadOnlyObservableCollection<ReadOnlyCompany>;

                }

                    HidePleaseWaitMessage();

            }, null); ;

 

        }

Here I am putting up a please wait window, fire a query and interpret the results.  As I mention before, my next step is to get full Company object based on selected ID.  Again, I am adding a new method to DomainService class on .NET side:

        public Companies GetCompany(int companyID)

        {

            var returnValue = this.ObjectContext.Companies

                .Include("CompanyContacts")

                .Include("CompanyContacts.CompanyContactPhones")

                .Where(one => one.CompanyId == companyID).FirstOrDefault();

            return returnValue;

        }

Now, let’s see what this call looks like on Silverlight side.  This also demonstrates the use of parameters:

var companyQuery = _context.GetCompanyQuery(_companyID);

_context.Load<Companies>(companyQuery, (o1) =>

{

    HidePleaseWaitMessage();

    if (o.Error != null)

    {

        ErrorHandler.HandleException(o.Error);

    }

    else

    {

        this.Model = o1.Entities.First() as Companies;

    }

}, null);

As you can see, RIA services build us a query that already has the same company ID parameter for us.  Pretty cool!  However, if I look at return value (Companies object), I will notice that contacts property is actually null even though I did add .Include statement to my custom Entity Framework based method.  To get this to work, I have to modify the metadata class that RIA Services generated(RolodexDomainService.metadata.cs in my case).  I need to open this class file and look for Companies object.  I will find public EntityCollection<CompanyContactPhones> CompanyContactPhones property in it.  To get RIA Services to include child collection, I need to add Include attribute here as well:

[Include]

public EntityCollection<CompanyContactPhones> CompanyContactPhones;

I would have to take the same step to include phones collection for each contact.  If I run my code again, I will not get Company object with a list of Contacts, each containing a list of Phones.

I will try to cover update in my next post.

Thank you and do not hesitate to ask questions.

Getting Started with WCF RIA Services

I know many posts have been written on this subject by many people, but I am writing this one mostly for myself.  I find it very useful to type my thoughts on any subject and create a sample project to help me commit more information to memory.  So, here it goes.

What you need to get started.

  1. Visual Studio 2008 SP 1 (You can use 2010 as well)
  2. WCF RIA Services Beta for Visual Studio 2008 SP1 (http://www.microsoft.com/downloads/details.aspx?FamilyID=76bb3a07-3846-4564-b0c3-27972bcaabce&displaylang=en)

Here is what needs to be done next.

Create new Silverlight project.  To do so just create new project and select Silverlight.  Make sure to create a host web site as well.

image

On the next wizard screen select an option to create new host web site and enable .NET RIA Services.

image

 

Now, add new project to the solution, selecting WCF RIA Services Library under Silverlight projects.

image

Here is what your solution will look like:

image

Go ahead and delete Class1.cs from both RIAServicesLibrary projects.  What you see here are two new project.  RIAServicesClassLibrary1.Web will be server side project, the other RIA project is client side.  Now you need to establish references.  Add a project reference to SilverlightApplication1 and point it to RIASerivcesLibrary1 project.  Add a project reference to SilverlightApplication1.Web and point it to RIAServicesLirbary1.Web.

 

Now you need to create Entity Framework item inside the RIAServicesLibrary1.Web project.

image

Follow all the step of that wizard, generating a model from the database and pointing to a new or existing server connection.  You can select all or some tables from a database.

Go ahead and build your solution now.

Now add another new item to RIAServicesLirbary1.Web project and select Domain Service Class

image

Select the Entity Framework model you just created in that wizard and select tables you would like to use on the client.

image

Rebuild the solution once more to generate Silverlight side hidden classes that support RIA Services.  You can see that code if you click on Show All Files button in Solution Explorer:

image

Now, I am going to test the setup by adding the following code to MainPage.xaml.cs.  I am going to simulate the login process by getting a specific user from my Users table and checking the password:

using System.Windows.Ria;

using System.Linq;

public partial class MainPage : UserControl

    {

        DomainService1 _context = new DomainService1();

        public MainPage()

        {

            InitializeComponent();

            Login();

        }

 

        private void Login()

        {

            var query = _context.GetUsersQuery().Where(one => one.UserName == "admin");

 

            _context.Load<Users>(query, LoadBehavior.RefreshCurrent, (o) =>

            {

                if (o.Error != null)

                    MessageBox.Show(o.Error.ToString());

                else

                {

                    if ((o.AllEntities.Count() == 1) &&

                        (o.AllEntities.First() as Users).Password == "admin")

                    {

                        MessageBox.Show("Login successfull.");

                    }

 

                    else

                    {

                        MessageBox.Show("Login failed.");

                    }

                }

            }, null); ;

        }

    }

Next we need to update web.config in SilverlightApplication1.Web project using app.config from RIAServicesLirbary1.Web project as an example.  Make sure to paste all appropriate data into correct places:

<?xml version="1.0" encoding="utf-8"?>

<configuration>

    <connectionStrings>

        <add name="RolodexEntities" connectionString="metadata=res://*/Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl;provider=System.Data.SqlClient;provider connection string=&quot;Data Source=.\sql2008;Initial Catalog=Rolodex;Integrated Security=True;MultipleActiveResultSets=True&quot;" providerName="System.Data.EntityClient" />

    </connectionStrings>

    <system.serviceModel>

        <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />

    </system.serviceModel>

    <system.web>

        <httpModules>

            <add name="DomainServiceModule" type="System.Web.Ria.Services.DomainServiceHttpModule, System.Web.Ria, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />

        </httpModules>

    </system.web>

</configuration>

Run the application now to confirm that everything is working properly.  In the next post I will explore the subject of updating the data on the server.