Silverlight: WCF RIA Services

WCF RIA Services simplifies the development of n-tier solutions for Rich Internet Applications (RIA), such as Silverlight applications. A common problem when developing an n-tier RIA solution is coordinating application logic between the middle tier and the presentation tier. To create the best user experience, you want your RIA client to be aware of the application logic that resides on the server, but you do not want to develop and maintain the application logic on both the presentation tier and the middle tier. RIA Services solves this problem by providing framework of components, tools, and services that make the application logic on the server available to the RIA client without requiring you to manually duplicate that programming logic. You can create a RIA client that is aware of business rules and know that the client is automatically updated with the latest middle tier logic every time that the solution is re-compiled.

The following illustration shows a simplified version of an n-tier application. RIA Services focuses on the box between the presentation tier and the data access layer (DAL) to facilitate n-tier development with a RIA client.

n-tier RIA application

RIA Services adds tools to Visual Studio that enable linking client and server projects in a single solution and generating code for the client project from the middle-tier code. The framework components support prescriptive patterns for writing application logic so that it can be reused on the presentation tier. Services for common scenarios, such as authentication and user settings management, are provided to reduce development time.
In RIA Services, you expose data from the server project to client project by adding domain services. The RIA Services framework implements each domain service as a Windows Communication Foundation (WCF) service. Therefore, you can apply the concepts you know from WCF services to domain services when customizing the configuration.

Let's create one Silverlight Business Application. Silverlight Business Application is template available if RIA Services is installed.

Silverlight Business Application

Remember we have not checked the checkbox for "Enable .NET RIA Services", now we need to check it for enabling .NET RIA Services.

Enable .NET RIA Services

The Business Application comes with the following solution structure, where we can find different folders for different functionalities.

solution structure

Now we can take any one of the above described methods for Data Access such as LINQ to SQL Classes or ADO.NET Entity Data Model. Let's take an ADO.NET Entity Data Model as our Data Access method.

ADO.NET Entity Data Model

After creating the model, let's see how we can add RIA functionality to Silverlight project.

Domain Service Class

Domain services are WCF services that expose the data access layer to the client project. When you create an instance of a domain service, you specify the entity classes that you want to expose and the data operations that are permitted through the domain service.

DomainService and Derived Classes

The DomainService class is the base class for all classes that serve as domain services. WCF RIA Services also provides the LinqToEntitiesDomainService<(Of <(<'TContext>)>)> class, which is an abstract class that derives from DomainService. The LinqToSqlDomainService<(Of <(<'TContext>)>)> class is available in the RIA Services Toolkit.

To create a domain service that binds to an ADO.NET Entity model, you create a class that derives from LinqToEntitiesDomainService<(Of <(<'TContext>)>)>. To create a domain service that binds to a custom data object, you create a class that derives from DomainService. When you use the Add New Domain Service Class dialog box to create a domain service, the correct type of domain service based on the entities you expose is automatically created.

A domain service class must be marked with the EnableClientAccessAttribute attribute to make the service available to the client project.

WCF and Domain Services

As a WCF service, the domain service builds upon WCF concepts. The domain service preserves the following:

  • Standard usage of WCF services.
  • Existing WCF programming models constructs, such as operation contracts, operation behaviors, and service behaviors.
  • Standard WCF customization capabilities, such as binding configuration, behavior configuration, and management infrastructure.

The domain context communicates with the domain service by using the WCF ChannelFactory to create a channel and passing to it a service contract that was generated from the domain service.

By default, only the Binary endpoint is enabled for domain services.

Let's add a Domain Service Class to the Web Project.

Domain Service

After pressing Add button you would be notified to add entity to the Domain Service Class.

add entity to the Domain Service

Remember if you are not getting any Entity Frameworks listed, then just press cancel and rebuild the Web Project. Follow the above steps again.
By Checking the checkboxes we are enabling Client Access and by Exposing OData endpoint to share data over the web. OData is an emerging set of extensions for the ATOM protocol. By checking the option the Domain Service would will be exposed as OData feed.

We can see that along with the EmployeeDomainService.cs we have another CS file auto generated.

CS file in WCF

As the filename specifies metadata.cs, it consists of the Entity properties. The following listing shows the details of it.

namespace RIASilverlight4Sample01.Web
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.ComponentModel.DataAnnotations;
    using System.Linq;
    using System.Web.DomainServices;
    using System.Web.Ria;
    using System.Web.Ria.Services;

    // The MetadataTypeAttribute identifies EmployeeMetadata as the class
    // that carries additional metadata for the Employee class.
    [MetadataTypeAttribute(typeof(Employee.EmployeeMetadata))]
    public partial class Employee
    {

        // This class allows you to attach custom attributes to properties
        // of the Employee class.
        //
        // For example, the following marks the Xyz property as a
        // required field and specifies the format for valid values:
        //    [Required]
        //    [RegularExpression("[A-Z][A-Za-z0-9]*")]
        //    [StringLength(32)]
        //    public string Xyz;
        internal sealed class EmployeeMetadata
        {
 
            // Metadata classes are not meant to be instantiated.
            private EmployeeMetadata()
            {
            }

            public string Contact;

            public string EmailID;

            public string FirstName;

            public long ID;

            public string LastName;
        }
    }
}

Listing

As you see in the listing above, we have an internal sealed class that consists of the properties auto generated.

We can set attributes for specific properties for validation purposes; we will see this in later descriptions.

Now if you look at the EmployeeDomainService class it has all the CRUD operation methods auto generated. See following listing.

namespace RIASilverlight4Sample01.Web
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.ComponentModel.DataAnnotations;
    using System.Data;
    using System.Linq;
    using System.Web.DomainServices;
    using System.Web.DomainServices.Providers;
    using System.Web.Ria;
    using System.Web.Ria.Services;

    // Implements application logic using the EmployeeDBEntities context.
    // TODO: Add your application logic to these methods or in additional methods.
    // TODO: Wire up authentication (Windows/ASP.NET Forms) and uncomment the following to disable anonymous access
    // Also consider adding roles to restrict access as appropriate.
    // [RequiresAuthentication]
    [EnableClientAccess()]
    public class EmployeeDomainService : LinqToEntitiesDomainService<EmployeeDBEntities>
    {

        // TODO: Consider
        // 1. Adding parameters to this method and constraining returned results, and/or
       
// 2. Adding query methods taking different parameters.

       [Query(IsDefault = true)]
        public IQueryable<Employee> GetEmployees()
        {
            return this.ObjectContext.Employees;
        }

        public void InsertEmployee(Employee employee)
        {
            if ((employee.EntityState != EntityState.Added))
            {
                if ((employee.EntityState != EntityState.Detached))
                {
                    this.ObjectContext.ObjectStateManager.ChangeObjectState(employee, EntityState.Added);
                }
                else
                {
                    this.ObjectContext.AddToEmployees(employee);
                }
            }
        }

        public void UpdateEmployee(Employee currentEmployee)
        {
            if ((currentEmployee.EntityState == EntityState.Detached))
            {
                this.ObjectContext.AttachAsModified(currentEmployee, this.ChangeSet.GetOriginal(currentEmployee));
            }
        }

        public void DeleteEmployee(Employee employee)
        {
            if ((employee.EntityState == EntityState.Detached))
            {
                this.ObjectContext.Attach(employee);
            }
            this.ObjectContext.DeleteObject(employee);
        }
    }
}

Listing

The listing above displays the methods that are auto generated, such as: GetEmployees(), InsertEmployee(), UpdateEmployee(), and DeleteEmployee().

The GetEmployees method displays all data available for the Employee entity. We can add some code to filter it, or we can even sort it.

public IQueryable<Employee> GetEmployees()
{

return this.ObjectContext.Employees.OrderBy(e => e.FirstName);
}


Listing

Data Operations

You add methods to a domain service that perform the data operation you want to expose. For example, you can add methods that perform the following operations:

  • Query

  • Update

  • Insert

  • Delete


In addition, you can add the following more complicated operations:

  • Invoke - operations that need to be executed without tracking or deferred execution.

  • Named Update - custom operations that do not fall into simple modification operations.


Conventions

When you add methods to perform these operations, the method must match the expected signature for that operation. In addition to matching the signature, the method must include a name prefix that matches the naming convention for that data operation. If the name of the method does not start with the expected prefix, you must apply the corresponding attribute for that operation. The attribute is optional if the name of the operation matches the naming convention. Using the naming convention provides a more consistent experience for developers.

You cannot overload methods that are domain operations. You must specify a unique name for each method that can be called from the client project. All methods representing domain service operations must be public. The methods must use serializable types for parameters and return types.

You can prevent a method from being exposed by adding the IgnoreAttribute attribute to the method.
The data operation signatures are provided in the following tables.

Query

The query method in the domain context typically has the same name as the domain service query method plus a postfix of Query. For example, a GetEmployeesQuery method in the domain context is generated from a GetEmployees method in the domain service. The domain context query method returns an EntityQuery object that you can use to apply additional operations.

All queries from a domain context are executed asynchronously. To execute the query, you pass the EntityQuery object as a parameter in the Load method.

 

Return value

IEnumerable<T>,IQueryable<T>, or entity

Parameters

Any number

Name Prefix

Any name

Attribute

[Query] (C#)

Example

public IQueryable<Product> GetProducts() (C#)


Update

When the domain service includes methods for updating, inserting, and deleting entities, those methods are not generated in the domain context. Instead, you use the SubmitChanges method on the domain context and the proper operations on the domain service are called. No changes in the data source are made until you call SubmitChanges. You can cancel pending changes by calling the RejectChanges method.

The DomainContext class also provides the HasChanges and EntityContainer properties to enable you to evaluate pending changes. The EntityContainer object for a domain context tracks the pending changes. Pending changes does not include calls to invoke operations in the domain service because invoke operations are executed immediately when they are called. When you call SubmitChanges, all pending changes are sent to the domain service together.
 

Return value

None

Parameters

Entity

Name Prefix

Update, Change, or Modify

Attribute

[Update] (C#)

Example

public void UpdateProduct(Product product) (C#)


Insert

The expected signature values for an insert operation.
 

Return value

None

Parameters

Entity

Name Prefix

Insert, Add, or Create

Attribute

[Insert] (C#)

Example

public void InsertProduct(Product product) (C#)


Delete

The expected signature values for a delete operation.
 

Return value

None

Parameters

Entity

Name Prefix

Delete or Remove

Attribute

[Delete] (C#)

Example

public void DeleteProduct(Product product) (C#)


Invoke

The expected signature values for an invoke operation.

The domain context will contain a method for each service operation on the domain service. Unlike domain operations, service operations are executed immediately. You do not call the SubmitChanges method. Service operations are executed asynchronously. The service operation returns an InvokeOperation object. You retrieve the value of the Value property to get the returned value from the service operation.

 

Return value

Any

Parameters

Any

Name Prefix

Any

Attribute

[Invoke] (C#)

Example

[Invoke]

public decimal GetCompetitorsPrice(Product product) (C#)


Named Update

The expected signature values for a named update operation.
 

Parameters

Entity any number of other parameters

Name Prefix

Any name other than one starting with the prefixes for Insert, Update, or Delete

Attribute

[Update(UsingCustomMethod=true] (C#)

Example

[Update(UsingCustomMethod=true]

public void DiscountProduct(Product product, int percentage) (C#)


As you see in previous listing we have given an OrderBy method where all the Employees would be ordered by itsit"s FirstName property. By default Visual Studio makes read operation as the default query.

In InsertEmployee method you can see an Employee entity is being passed, and based on the EntityState the data would be inserted.

public void InsertEmployee(Employee employee)
        {
            if ((employee.EntityState != EntityState.Added))
            {
                if ((employee.EntityState != EntityState.Detached))
                {
                    this.ObjectContext.ObjectStateManager.ChangeObjectState(employee, EntityState.Added);
                }
                else
                {
                    this.ObjectContext.AddToEmployees(employee);
                }
            }
        }

Listing

In UpdateEmployee method using ObjectContext's AttachAsModified method the particular Employee could can be updated.

public void UpdateEmployee(Employee currentEmployee)
{
if ((currentEmployee.EntityState == EntityState.Detached))
{

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

}
}


Listing

In DeleteEmployee method, ObjectContext's DeleteObject method would delete a particular Employee in respect to the EntityState.

public void DeleteEmployee(Employee employee)
{
if ((employee.EntityState == EntityState.Detached))
{
this.ObjectContext.Attach(employee);
}

this.ObjectContext.DeleteObject(employee);

}


Listing

The web.config file is updated with the addition of the Domain Service Class to the project.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <
configSections>
    <
sectionGroup name="system.serviceModel">
      <section name="domainServices"

 type="System.ServiceModel.DomainServices.Hosting.DomainServicesSection, System.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"

allowDefinition="MachineToApplication"

requirePermission="false" />

    </sectionGroup>
  </
configSections>
  <
system.web>
    <
httpModules>
      <
add name="DomainServiceModule"

    type="System.ServiceModel.DomainServices.Hosting.DomainServiceHttpModule, System.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />

    </httpModules>
    <
compilation debug="true" targetFramework="4.0">
      <assemblies>
        <
add assembly="System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
      </assemblies>
    </
compilation>
    <
roleManager enabled="true" />
    <authentication mode="Forms">
      <forms name=".BusinessApplication02_ASPXAUTH" />
    </authentication>
    <
profile>
      <
properties>
        <
add name="FriendlyName" />
      </properties>
    </
profile>
  </
system.web>
  <
system.webServer>
    <
validation validateIntegratedModeConfiguration="false" />
    <modules runAllManagedModulesForAllRequests="true">
      <add name="DomainServiceModule"
    preCondition="managedHandler"
    type="System.ServiceModel.DomainServices.Hosting.DomainServiceHttpModule, System.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
    </modules>
  </
system.webServer>
  <
system.serviceModel>
    <
domainServices>
      <
endpoints>
        <
add name="OData"
      type="System.ServiceModel.DomainServices.Hosting.ODataEndpointFactory, System.ServiceModel.DomainServices.Hosting.OData, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
      </endpoints>
    </
domainServices>
    <
serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  <
connectionStrings>
    <
add name="EmployeeDBEntities"

connectionString="metadata=res://*/Models.EmployeeModel.csdl|res://*/Models.EmployeeModel.ssdl|res://*/Models.EmployeeModel.msl;
provider=System.Data.SqlClient;provider connection string=
&quot;Data Source=B314LTRV\SQLEXPRESS;Initial Catalog=EmployeeDB;Integrated Security=True;MultipleActiveResultSets=True&quot;" providerName="System.Data.EntityClient"
/>

  </connectionStrings>
</
configuration>

Listing

Let's go to the Silverlight project and add our custom page to display Employee data.

custom page in silverlight

Remember to add all the Silverlight pages into View folder.

Silverlight pages

We need to set the navigation to our newly added Employee.xaml page in MainPage.xaml

<UserControl
  x:Class="RIASilverlight4Sample01.MainPage"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
  xmlns:uriMapper="clr-namespace:System.Windows.Navigation;assembly=System.Windows.Controls.Navigation"
  xmlns:dataControls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm.Toolkit"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns
:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

  mc:Ignorable="d"

d:DesignWidth="640"

d:DesignHeight="480">

    <Grid x:Name="LayoutRoot" Style="{StaticResource LayoutRootGridStyle}">

         <Border x:Name="ContentBorder" Style="{StaticResource ContentBorderStyle}">
            <navigation:Frame x:Name="ContentFrame"

Style="{StaticResource ContentFrameStyle}"

Source="/Home" Navigated="ContentFrame_Navigated" NavigationFailed="ContentFrame_NavigationFailed">

                <navigation:Frame.UriMapper>
                    <uriMapper:UriMapper>
                        <uriMapper:UriMapping Uri="" MappedUri="/Views/Home.xaml"/>
                        <uriMapper:UriMapping Uri="/{pageName}"

     MappedUri="/Views/{pageName}.xaml"/>

                    </uriMapper:UriMapper>
                </navigation:Frame.UriMapper>
            </navigation:Frame>
        </Border>

        <Grid Style="{StaticResource NavigationOuterGridStyle}">
            <Grid x:Name="NavigationGrid"

    Style="{StaticResource NavigationGridStyle}">

                <Border x:Name="BrandingBorder"

   Style="{StaticResource BrandingBorderStyle}">
                    <StackPanel x:Name="BrandingStackPanel"

    Style="{StaticResource BrandingStackPanelStyle}">

                        <ContentControl Style="{StaticResource LogoIcon}"/>
                        <TextBlock x:Name="ApplicationNameTextBlock"

 Style="{StaticResource ApplicationNameStyle}"                                    Text="{Binding
ApplicationStrings.ApplicationName, Source={StaticResource ResourceWrapper
}}"/>
                    </StackPanel>
                </Border> 

                <Border x:Name="LinksBorder"

   Style="{StaticResource LinksBorderStyle}">

                    <StackPanel x:Name="LinksStackPanel"

    Style="{StaticResource LinksStackPanelStyle}">

                        <HyperlinkButton x:Name="Link1"

     Style="{StaticResource LinkStyle}"
                                     NavigateUri="/Home"

TargetName="ContentFrame" Content="{Binding Path=ApplicationStrings.HomePageTitle, Source={StaticResource ResourceWrapper}}"/>

                        <Rectangle x:Name="Divider1"

Style="{StaticResource DividerStyle}"/>

<HyperlinkButton x:Name="Link2"

Style="{StaticResource LinkStyle}"

NavigateUri="/Employee"

TargetName="ContentFrame"

Content="Employee Details"/>

                        <Rectangle x:Name="Divider2"

Style="{StaticResource DividerStyle}"/>

                        <HyperlinkButton x:Name="Link3"

Style="{StaticResource LinkStyle}"
                                         NavigateUri="/About"

TargetName="ContentFrame" Content="{Binding Path=ApplicationStrings.AboutPageTitle, Source={StaticResource ResourceWrapper}}"/>

                    </StackPanel>
                </Border>

            </Grid>

            <Border x:Name="loginContainer"

Style="{StaticResource LoginContainerStyle}">

                <!-- LoginStatus will be added here in code behind. This is required for the designer view to work -->
            </Border>
        </Grid>

    </Grid>

</UserControl>

Listing

In our Employee Page let's add a DataGrid and customize it as we did previously.

<navigation:Page
           xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
           xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
           xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
           mc:Ignorable="d"
           xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
           xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data" x:Class="RIASilverlight4Sample01.Views.Employee"

Style="{StaticResource PageStyle}"

Title="Employee Details"

d:DesignHeight="480"

d:DesignWidth="640">

  <Grid x:Name="LayoutRoot">

        <Grid.RowDefinitions>

            <RowDefinition Height="149*" />

            <RowDefinition Height="331*" />

        </Grid.RowDefinitions>

        <data:DataGrid x:Name="dgData"

IsReadOnly="True"

AutoGenerateColumns="False">

      <data:DataGrid.Columns>

        <data:DataGridTextColumn Binding="{Binding ID}"

Header="ID"/>

        <data:DataGridTextColumn Binding="{Binding FirstName}"

Header="First Name"/>

        <data:DataGridTextColumn Binding="{Binding LastName}"

Header="Last Name"/>

        <data:DataGridTextColumn Binding="{Binding EmailID}"

Header="Email ID" Width="*"/>

        <data:DataGridTextColumn Binding="{Binding Contact}"

Header="Contact"/>

      </data:DataGrid.Columns>
    </
data:DataGrid>

  </Grid>
</
navigation:Page>

Listing

Now we will see some good stuff that is available for easy design and implementing data with controls associated.

Listing

After clicking on the above option we will see the following pane. If you do not have any Domain Service Class then the list would be empty, for some reason if you have one but still you don't get it; you need to rebuild the solution once again. Remember, this pane is only displayed when a design view is opened (XAML view or Design view).

XAML view in silverlight

As you see in the figure above, we have EmployeeDomainContext available to us after we have added the Domain Service Class with respect to Entity Model.

Data Sources are used for binding controls with entity; so that the design time can be reduced. As you see in the figure below, we have Employee as an entity and it is preceded with the image of the DataGrid and the properties are preceded with TextBox symbol.

DataGrid entity in silverlight

That's right, the following controls would be associated once you drag and drop into the design pane.

Data Sources in silverlight

As soon as the DataGrid is added from Data Sources pane the Domain Context would will be added in XAML behind.

<riaControls:DomainDataSource
AutoLoad
="True"
d
:DesignData="{d:DesignInstance my:Employee, CreateList=true}"
Height
="0"
LoadedData
="employeeDomainDataSource_LoadedData"
Name
="employeeDomainDataSource"
QueryName
="GetEmployeesQuery"
Width
="0">
            <riaControls:DomainDataSource.DomainContext>
                <my1:EmployeeDomainContext />
            </riaControls:DomainDataSource.DomainContext>
</
riaControls:DomainDataSource>

Listing

The following is the structure of the DataGrid in XAML after it is added from DataSource. Visual Studio will do automatic binding with each column type.

<sdk:DataGrid AutoGenerateColumns="False"

Height="200"

HorizontalAlignment="Left"

ItemsSource="{Binding ElementName=employeeDomainDataSource, Path=Data}"

Margin="111,0,0,0"

Name="employeeDataGrid"

RowDetailsVisibilityMode="VisibleWhenSelected"

VerticalAlignment="Top"

Width="400">

            <sdk:DataGrid.Columns>

                <sdk:DataGridTextColumn x:Name="contactColumn"

      Binding="{Binding Path=Contact}"

      Header="Contact"

      Width="SizeToHeader" />

                <sdk:DataGridTextColumn x:Name="emailIDColumn"

      Binding="{Binding Path=EmailID}"

                           Header="Email ID"

Width="SizeToHeader" />

                <sdk:DataGridTextColumn x:Name="firstNameColumn"

Binding="{Binding Path=FirstName}"

Header="First Name"

Width="SizeToHeader" />

                <sdk:DataGridTextColumn x:Name="iDColumn"

Binding="{Binding Path=ID}"

Header="ID"

Width="SizeToHeader" />

                <sdk:DataGridTextColumn x:Name="lastNameColumn"

Binding="{Binding Path=LastName}"

Header="Last Name"

Width="SizeToHeader" />

            </sdk:DataGrid.Columns>
        </sdk:DataGrid>


Listing

We have other options other than DataGrid, such as Details and Customize.

DataGrid option

If the Details view is selected and dropped on to the designer surface, we will get all the controls in a Grid Panel and the default control associated with it is the TextBox control.

controls in a Grid Panel

<Grid DataContext="{Binding ElementName=employeeDomainDataSource, Path=Data}"

HorizontalAlignment="Left"

Margin="168,266,0,0"

Name="grid1"

VerticalAlignment="Top">

            <Grid.ColumnDefinitions>

                <ColumnDefinition Width="Auto" />

                <ColumnDefinition Width="Auto" />

            </Grid.ColumnDefinitions>

            <Grid.RowDefinitions>

                <RowDefinition Height="Auto" />

                <RowDefinition Height="Auto" />

                <RowDefinition Height="Auto" />

                <RowDefinition Height="Auto" />

                <RowDefinition Height="Auto" />

            </Grid.RowDefinitions>

            <sdk:Label Content="Contact:"

Grid.Column="0" Grid.Row="0"

HorizontalAlignment="Left"

Margin="3"

VerticalAlignment="Center" />

            <TextBox Grid.Column="1"

Grid.Row="0" Height="23"

HorizontalAlignment="Left"

Margin="3"

Name="contactTextBox"

Text="{Binding Path=Contact, Mode=TwoWay, NotifyOnValidationError=true, TargetNullValue=''}" VerticalAlignment="Center" Width="120" />

            <sdk:Label Content="Email ID:"

  Grid.Column="0" Grid.Row="1"

  HorizontalAlignment="Left" Margin="3"

  VerticalAlignment="Center" />

            <TextBox Grid.Column="1" Grid.Row="1"

 Height="23"

HorizontalAlignment="Left"

Margin="3"

Name="emailIDTextBox"

Text="{Binding Path=EmailID, Mode=TwoWay, NotifyOnValidationError=true, TargetNullValue=''}" VerticalAlignment="Center"

Width="120" />

            <sdk:Label Content="First Name:"

Grid.Column="0" Grid.Row="2"

HorizontalAlignment="Left"

Margin="3"

VerticalAlignment="Center" />

            <TextBox Grid.Column="1" Grid.Row="2"

Height="23"

HorizontalAlignment="Left"

Margin="3"

Name="firstNameTextBox"

Text="{Binding Path=FirstName, Mode=TwoWay, NotifyOnValidationError=true, TargetNullValue=''}" VerticalAlignment="Center"

Width="120" />

            <sdk:Label Content="ID:"

Grid.Column="0" Grid.Row="3"

HorizontalAlignment="Left"

Margin="3"

VerticalAlignment="Center" />

            <TextBox Grid.Column="1" Grid.Row="3"

Height="23"

HorizontalAlignment="Left"

Margin="3"

Name="iDTextBox"

Text="{Binding Path=ID, Mode=TwoWay, NotifyOnValidationError=true}" VerticalAlignment="Center"

Width="120" />

            <sdk:Label Content="Last Name:"

Grid.Column="0" Grid.Row="4"

HorizontalAlignment="Left"

Margin="3"

VerticalAlignment="Center" />

            <TextBox Grid.Column="1" Grid.Row="4"

Height="23"

HorizontalAlignment="Left"

Margin="3"

Name="lastNameTextBox"

Text="{Binding Path=LastName, Mode=TwoWay, NotifyOnValidationError=true, TargetNullValue=''}" VerticalAlignment="Center"

Width="120" />

</Grid>


Listing

The above listing shows the implementation of Details view in XAML behind. You can notice one thing isNote that the Column header name is separated with a space; this is actually a great thing because in previous versions of Visual Studio we had to customize everything on our own.

This is really helpful.

We can even change the control for each Column.

control of each Column

As you see in the figure above, we have the control options such as TextBox, ComboBox, TextBlock and even we can Customize.

Let's have different controls for each column in the Details view and see how it is helpful.

control for each Column

control of column in silverlight

The following listing shows the Details view in XAML behind.

<Grid DataContext="{Binding ElementName=employeeDomainDataSource, Path=Data}"

HorizontalAlignment="Left"

Margin="206,262,0,0"

Name="grid1"

VerticalAlignment="Top">

            <Grid.ColumnDefinitions>

                <ColumnDefinition Width="Auto" />

                <ColumnDefinition Width="Auto" />

            </Grid.ColumnDefinitions>

            <Grid.RowDefinitions>

                <RowDefinition Height="Auto" />

                <RowDefinition Height="Auto" />

                <RowDefinition Height="Auto" />

                <RowDefinition Height="Auto" />

            </Grid.RowDefinitions>

            <sdk:Label Content="ID:"

Grid.Column="0" Grid.Row="0"

HorizontalAlignment="Left"

Margin="3"

VerticalAlignment="Center" />

            <TextBlock Grid.Column="1" Grid.Row="0"

Height="23"

HorizontalAlignment="Left"

Margin="3"

Name="iDTextBlock"

Text="{Binding Path=ID, Mode=TwoWay}"

VerticalAlignment="Center" />

            <sdk:Label Content="First Name:"

Grid.Column="0" Grid.Row="1"

HorizontalAlignment="Left"

Margin="3"

VerticalAlignment="Center" />

            <TextBox Grid.Column="1" Grid.Row="1"

Height="23"

HorizontalAlignment="Left"

Margin="3"

Name="firstNameTextBox"

Text="{Binding Path=FirstName, Mode=TwoWay, NotifyOnValidationError=true, TargetNullValue=''}" VerticalAlignment="Center"

Width="120" />

            <sdk:Label Content="Last Name:"

Grid.Column="0" Grid.Row="2"

HorizontalAlignment="Left"

Margin="3"

VerticalAlignment="Center" />

            <TextBox Grid.Column="1" Grid.Row="2"

Height="23"

HorizontalAlignment="Left"

Margin="3"

Name="lastNameTextBox"

Text="{Binding Path=LastName, Mode=TwoWay, NotifyOnValidationError=true, TargetNullValue=''}" VerticalAlignment="Center"

Width="120" />

            <sdk:Label Content="Contact:"

Grid.Column="0" Grid.Row="3"

HorizontalAlignment="Left"

Margin="3"

VerticalAlignment="Center" />

            <TextBox Grid.Column="1" Grid.Row="3"

Height="23"

HorizontalAlignment="Left"

Margin="3"

Name="contactTextBox"

Text="{Binding Path=Contact, Mode=TwoWay, NotifyOnValidationError=true, TargetNullValue=''}" VerticalAlignment="Center"

Width="120" />

</Grid>

Listing

Let's see how we can customize the Data Source.

customize the Data Source

The following dialog box would be displayed when we select Customize as displayed in above figure.

dialog box in customize data

In code behind of the view (Employee.xaml.cs) we can create an EmployeeDomainContext instance by using the service Namespace and using that we would perform all the operations.

EmployeeDomainContext instance

We can use the following constructors based on our requirements:

constructors in silverlight

silverlight constructors


By simply calling the method described in EmployeeDomainContext we can achieve the respective operations.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Navigation;
using BusinessApplication02.Web.Services;

namespace BusinessApplication02.Views
{
    public partial class Employee : Page
    {
        EmployeeDomainContext context = new EmployeeDomainContext();

        public Employee()
        {
            InitializeComponent();
        }

        // Executes when the user navigates to this page.
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            LoadData();
        }

        private void LoadData()
        {
            employeeDataGrid.ItemsSource = context.Employees;
            context.Load(context.GetEmployeesQuery());
        }

        private void employeeDomainDataSource_LoadedData(object sender, System.Windows.Controls.LoadedDataEventArgs e)
        {

            if (e.HasError)
            {
                System.Windows.MessageBox.Show(e.Error.ToString(), "Load Error", System.Windows.MessageBoxButton.OK);
                e.MarkErrorAsHandled();
            }
        }

    }
}



Listing

As you see in the above listing above, when we navigate to the Employee page the data would beis loaded.

data beis loaded

As you see, in the preceding figure, all Employee data are is displayed.

Let's change the GetEmployees() query to get all the employee names in ascending order of First Name.
Go back to the EmployeeDomainClass.cs in Service folder of the Web project, and change the query for GetEmployees() method.

[Query(IsDefault = true)]

public IQueryable<Employee> GetEmployees()

{

return this.ObjectContext.Employees.OrderBy(m => m.FirstName);

}

Listing

data beis loaded

Up Next
    Ebook Download
    View all
    Learn
    View all