Deborah's Developer MindScape






         Tips and Techniques for Web and .NET developers.

Archive for ASP.NET RIA Services

January 31, 2011

Accessing Data in a Silverlight Application: EF

Filed under: ASP.NET RIA Services,C#,Silverlight,VB.NET @ 12:29 am

One of the challenges of building a Silverlight line of business (LOB) application is getting the data from the business database. The business database resides on a server computer and the Silverlight application resides on each user’s computer. This post covers using Entity Framework (EF) to get the data from the database server to the Silverlight application.

NOTE: This post is part of a series that starts with this prior post. The example in this post uses the application that is built as part of that series.

The primary choices for getting data into your Silverlight application are:

  • Build an entity class in the Silverlight application that is populated from a WCF service.
  • Generate an entity class in the Silverlight application using WCF RIA services and Entity Framework.
  • Generate an entity class in the Silverlight application using WCF RIA services and your business objects (sometimes called Plain Old CLR Objects or POCOs). See this prior post for more information on using WCF RIA with your business objects.

Plus there are third party products that provide similar functionality.

This post focuses on the second approach: using Entity Framework and WCF RIA Services.

Prerequisites

Before beginning, you need a database. I had two choices when preparing the example for this post:

1) Build a database with a single table for the student and scores. This option would keep things very simple and readily map to the class created in the prior posts of this series.

2) Build a database with several tables that more closely map to a "real" database design. Very rarely would a real LOB application access only a single table.

I picked option 2 in order to provide a more realistic example. So if you want to work through this example, build a database. I called mine InStepSM for InStep Student Management, which is the sample application built in this blog series. Then add three tables:

image

Enter some test data into each table. Be sure to match up the foreign keys.

Entity Framework

Once you have the database, you are ready to build the Entity Framework model.

1) Open the Web project of your Silverlight solution.

2) Add an ADO.NET Entity Data Model using the appropriate VB.NET or C# template.

image

3) Select to generate the data model from the database.

image

4) Select the connection to the desired database.

image

5) Select the desired tables, views, or stored procedures. In this example, all three tables are selected.

image

The result is an Entity Framework data model (edmx):

image

WCF RIA Services

With the entity model in place, use WCF RIA Services to generate the business classes and WCF communication code in your Silverlight project. You can then use the generated business classes in your Silverlight application for create, retrieve, update, delete (CRUD) operations.

1) Enable WCF RIA Services in your Silverlight application by setting the WCF RIA Services Link in the Silverlight project properties to reference the Silverlight Web project.

image

2) Build the solution.

NOTE: This is required or the Domain Service won’t know about your entity model and the following steps won’t work as expected.

3) In the Web project, build a Domain Service class for one of your entities.

image

4) Select the appropriate entities from the resulting dialog:

image

Since Step 3 identified a StudentService class, Step 4 selects the Student entity. If you want your Silverlight code to change the data, be sure to check "Enable editing". Leave it unchecked if you want read-only data.

When you click OK, Visual Studio generates the service code in VB.NET or C#.

5) Repeat steps 3 and 4 for each entity.

If you want to build along with this example, add a ScoreService  that references the Score entity and a ScoreTitleService that references the ScoreTitle entity. Be sure to check "Enable editing" for both of them.

Silverlight

Wow! Quite a bit of work. But we are finally ready to hook the data to the Silverlight user interface.

1) Add a page to your Silverlight project that will allow the user to review or edit student information.

If you are working through the set of blog posts, add a new Page called Students. (Be sure to add a link from the main page to this page using the technique from this prior post.)

2) When the Silverlight page is active in the designer, show the available Data Sources: Data | Show Data Sources.

There should be a data source for each domain service you added above.

image

3) Drag and drop the Student data source onto the page.

image

Silverlight generates the UI, including all of the required binding. By default, it adds controls for each field in the table in alphabetical order. You can delete, or re-arrange as desired.

NOTE: If you want to define the desired order of the columns BEFORE dragging and dropping the grid onto the form, you can edit the generated metadata file. In the Web project, locate the StudentService.metadata.cs or StudentService.metadata.vb file. If you don’t see the metadata files, recompile to generate them. Add attributes onto the properties as shown below and recompile.

In C#:

[Display(Order=3)]
public Nullable<int> Age { get; set; }

[Display(Order=2)]
public string Email { get; set; }

[Display(Order=0)]
public string FirstName { get; set; }

[Display(Order=1)]
public string LastName { get; set; }

In VB:

<Display(Order:=3)>
Public Property Age As Nullable(Of Integer)

<Display(Order:=2)>
Public Property Email As String

<Display(Order:=0)>
Public Property FirstName As String

<Display(Order:=1)>
Public Property LastName As String

4) Run the application.

image

Now the real question is … how the heck did that work?

The xaml that was generated in Step 3 above includes a DomainDataSource control:

<riaControls:DomainDataSource AutoLoad="True"
     d:DesignData="{d:DesignInstance my:Student, CreateList=true}"
     Height="0"
     LoadedData="studentDomainDataSource_LoadedData"
     Name="studentDomainDataSource"
    
QueryName="GetStudentsQuery"
     Width="0">
    <riaControls:DomainDataSource.DomainContext>
        <my:StudentContext />
    </riaControls:DomainDataSource.DomainContext>

The code items shown in bold are the most important. The QueryName property defines the query that is executed to obtain the data. The DomainContext property defines the name of the class containing that query method. But try to locate either of those in your code and you won’t find them.

Select the Silverlight project in Solution Explorer and click on the Show All Files button in the Solution Explorer toolbar.The Generated_Code folder then appears. In that folder is an InStepSM.SL.Web.g.cs or InStepSM.SL.Web.g.vb file. That file contains all of the code in the Silverlight project that was generated by Visual Studio from the information in the domain service classes. That code contains the StudentContext class which has the GetStudentsQuery method referenced by the DomainDataSource control.

The GetStudentsQuery method uses WCF to call the Web project’s StudentService which in turn uses EF to retrieve the data and return it to the Silverlight project. The binding in the xaml does the rest.

Use the steps above any time you want to use EF and WCF RIA Services to access data in your application.

Click here to download the associated source code.

Enjoy!

EDIT 2/5/2011: Added a few more columns to the Student table to make the later update features more interesting. Also added a note about editing the metadata.

EDIT 2/8/2011: Added the link to the source code.

July 20, 2010

SL/RIA/POCO: No Generated Code?

Filed under: ASP.NET RIA Services,C#,Silverlight,VB.NET @ 4:38 pm

This prior post described the code that is generated when you compile a Silverlight application that uses WCF RIA Services and your own plain old CLR objects (POCOs). This post provides some ideas if something goes horribly wrong and the compiler does not generate the appropriate code.

If you build your solution and you don’t get the generated code described in this prior post, here are some things to check:

  • In your business layer component (where your business classes are defined):
    • The class(es) you want to access from Silverlight must be a public.
    • The properties that you want to access from Silverlight must be public.
    • The Key attribute must be defined on one of the business class public properties.
  • In your Silverlight project:
    • Ensure you have a WCF RIA Services link.
      • Open the Silverlight project properties. The WCF RIA Services link is defined at the bottom of the properties dialog
  • In your ASP.NET project (that was created automatically when you created your Silverlight project)
    • You must have a reference defined to your business layer component.
  • In your Domain Service class:
    • The Domain Service class must be defined within the ASP.NET project (or in a class referenced by the ASP.NET project).
    • It must have the EnableClientAccess attribute on the class.
    • It must inherit from the DomainService class (or one of its specialized classes such as LinqToEntiesDomainService if you are using Entity Framework)
    • If you define a Query method to retrieve data from your business class, the Query method:
      • Must be a method and not a property.
      • Must return one of the following:
        • A single entity (instance of the business object class)
        • An IEnumerable<T> where T is the entity
        • An IQueryable<T> where T is the entity
      • Can have any parameters.
      • Can have any name.
      • Can (but does not have to) have a Query attribute to define the method as a query.
    • If you define an Update method to update data from your business class, the Update method:
      • Must be a method.
      • Must have no return value.
      • Must pass the entity as a parameter.
      • Must either:
        • Start with "Update", "Change" or "Modify
        • Or have an Update attribute

The first time I tried using WCF RIA services, I did not know about the Key attribute, so did not get any generated code.

More recently, I used a converter to convert some working C# code from my Domain Service class into VB and then could not get any generated code from the VB solution. I did not immediately notice that the converter translated IEnumerable<Customer> to just IEnumerable (non-generic). So the compiler did not know which entity to process.

I hope the above checklist will help you quickly find why you are not getting generated code. If you have other things to add to this checklist, I’d love to hear from you. Please add a comment to this post.

Enjoy!

EDITED 8/12/10: Incorporated Colin Blair’s comment from 8/11/10.

SL/RIA/POCO: Generated Code

Filed under: ASP.NET RIA Services,C#,Silverlight,VB.NET @ 3:38 pm

This post provides a high-level description of what happens when you compile a Silverlight application that uses WCF RIA Services and plain old CLR objects (POCOs) and the resulting generated code. (See this prior post for a code example.)

After you define your Silverlight project, enable WCF RIA Services, and add your Domain Service class with appropriate method(s), you have enough to build your solution and view the generated code.

In Solution Explorer, select your Silverlight project and click the Show All  Files button in the toolbar. You will then see a Generated_Code folder containing a *.g.cs file. This contains the code generated by Visual Studio and defines the classes detailed below.

Entity class

This class is generated from information in the business layer (your POCO classes on the server) and includes all of the class’s properties. It inherits from the DomainServices Entity class (not to be confused with the Entity Framework (EF) Entity class).

The compiler looks at the properties of the class and any attributes on those properties. It then generates a copy of those properties with their associated attributes in the generated code file.

How does it know which business layer class to use for the Entity class? It determines the appropriate class based on the parameters and/or return values on the methods in your Domain Service class.

If you have a query style method, it returns either a single entity or an IEnumerable or IQueryable of the entity. The compiler uses that information to determine which business layer class to use.

If you have an insert, update, or delete method, the parameter defines the entity. The compiler uses that information to determine which business layer class to use.

The compiler attributes the generated class as a DataContract and each property as a DataMember so it can be processed appropriate when the class is used as part of a WCF service. (WCF RIA Services uses WCF "under the covers".)

Domain Context class

This class is generated from information in the Domain Service class that you created. It inherits from DomainContext.

The compiler defines a WCF service contract in this class. It also defines wrapper methods for the methods in your Domain Service class.

When your Silverlight application calls methods in this class, the class makes the WCF service calls to the Domain Service class in the ASP.NET project.

Here it is in picture form:

image

Enjoy!

July 19, 2010

SL/RIA/POCO: Field Ordering

Filed under: ASP.NET RIA Services,C#,Silverlight,VB.NET @ 5:30 pm

The prior post here detailed how to build a Silverlight application using WCF RIA Services and your own plain old CLR objects (POCOs). That application displayed the data in a grid and allowed updates using a set of labels and textboxes. But the order of the fields was alphabetical, which is rarely the order that you want.

image

So you could spend some time in the XAML, reordering the controls.

OR, you could add a few more attributes to your business object in your business layer and the fields will be ordered as you want.

Use the DisplayAttribute to:

  • Define a field name different from the business object property name.
  • Define a short name for use in the grid.
  • Define a field to use as a watermark.
  • Define the order of the field when building the UI.
  • Define the resource type if the strings are defined in a resource file.

First, be sure to import the System.ComponentModel.DataAnnotations namespace. Then add the DisplayAttribute to the properties as shown in this example.

In C#:

[Display(Order=1, Description="Customer’s last name", ShortName="LName", Name="Name (Last)")]
public string LastName { get; set; }

In VB:

<Display(Order:=1, Description:="Customer’s last name", ShortName:="LName", Name:="Name (Last)")>
Public Property LastName() As String

The result looks like this:

image

Notice the use of the ShortName in the grid column header and of the Name in the field label. But more importantly for this column, notice the order of the fields both in the grid and in the data entry form.

Use this technique to define the field ordering before you drag and drop a grid or data entry form onto your Silverlight page.

Enjoy!

SL/RIA/POCO: Update Data

Filed under: ASP.NET RIA Services,C#,Silverlight,VB.NET @ 2:55 pm

The prior post here detailed how to build a simple Silverlight application that used WCF RIA Services to communicate with your server-side business layer to retrieve data and display it in a Silverlight DataGrid. This post adds to the prior post, providing an update feature.

In keeping with the general theme of the prior post, this post is going to provide the simplest technique for performing an update in a Silverlight application using your own Plain Old CLR Objects (POCOs) that reside on the server.

So first, the prerequisites:

1) Follow the steps in the prior post here.

2) Build the Data Access Layer.

The prior post "mocked" the data access by returning a simple list from the business layer retrieve method. To try out the save, this won’t suffice. The code needs real data access.

Use the information in the post defined here to build your data access layer.

3) Change your Customer class to call the Data Access layer.

In the prior post, the Customer class Retrieve method returned a hard-coded set of customers. This method needs to change to use the Data Access layer. In addition, the code needs a Save method to save the customer data.

In C#:

public List<Customer> Retrieve()
{
    List<Customer> custList = new List<Customer>();

    DataTable dt = Dac.ExecuteDataTable("CustomerRetrieve");

    foreach (DataRow dr in dt.Rows)
    {
        Customer cust = new Customer
        {
            CustomerId = (int)dr["CustomerId"],
            LastName = dr["LastName"].ToString(),
            FirstName = dr["FirstName"].ToString(),
            EmailAddress = dr["EmailAddress"].ToString()
        };
        custList.Add(cust);
    }

    return custList;
}

public bool Save()
{
    bool success = true;

    // Call the save
    int returnId = Dac.ExecuteNonQuery("CustomerUpdate",
                    new SqlParameter("CustomerId", CustomerId),
                    new SqlParameter("LastName", LastName),
                    new SqlParameter("FirstName", FirstName),
                    new SqlParameter("EmailAddress", EmailAddress));

    if (returnId != 0)
        this.CustomerId = returnId;

    return success;
}

In VB:

Public Function Retrieve() As List(Of Customer)
    Dim custList As New List(Of Customer)

    Dim dt As DataTable = Dac.ExecuteDataTable("CustomerRetrieve")

    For Each dr As DataRow In dt.Rows
        Dim cust As New Customer With {
           .CustomerId = CType(dr("CustomerId"), Integer),
           .LastName = dr("LastName").ToString(),
           .FirstName = dr("FirstName").ToString(),
           .EmailAddress = dr("EmailAddress").ToString()}
        custList.Add(cust)
    Next
    Return custList
End Function

Public Function Save() As Boolean
    Dim success As Boolean = True

    ‘ Call the save
    Dim returnId As Integer = Dac.ExecuteNonQuery("CustomerUpdate",
                    New SqlParameter("CustomerId", Me.CustomerId),
                    New SqlParameter("LastName", Me.LastName),
                    New SqlParameter("FirstName", Me.FirstName),
                    New SqlParameter("EmailAddress", Me.EmailAddress))

    If returnId <> 0 Then
        Me.CustomerId = returnId
    End If

    Return success
End Function

NOTE: Normally I build a Customer class (singular) with the properties and methods that mange a single customer and a Customers class (plural) that works with a list of those customers. The functionality for both were added to one class to keep this example as simple as possible.

Once the prerequisites are in place, we can get started.

STEP 1: Add an Update method to the Domain Service class

Open the Domain Service class and add an update method.

In C#:

public void UpdateCustomer(Customer updatedCustomer)
{

}

In VB:

Public Sub UpdateCustomer(ByVal updatedCustomer As Customer)

End Sub

A few notes about this UpdateCustomer method:

  • It must be a method.
  • Must have no return value.
  • Must pass the entity as a parameter.
  • Must either:
    • Start with "Update", "Change" or "Modify
    • Or have an Update attribute

WCF RIA Services recognizes the method as an update method because of its name or Update attribute. It recognizes it as an update of a Customer by its parameter. This method is required for an update operation to be valid in the Silverlight application.

This method does not require any code, unless you want to perform some additional operations on an update. However, the method still needs to exist for the DomainDataSource on the page to accept updates.

This method is called when the code calls SubmitChanges, as shown later in this post.

STEP 2: Override the PersistChangeSet method

The PersistChangeSet method is called when the code calls SubmitChanges, after the code calls the Update method. Use this method to call the Save method on your business objects.

In C#:

protected override bool PersistChangeSet()
{
    Customer changedCustomer;
    bool success = true;

    foreach (var item in ChangeSet.ChangeSetEntries)
    {
        if (item.Entity.GetType() == typeof(Customer))
        {
            changedCustomer = (Customer)item.Entity;
            success = changedCustomer.Save();
            if (!success)
                break;
        }
    }
    return success;
}

In VB:

Protected Overrides Function PersistChangeSet() As Boolean
    Dim changedCustomer As Customer
    Dim success As Boolean = True

    For Each item In ChangeSet.ChangeSetEntries
        If item.Entity.GetType() = GetType(Customer) Then
            changedCustomer = DirectCast(item.Entity, Customer)
            success = changedCustomer.Save()
            If Not success Then
                Exit For
            End If
        End If
    Next
    Return success
End Function

WCF RIA Services keeps track of any changes to the entities in the application (Customer in this example). You can access the set of changes using the ChangeSet property.

The Customer class in this example has a Save method that saves one specific customer. So the above code loops through the customers in the ChangeSet and calls the Save method on the customer.

This would be more efficient if you had a Customers class (as mentioned in the Note earlier in this post). Your Customers class could have a save method against a list of customers. You could then call Customers.Save to save all of your customer change instead of calling Save on each individual customer.

STEP 3: Build the UI

Again, keeping with the goal of staying as simple as possible, the UI in this example is a simple set of properties.

1) Open the designer for MainPage.xaml by double-clicking on the file.

2) Open the Data Sources window (Data | Show Data Sources).

Visual Studio automatically added the defined domain context class to your data sources. Change the default UI from a data grid to a details view:

image

3) Drag and drop the Customer data source onto the designer to the right of the grid (created in the prior post) and Visual Studio creates the user interface for you:

image

4) Add the Save button as shown above.

In the code behind for the Save button, add code as follows.

In C#:

private void SaveButton_Click(object sender, RoutedEventArgs e)
{
    if (customerDomainDataSource.HasChanges)
        customerDomainDataSource.SubmitChanges();
}

In VB:

Private Sub SaveButton_Click(ByVal sender As System.Object, 
  ByVal e As System.Windows.RoutedEventArgs) Handles SaveButton.Click
    If CustomerDomainDataSource.HasChanges Then
        CustomerDomainDataSource.SubmitChanges()
    End If
End Sub

This code checks for changes and if the DomainDataSource on the page is changed, it calls SubmitChanges. SubmitChanges first calls the UpdateCustomer method and then the PersistChangeSet method, which in turn calls the Save method of the Customer business object.

STEP 4: Run

If you run the application, you should get this:

image

This works because when you drag and drop the data source onto the page, Visual Studio defines the labels and textboxes and binds the textboxes to the DomainDataSource that was added in the prior post. The DomainDataSource takes care of managing the updates.

For a more complete example, download the extended source from here.

If you want to learn more about using Silverlight and RIA Services, come and see my talk at VSLive in Redmond Washington August 4, 2010. See this post for more information.

Use these techniques as a starting point when performing update operations using your server-side business layer from a Silverlight application.

Enjoy!

The World’s Simplest Silverlight/RIA/POCO Example

Filed under: ASP.NET RIA Services,C#,Silverlight,VB.NET @ 10:33 am

This post demonstrates how to use Silverlight and WCF RIA Services to access YOUR server-side business objects to build a line of business application.

This particular example is as absolutely simple as possible to help you get up and running with these technologies. To meet this goal, this first post covers only how to retrieve data. Later posts will cover update operations.

So first, the prerequisites:

1) Download and install Silverlight 4 and WCF RIA Services 1.0.

Visual Studio 2010 comes with Silverlight 3. You need to download and install the Silverlight 4 Tools for Visual Studio 2010 from here. It comes with WCF RIA Services, so no additional download is required for RIA.

2) Download the Silverlight Toolkit.

This download provides many additional controls and themes. You can download it from here.

3) Build the Business Layer component.

When building a Silverlight/RIA application, you have probably already built your business layer and have business objects that you want to use in your Silverlight application. But if not, you need to do that first.

This example uses an overly simplified business layer component with the Customer class shown below.

Build a standard Class Library project and add the following code:

In C#:

using System.Collections.Generic;

namespace ACM.BLCSharp
{
    /// <summary>
    /// Manages a customer
    /// </summary>
    public class Customer
    {
        public int CustomerId { get; set; }
        public string LastName { get; set; }
        public string FirstName { get; set; }
        public string EmailAddress { get; set; }

        /// <summary>
        /// Retrieves a list of customers.
        /// </summary>
        /// <returns></returns>
        /// <remarks>
        /// In a "real" application, this code would
        /// call a data access component that retrieves
        /// the data from a database.
        /// </remarks>
        public List<Customer> Retrieve()
        {
            List<Customer> custList = new List<Customer>
                    {new Customer()
                          { CustomerId = 1,
                            FirstName="Bilbo",
                            LastName = "Baggins",
                            EmailAddress = "bb@hob.me"},
                    new Customer()
                          { CustomerId = 2,
                            FirstName="Frodo",
                            LastName = "Baggins",
                            EmailAddress = "fb@hob.me"},
                    new Customer()
                          { CustomerId = 3,
                            FirstName="Samwise",
                            LastName = "Gamgee",
                            EmailAddress = "sg@hob.me"},
                    new Customer()
                          { CustomerId = 4,
                            FirstName="Rosie",
                            LastName = "Cotton",
                            EmailAddress = "rc@hob.me"}};
            return custList;
        }
    }
}

In VB:

”’ <summary>
”’ Manages a customer
”’ </summary>
”’ <remarks></remarks>
”’ <editHistory></editHistory>
Public Class Customer
    Public Property CustomerId As Integer
    Public Property FirstName() As String
    Public Property LastName() As String
    Public Property EmailAddress() As String

    ”’ <summary>
    ”’ Retrieves a list of customers.
    ”’ </summary>
    ”’ <returns></returns>
    ”’ <remarks>
    ”’ In a "real" application, this code would
    ”’ call a data access component that retrieves
    ”’ the data from a database.
    ”’ </remarks>
    Public Function Retrieve() As List(Of Customer)
        Dim custList As New List(Of Customer) From
                    {New Customer() With
                          {.CustomerId = 1,
                            .FirstName = "Bilbo",
                            .LastName = "Baggins",
                            .EmailAddress = "bb@hob.me"},
                    New Customer() With
                          {.CustomerId = 2,
                            .FirstName = "Frodo",
                            .LastName = "Baggins",
                            .EmailAddress = "fb@hob.me"},
                    New Customer() With
                          {.CustomerId = 3,
                            .FirstName = "Samwise",
                            .LastName = "Gamgee",
                            .EmailAddress = "sg@hob.me"},
                    New Customer() With
                          {.CustomerId = 4,
                            .FirstName = "Rosie",
                            .LastName = "Cotton",
                            .EmailAddress = "rc@hob.me"}}
        Return custList
    End Function
End Class

NOTE: This code does not use a data access layer at this time. To keep this example as simple as possible, the Retrieve method "mocks" a retrieve that would call your data access component and retrieve the data from the database. This code will be replaced in a later post to actually access a database.

NOTE: Normally I build a Customer class (singular) with the properties and methods that mange a singe customer and a Customers class (plural) that works with a list of those customers. The functionality for both were added to one class to keep this example as simple as possible.

Once the prerequisites are in place, we can get started.

STEP 1: Attribute your business objects to work with Silverlight

There are several different attributes you can define in your business objects so they work better with Silverlight, but in keeping with our goal of creating the simplest Silverlight/RIA/POCO example, you are only required to have one.

For WCF RIA services to work properly, you must have the Key attribute defined on the property that represents the unique key for your business object. This is most often the property of the class that stores the primary key value from your underlying table. In this example, it is the CustomerId property.

The Key attribute is defined in System.ComponentModel.DataAnnotations. So you must add a reference to that namespace and import it to use it.

In C#:

using System.ComponentModel.DataAnnotations;

[Key()]
public int CustomerId { get; set; }

In VB:

Imports System.ComponentModel.DataAnnotations

<Key()>
Public Property CustomerId As Integer

STEP 2: Add the Silverlight Project

1) Right-click on the solution and select Add | New Project.

2) Select Silverlight | Silverlight Application.

Be sure to select the template under Visual Basic for VB applications or the template under Visual C# for C# applications.

3) Check Enable WCF RIA Services.

Be sure to check the WCF RIA Services checkbox as shown below.

image

When you add a Silverlight project, Visual Studio creates both a Silverlight project and a Web (ASP.NET) project. The ASP.NET project is crucial because:

  • It provides the startup code to start up your Silverlight project.
  • It provides the WCF service "behind the scenes" to communicate between your Silverlight application and your business objects.

NOTE: If you already have a Silverlight project, you can still enable WCF RIA Services by accessing the Silverlight project properties and defining the WCF RIA Services link as shown below.

image

STEP 3: Set a reference in the ASP.NET project to your business layer component.

Select the ASP.NET project. Then add a reference to your business layer component.

image

The solution for this post has both the C# and VB components. If you are working through this example in your language of choice, you should only have one business layer component project on this list.

By setting this reference, the ASP.NET project code can call your business layer.

STEP 4: Add a Domain Service Class

Add a domain service class to the ASP.NET application. This is the class that calls your business layer methods.

The class must be in the ASP.NET application and not the Silverlight application because you cannot define a direct reference between your Silverlight application and a standard class library project. This makes sense because the Silverlight application runs in the client browser and the ASP.NET application runs on the server where it can access your business layer component.

To add a domain service class, right-click on the ASP.NET application and select Add | New Item. Then pick the Domain Service Class template.

image

This is where this example varies greatly from an Entity Framework example. If you are using Entity Framework, you will get the list of entities here and can set some entity attributes. If you are using your own business objects, you don’t have anything shown in the Entities grid. That’s fine. Just click OK.

Since you are building your own Domain Service Class, Visual Studio will inherit your Domain Service class from DomainService. If you were building the class using Entity Framework, the class would instead inherit from LinqToEntitiesDomainService.

In most cases, you will build one Domain Service Class for each business class that you want to access from your Silverlight project.

STEP 5: Add a Query method to the Domain Service class

The query method in the Domain Service class will in turn call the Retrieve method in the business layer component.

In C#:

namespace ACM.SilverlightCSharp.Web
{
    using System.Collections.Generic;
    using System.ServiceModel.DomainServices.Hosting;
    using System.ServiceModel.DomainServices.Server;
    using ACM.BLCSharp;

    [EnableClientAccess()]
    public class CustomerDomainService : DomainService
    {

        public IEnumerable<Customer> GetCustomers()
        {
            Customer cust = new Customer();
            return cust.Retrieve();
        }

    }
}

In VB:

Imports System.ServiceModel.DomainServices.Hosting
Imports System.ServiceModel.DomainServices.Server
Imports ACM.BLVB

<EnableClientAccess()> _
Public Class CustomerDomainService
    Inherits DomainService

    Public Function GetCustomers() As IEnumerable(Of Customer)
        Dim cust As New Customer
        Return cust.Retrieve
    End Function

End Class

A few notes about this GetCustomers method:

  • It must be a method and not a property.
  • Must return one of the following:
    • A single entity
    • An IEnumerable<T> where T is the entity
    • An IQueryable<T> where T is the entity
  • Can have any parameters.
  • Can have any name.
  • Can (but does not have to) have a Query attribute to define the method as a query.

WCF RIA Services recognizes the method as a query method because of its return value. Therefore, the return value must specify the entity either by returning it or returning an IEnumerable or IQueryable of the entity.

If you build at this point and view all files in your Silverlight project, you will see a Generated_Code folder containing a *.g.cs file. This contains the code generated by Visual Studio and defines the following classes:

  • Entity class (Customer in this example): This class is generated from the business layer and includes all of the entity’s properties.
  • Domain Context class (CustomerDomainContext in this example): This class makes the WCF service calls to the Domain Service class in the ASP.NET project.

In addition, the generated code creates a service contract for the ASP.NET Domain Service class.

[For more information on the generated code, see this post.]

Step 6: Build the UI

Again, keeping with the goal of staying as simple as possible, the UI in this example is a data grid.

1) Open the designer for MainPage.xaml by double-clicking on the file.

2) Open the Data Sources window (Data | Show Data Sources).

Visual Studio automatically added the defined domain context class to your data sources:

image

Simply drag and drop the Customer data source onto the designer and Visual Studio creates the user interface for you:

image

STEP 7: Run

If you run the application, you should get this:

image

This works because when you drag and drop the data source onto the page, Visual Studio defines a DomainDataSource control and a DataGrid control on the page. It sets the DomainDataSource QueryName property to "GetCustomersQuery" and sets the DataGrid ItemsSource property to the DomainDataSource. This causes the code to call your GetCustomers method and use the results to populate the grid.

NOTE: The generated code appends the "Query" suffix to your method name in the domain context class (CustomerDomainContext). That is the method that is called from the UI, which in turn calls the GetCustomers method that you created in the domain service class (CustomerDomainService), which in turn calls the Retrieve method in your business layer via a WCF service.

So your Silverlight application is calling your business layer using the ASP.NET application as a WCF service. Here it is in picture form:

 image

To add a feature to update your data, see this post.

For a more complete example, download the extended source from here.

If you want to learn more about using Silverlight and RIA Services, come and see my talk at VSLive in Redmond Washington August 4, 2010. See this post for more information.

Use these techniques as a starting point when accessing your server-side business layer from a Silverlight application.

Enjoy!

November 25, 2009

Silverlight and RIA: Adding a ComboBox to a DataForm

Filed under: ASP.NET RIA Services,C#,Silverlight,VB.NET @ 5:44 pm

Building a DataForm is quick and easy and is detailed in this prior post. Even customizing it is a breeze as was also shown in that prior post. But now the design calls for a ComboBox. Ready to walk off a cliff?

It is surprisingly difficult to modify a DataForm to display a working ComboBox that is bound to a DomainDataSource. And it is even harder because so many of the available examples use a Fields collection that disappeared in July of 2009.

This post provides the steps for adding a ComboBox control to a DataForm. It retrieves the values for the ComboBox via RIA Services, but you could bind the ComboBox to any collection of data.

The ComboBox used in this example is the one from this prior post. To use it in this example, follow these steps:

1. Add a DomainDataSource for the codes that will populate the ComboBox.

2. Add the ComboBox and bind it to the DomainDataSource.

Before adding the ComboBox to the DataForm, let’s try the above steps adding the ComboBox directly to the UserControl.

In XAML:

<UserControl xmlns:dataFormToolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm.Toolkit"  x:Class="SLVB.CustomerSummaryUC"
    xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
    xmlns:riaControls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Ria.Controls" 
    xmlns:domain="clr-namespace:SLVB.Web">

    <Grid x:Name="LayoutRoot" Background="BlanchedAlmond">
        <riaControls:DomainDataSource x:Name="CustomerSource"
               QueryName="GetCustomers" AutoLoad="True">
            <riaControls:DomainDataSource.DomainContext>
                <domain:CustomerContext/>
            </riaControls:DomainDataSource.DomainContext>
        </riaControls:DomainDataSource>

        <riaControls:DomainDataSource x:Name="CustomerTypeSource" 
               QueryName="GetCustomerTypes" AutoLoad="True">
            <riaControls:DomainDataSource.DomainContext>
                <domain:CodeContext/>
            </riaControls:DomainDataSource.DomainContext>
        </riaControls:DomainDataSource>

        <StackPanel Margin="5,5,200,5">
            <ComboBox ItemsSource=
                 "{Binding Data, ElementName=CustomerTypeSource}"
                  DisplayMemberPath="CodeText"/>
            <dataFormToolkit:DataForm
                  ItemsSource=
                  "{Binding Data, ElementName=CustomerSource}">
            </dataFormToolkit:DataForm>
        </StackPanel>
    </Grid>
</UserControl>

The CustomerTypeSource is the new DomainDataSource. The ComboBox binds to this data source and displays the customer types.

The results are as follows:

image

The ComboBox appears above the DataForm and partially covers it when it is open. So now we know that the ComboBox works. The next step is to insert it instead into the DataForm.

There is no way to change the type of one field on the DataForm without then specifying every field on the DataForm. So if we want to specify that the Customer Type is a ComboBox, we can no longer use the auto field generation and must instead manually define every field.

The DataForm XAML code is then significantly longer.

In XAML:

<StackPanel Margin="5,5,200,5">
    <dataFormToolkit:DataForm
              ItemsSource="{Binding Data, ElementName=CustomerSource}">
        <dataFormToolkit:DataForm.EditTemplate>
            <DataTemplate>
                <StackPanel
                       dataFormToolkit:DataField.IsFieldGroup="True">
                    <dataFormToolkit:DataField>
                        <TextBox Text=
                           "{Binding FirstName, Mode=TwoWay}" />
                    </dataFormToolkit:DataField>
                    <dataFormToolkit:DataField>
                        <TextBox Text=
                           "{Binding LastName, Mode=TwoWay}" />
                    </dataFormToolkit:DataField>
                    <dataFormToolkit:DataField>
                        <ComboBox ItemsSource=
                      "{Binding Data, ElementName=CustomerTypeSource}"
                      DisplayMemberPath="CodeText"/>
                    </dataFormToolkit:DataField>
                    <dataFormToolkit:DataField>
                        <TextBox Text=
                           "{Binding EmailAddress, Mode=TwoWay}" />
                    </dataFormToolkit:DataField>
                </StackPanel>
            </DataTemplate>
        </dataFormToolkit:DataForm.EditTemplate>
    </dataFormToolkit:DataForm>
</StackPanel>

A DataField element defines each field on the form. Within the DataField element is the control to display for that data field. In most case, this is a TextBox control. The Text property of the TextBox is bound to the appropriate field in the data source. The mode is TwoWay to provide review and edit.

So far, the ComboBox code is the same code defined earlier when the ComboBox was above the DataForm. But now it is in the desired location within the DataForm.

Let’s give this a try and see what we have:

image

Well, the ComboBox is there … but it is empty. We simply copied the working ComboBox from above the DataForm to inside the DataForm and now it no longer populates. Hmmm.

After spending several hours Bing’ing about this … I ran across this post and thought it might be relevant. But instead of building a proxy, I thought I would just move the DomainDataSource into a UserControl resource.

So I removed the CustomerTypeSource DomainDataSource from under the Grid element and instead added it to a resources section. I then changed the x:Name to x:Key.

In XAML:

<UserControl.Resources>
    <riaControls:DomainDataSource x:Key="CustomerTypeSource"
                   QueryName="GetCustomerTypes"
                   AutoLoad="True">
        <riaControls:DomainDataSource.DomainContext>
            <domain:CodeContext/>
        </riaControls:DomainDataSource.DomainContext>
    </riaControls:DomainDataSource>
</UserControl.Resources>

This required changing the binding on the ComboBox to use this as a StaticResource. The other change was setting the SelectedItem to the CustomerTypeId.

In XAML:

<ComboBox ItemsSource=
    "{Binding Data, Source={StaticResource CustomerTypeSource}}"
     DisplayMemberPath="CodeText"
     SelectedItem="{Binding CustomerTypeId, Mode=TwoWay}"/>

And the result:

image

Whohoo!!

Now the only problem is that the SelectedItem is not correct. Regardless of the customer type for a Customer, the SelectedValue is always set to the first item on the list. How do we fix this? Yes, another cliff.

The data bound to the ComboBox has both a display member (the CodeText) and a value member (the CodeId). We want to bind the CustomerTypeId property to the Code Id.

Because the ComboBox does not have a ValueMemberPath property, there is no easy way to tell the control that it should map the CustomerTypeId to the CodeId. But there is a hard way using Converters.

The fact that the ComboBox is missing a ValueMemberPath seems like a bug that I hope will be corrected.

But I have to start baking Thanksgiving pies. So more on this in a future post.

Happy Thanksgiving!

Enjoy!

Silverlight and RIA Services: Binding to a DataForm

Filed under: ASP.NET RIA Services,C#,Silverlight,VB.NET @ 4:24 pm

When building line of business (LOB) applications, a common feature is create, review, update, and delete (CRUD) operations on the data in the application. An easy way to provide this feature is using the Silverlight DataForm.

[For an introduction to Silverlight and RIA Services, start here.]

NOTE: The example used here is a continuation of the example in this prior post.

Updating the Domain Service Class

The first step in building a DataForm is to ensure that your Domain Service classes support create, update, and delete operations. The Domain Service class for this example is a CustomerService and resides in the Web project created when the Silverlight project was created.

NOTE: If you don’t add these methods, you will get exceptions when trying to work with the DataGrid such as "Unhandled Error in Silverlight Application Code: 4004" with a message "Editing items is not supported by the IEditableCollection."

In C#:

namespace SLCSharp.Web
{
    using BoCSharp;
    using System.Collections.Generic;
    using System.Web.Ria;
    using System.Web.DomainServices;

    [EnableClientAccess()]
    public class CustomerService : DomainService
    {
        public IEnumerable<Customer> GetCustomers()
        {
            return Customers.Retrieve();
        }

        public void InsertCustomer(Customer currentPerson)
        {
        }

        public void UpdateCustomer(Customer currentPerson)
        {
        }

        public void DeleteCustomer(Customer currentPerson)
        {
        }
    }
}

In VB:

Imports System.Web.DomainServices
Imports System.Web.Ria

<EnableClientAccess()> _
Public Class CustomerService
    Inherits DomainService

    Public Function GetCustomers() As IEnumerable(Of Customer)
        Return Customers.Retrieve()
    End Function

    Public Sub InsertCustomer(ByVal currentPerson As Customer)

    End Sub

    Public Sub UpdateCustomer(ByVal currentPerson As Customer)

    End Sub

    Public Sub DeleteCustomer(ByVal currentPerson As Customer)

    End Sub
End Class

Notice that the Insert, Update, and Delete operations don’t contain any code. The code for these will be provided in a later post. In this post, the focus is on binding to the DataForm and trying out the DataForm features.

Having the methods in place, even though they are empty, allow the in-memory data to be updated and retained. This allows you to fully try out the user interface. But since there is no code within these methods, no changes are retained in the database.

Building the XAML

The XAML for building the DataForm requires two steps:

1) Defining the DomainDataSource for the data.

2) Defining the DataForm.

In XAML:

<UserControl x:Class="SLVB.CustomerSummaryUC"
    xmlns:dataFormToolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm.Toolkit"  
    xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
    xmlns:riaControls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Ria.Controls"
    xmlns:domain="clr-namespace:SLVB.Web">
    <Grid x:Name="LayoutRoot" Background="BlanchedAlmond">
        <riaControls:DomainDataSource x:Name="CustomerSource"
                 QueryName="GetCustomers" AutoLoad="True">
            <riaControls:DomainDataSource.DomainContext>
                <domain:CustomerContext/>
            </riaControls:DomainDataSource.DomainContext>
        </riaControls:DomainDataSource>
        <StackPanel>
            <dataFormToolkit:DataForm
                ItemsSource=
                     "{Binding Data, ElementName=CustomerSource}">
            </dataFormToolkit:DataForm>
        </StackPanel>
    </Grid>
</UserControl>

Using the DomainDataSource requires adding two namespaces and the DataForm requires adding a third namespace to the UserControl:

1) xmlns:riaControls is needed for the RIA DomainDataSource control.

NOTE: You won’t find the DomainDataSource control in the toolbox. You need to type it into the XAML manually.

2) xmlns:domain is the namespace for your Web project that launches your Silverlight application.

3) xmlns:dataFormToolkit contains the DataForm control.

Define a name for the DomainDataSource using the x:Name property. This is the name used in the binding. Also set the QueryName property to the name of the Domain Service class method that gets the data for this data source. In this case, it is the GetCustomers method.

The DomainDataSource also requires a DomainContext. This is where you define the name of the data context used by the data source. If you don’t see an appropriate context using intellisense, try rebuilding your application. Otherwise you can type in the name. It will be the same name as your Domain Service class but replacing "Service" with "Context". Since this example Domain Service class is CustomerService, the context is CustomerContext.

The next set of XAML builds a DataForm inside a StackPanel. The ItemsSource property of the DataForm defines the binding to the name of the DomainDataSource.

The result is as follows:

image

That could look a little nicer. It would be easier to use if the label names were correct words, if there was some help text, and the fields were in a more natural order. And does the user really need to see the CustomerId?

Updating the Business Objects

There are several ways to adjust the layout of the DataForm. The most unique technique is to update the business objects themselves. The idea behind this technique is that as the business objects change over time, they can ensure that the associated UI also changes over time. Regardless of whether you agree with this idea, here’s how you do it.

Simply add attributes to the associated properties.

In C#:

using System.ComponentModel.DataAnnotations;
namespace BoCSharp
{
    public class Customer
    {
        [Display(AutoGenerateField=false)]
        [Key()]
        public int CustomerId { get; set; }

        [Display(Name="Customer Type",
             Description="Select the type of customer",
             Order=3)]
        public int CustomerTypeId { get; set; }

        [Display(Name = "First Name",
             Description = "Enter the customer’s first name",
             Order = 1)]
        public string FirstName { get; set; }

        [Display(Name = "Last Name",
             Description = "Enter the customer’s last name",
             Order = 2)]
        public string LastName { get; set; }

        [Display(Name = "Email",
            Description = "Enter the customer’s primary email address",
            Order = 4)]
        public string EmailAddress { get; set; }

        public Customer()
        {
        }
     }
}

In VB:

Imports System.ComponentModel.DataAnnotations
Public Class Customer

    Private _CustomerId As Integer
    <Display(AutoGenerateField:=False)> _
    <Key()> _
    Public Property CustomerId() As Integer
        Get
            Return _CustomerId
        End Get
        Set(ByVal value As Integer)
            _CustomerId = value
        End Set
    End Property

    Private _CustomerTypeId As Integer
    <Display(Name:="Customer Type", _
             Description:="Select the type of customer", _
             Order:=3)> _
    Public Property CustomerTypeId() As Integer
        Get
            Return _CustomerTypeId
        End Get
        Set(ByVal value As Integer)
            _CustomerTypeId = value
        End Set
    End Property

    Private _FirstName As String
    <Display(Name:="First Name", _
             Description:="Enter the customer’s first name", _
             Order:=1)> _
    Public Property FirstName() As String
        Get
            Return _FirstName
        End Get
        Set(ByVal value As String)
            _FirstName = value
        End Set
    End Property

    Private _LastName As String
    <Display(Name:="Last Name", _
             Description:="Enter the customer’s last name", _
             Order:=2)> _
    Public Property LastName() As String
        Get
            Return _LastName
        End Get
        Set(ByVal value As String)
            _LastName = value
        End Set
    End Property

    Private _EmailAddress As String
    <Display(Name:="Email", _
           Description:="Enter the customer’s primary email address", _
           Order:=4)> _
    Public Property EmailAddress() As String
        Get
            Return _EmailAddress
        End Get
        Set(ByVal value As String)
            _EmailAddress = value
        End Set
    End Property
End Class

Notice that each property now has a Display attribute. The key named parameters within this attribute are:

  • AutoGenerateField: Set to false, this will hide the field so it does not appear on the data form. If not set, the value is true.
  • Description: This is the text that will appear as a tooltip (see screen shot below). If not set, no tooltip appears.
  • Name: This is the name that will appear as the label for the field. If not set, the field name is used.
  • Order: This defines the order of the fields on the data form.

The result is as follows:

image

MUCH nicer!

NOTE: I also added a Margin ="5,5,200,5" to the StackPanel to ensure there was space to the right to see the information tooltip. No other changes were made to the XAML.

NOTE: When I made the changes to the business object and ran the application, the DataForm layout did not change. I recompiled the application and ran again before I saw the changes.

The only thing left to do is change the Customer Type text field to a ComboBox. But amazing, that is a significant amount of work. Better left for a future post.

Enjoy!

Silverlight and RIA Services: Managing Your Codes

Filed under: ASP.NET RIA Services,C#,Silverlight,VB.NET @ 1:15 pm

Every application has some type of codes: customer types, reason codes, states, and so on. Sometimes these codes can be hard-coded in an application as Enum values. But other times these codes are more readily stored in a table. This post details how to work with code tables through RIA Services.

[For details on how to work with Enum values through RIA Services, see this prior post.]

NOTE: This example continues from this prior post that introduces RIA Services and your business objects.

Building the Business Layer Classes

To manage the codes in your application without having to build a class for each type of code, you can build a more generalized Code class in your business layer. This class is added to the project containing the Customer and Customers classes from this prior post.

In C#:

using System.ComponentModel.DataAnnotations;

namespace BoCSharp
{
    public class Code
    {
        [Key()]
        public int CodeId { get; set; }
        public string CodeText { get; set; }
    }
}

In VB:

Imports System.ComponentModel.DataAnnotations

Public Class Code

    Private _CodeId As Integer
    <Key()> _
    Public Property CodeId() As Integer
        Get
            Return _CodeId
        End Get
        Set(ByVal value As Integer)
            _CodeId = value
        End Set
    End Property

    Private _CodeText As String
    Public Property CodeText() As String
        Get
            Return _CodeText
        End Get
        Set(ByVal value As String)
            _CodeText = value
        End Set
    End Property
End Class

Each code has an Id and a text value. The Key() attribute on the CodeId ensures that this class is set up to use RIA Services.

A separate class in the business layer then tracks the sets of all codes.

In C#:

using System.Collections.Generic;

namespace BoCSharp
{
    public class Codes
    {
        public static List<Code> RetrieveCustomerTypes()
        {
            List<Code> custTypeList = new List<Code>
                {new Code()
                    {CodeId=1,
                     CodeText="Individual"},
                new Code()
                    {CodeId=2,
                     CodeText="Corporate"},
                new Code()
                    {CodeId=3,
                     CodeText="Government"},
                new Code()
                    {CodeId=4,
                     CodeText="Education"}};
            return custTypeList;
        }
    }
}

In VB:

Public Class Codes

    Public Shared Function RetrieveCustomerTypes() As List(Of Code)
        Dim custTypeList As New List(Of Code)
        custTypeList.Add(New Code With { _
                         .CodeId = 1, _
                         .CodeText = "Individual"})
        custTypeList.Add(New Code With { _
                         .CodeId = 2, _
                         .CodeText = "Corporate"})
        custTypeList.Add(New Code With { _
                         .CodeId = 3, _
                         .CodeText = "Government"})
        custTypeList.Add(New Code With { _
                         .CodeId = 4, _
                         .CodeText = "Education"})
        Return custTypeList
    End Function
End Class

Two things to note about this class:

  1. It will have a Retrieve method for each kind of code (customer types, reason codes, states, and so on).
  2. In the "real" application, it will get these values from the database instead of hard-coded values. (Hard-coded values are used here so you don’t have to set up a database to try these techniques.)

Finally, let’s modify the Customer class to add the customer type property. Each customer has a customer type.

In C#:

using System.ComponentModel.DataAnnotations;
namespace BoCSharp
{
    public class Customer
    {
        [Key()]
        public int CustomerId { get; set; }
        public int CustomerTypeId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string EmailAddress { get; set; }

        public Customer()
        {
        }
     }
}

In VB:

Imports System.ComponentModel.DataAnnotations
Public Class Customer

    Private _CustomerId As Integer
    <Key()> _
    Public Property CustomerId() As Integer
        Get
            Return _CustomerId
        End Get
        Set(ByVal value As Integer)
            _CustomerId = value
        End Set
    End Property

    Private _CustomerTypeId As Integer
    Public Property CustomerTypeId() As Integer
        Get
            Return _CustomerTypeId
        End Get
        Set(ByVal value As Integer)
            _CustomerTypeId = value
        End Set
    End Property

    Private _FirstName As String
    Public Property FirstName() As String
        Get
            Return _FirstName
        End Get
        Set(ByVal value As String)
            _FirstName = value
        End Set
    End Property

    Private _LastName As String
    Public Property LastName() As String
        Get
            Return _LastName
        End Get
        Set(ByVal value As String)
            _LastName = value
        End Set
    End Property

    Private _EmailAddress As String
    Public Property EmailAddress() As String
        Get
            Return _EmailAddress
        End Get
        Set(ByVal value As String)
            _EmailAddress = value
        End Set
    End Property
End Class

Though only the CustomerTypeId is new, all of the code from the Customer class (originally defined in this prior post) is shown above.

Building the Domain Service Class

The next step is to build a Domain Service class for the Codes class so that the Silverlight project can access the codes.

In C#:

namespace SLCSharp.Web
{
    using BoCSharp;
    using System.Collections.Generic;
    using System.Web.Ria;
    using System.Web.DomainServices; 

    [EnableClientAccess()]
    public class CodeService : DomainService
    {
        public IEnumerable<Code> GetCustomerTypes()
        {
            return Codes.RetrieveCustomerTypes();
        }
    }
}

In VB:

Imports System.Web.DomainServices
Imports System.Web.Ria

<EnableClientAccess()> _
Public Class CodeService
    Inherits DomainService

    Public Function GetCustomerTypes() As IEnumerable(Of Code)
        Return Codes.RetrieveCustomerTypes()
    End Function
End Class

This class will have a function to expose each type of code. For now, it provides a function to get the customer types.

Accessing the Codes from Silverlight

One of the common ways to use the codes in Silverlight is to present them in a ComboBox. This involves two steps:

  1. Add a DomainDataSource that accesses the Domain Service class.
  2. Add a ComboBox that binds to the DomainDataSource.

In XAML:

<UserControl x:Class="SLVB.CodesExampleUC"
    xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:riaControls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Ria.Controls"
    xmlns:domain="clr-namespace:SLVB.Web"
    Width="400" Height="300">
    <Grid x:Name="LayoutRoot" Background="White">
        <riaControls:DomainDataSource x:Name="CustomerTypeSource"
                    QueryName="GetCustomerTypes" AutoLoad="True">
            <riaControls:DomainDataSource.DomainContext>
                <domain:CodeContext/>
            </riaControls:DomainDataSource.DomainContext>
        </riaControls:DomainDataSource>
        <StackPanel Margin="10">
            <ComboBox ItemsSource=
                 "{Binding Data, ElementName=CustomerTypeSource}"
                 DisplayMemberPath="CodeText"/>
        </StackPanel>
    </Grid>
</UserControl>

Using the DomainDataSource requires adding two namespaces to the UserControl:

1) xmlns:riaControls is needed for the RIA DomainDataSource control.

NOTE: You won’t find the DomainDataSource control in the toolbox. You need to type it into the XAML manually.

2) xmlns:domain is the namespace for your Web project that launches your Silverlight application.

Define a name for the DomainDataSource using the x:Name property. This is the name used in the binding. Also set the QueryName property to the name of the Domain Service class method that gets the data for this data source. In this case, it is the GetCustomerTypes method.

The DomainDataSource also requires a DomainContext. This is where you define the name of the data context used by the data source. If you don’t see an appropriate context using intellisense, try rebuilding your application. Otherwise you can type in the name. It will be the same name as your Domain Service class but replacing "Service" with "Context". Since this example Domain Service class is CodeService, the context is CodeContext.

The next set of XAML builds a ComboBox inside a StackPanel. The ItemsSource property of the ComboBox defines the binding to the name of the DomainDataSource. The DisplayMemberPath property is set to the name of the class property to display in the ComboBox. In this case, the ComboBox displays the CodeText property.

The result is as follows:

image

Use this technique to access your code tables from your Silverlight application.

Enjoy!

November 10, 2009

Silverlight and RIA: Enum Properties

Filed under: ASP.NET RIA Services,C#,Silverlight,VB.NET @ 2:58 pm

I mentioned in this prior post that RIA Services did not know how to handle properties that have an Enum data type. The problem is not really that RIA Services does not know how to handle the properties; rather it does not have any knowledge of the Enum itself.

Take this example.

In C#:

public enum CustomerTypeOption
{
    Consumer,
    Corporation,
    Education,
    Government
}

private CustomerTypeOption _CustomerType;
public CustomerTypeOption CustomerType {
    get { return _CustomerType; }
    set { _CustomerType = value; }
}

In VB:

Public Enum CustomerTypeOption
    Consumer
    Corporation
    Education
    Government
End Enum

Private _CustomerType As CustomerTypeOption
Public Property CustomerType() As CustomerTypeOption
    Get
        Return _CustomerType
    End Get
    Set(ByVal value As CustomerTypeOption)
        _CustomerType = value
    End Set
End Property

This code resides in a Customer class like the one shown in this prior post. If you create a CustomerService domain services class, Silverlight will generate a proxy class containing all of the Customer class properties. However, the generated code contains a syntax error because it does not understand the CustomerTypeOption type.

This problem can be solved by sharing the Enum with Silverlight following the technique detailed in this prior post and shown below.

image

Let’s go through the steps in detail:

1. Add a new file to your business object component and call it Customer.shared.vb (if your component is VB.NET) or Customer.shared.cs (if you are using C#).

2. Add the partial keyword to the class name in the file as shown in the code below.

3. Cut the Enum definition from the Customer class and paste it into this partial class.

In C#:

public partial class Customer
{
    public enum CustomerTypeOption
    {
        Consumer,
        Corporation,
        Education,
        Government
    }
}

In VB:

Partial Public Class Customer

    Public Enum CustomerTypeOption
        Consumer
        Corporation
        Education
        Government
    End Enum

End Class

4. Right click on the Web project linked to your Silverlight project and select Add | Existing Item.

5. In the Add Existing Item dialog, select the file and select Add As Link from the Add button drop down menu.

image

This links the file between your business object class library project and the Web project linked to your Silverlight project.

6. Build the solution.

Silverlight should now have a copy of the Customer.shared.vb or Customer.shared.cs file under the Generated_Code node. Since the Enum is now defined in the Silverlight project, your CustomerType property in the generated code should compile without errors.

Use this technique any time you want to share an Enum between your business object class library project and your Silverlight project.

Enjoy!

« Previous PageNext Page »

© 2019 Deborah's Developer MindScape   Provided by WPMU DEV -The WordPress Experts   Hosted by Microsoft MVPs