Tuesday, January 08, 2013

WCF Data Services + oData + Entity Framework + Silverlight + Telerik simple application

At the time writing this article, WCF Data Services(previously known as ADO.NET Data Services) ships separately from .NET Framework and are distributed by NuGet.

This means, the namespace used is in the form Microsoft.Data.Service.* and not System.Data.Service* (like in .NET Framework). The new release supports oData v3, while the previous release of WCF Data Services existing in .NET Framework supports oData v1 and v2.

This is described here: http://msdn.microsoft.com/en-us/data/ee720179

The Telerik suite has a nice support for WCF Data Services by the RadDataServiceDataSource class, which is able to bind the data from WCF Data Service to the UI.

http://www.telerik.com/help/silverlight/raddataservicedatasource-overview.html

One problem is Telerik was built against the .NET Framework support for WCF Data Services, not against the assemblies distributed by NuGet.

Read more about this here: http://www.telerik.com/community/forums/silverlight/dataservice-datasource/raddataservicedatasource-problem-visual-2012.aspx

 

Steps to create the app:

1. Create a new Silverlight 5 application using Web Application project setting, and suppose the name of the app is SilverlightApplication1

The next steps refer to the web application:

2. I wanted to have a code behind class for the generated SilverlightApplication1TestPage.aspx, so I added a SilverlightApplication1TestPage.aspx.cs class, and in the SilverlightApplication1TestPage.aspx I added:

CodeBehind="SilverlightApplication1TestPage.aspx.cs" Inherits="SilverlightApplication1.Web.SilverlightApplication1TestPage"

Also, I made SilverlightApplication1TestPage to derive from System.Web.UI.Page

3. Add references to System.Data.Services, and System.Data.Services.Client to add support for WCF Data Services.

Also, rigth click on the web project and choose “Manage NuGet packages”. In the top right search box, search for Entity Framework and when found, click on Install button. It will add a reference to EntityFramework assembly.

4. Create a class for an entity called User:

[DataServiceKey("Id")]
[DataServiceEntity]
public class User
{
    [Key]
    public virtual int Id { get; protected set; }
    public virtual string FirstName { get; protected set; }
    public virtual string LastName { get; protected set; }
}

The DataServiceKey and DataServiceEntity attribute need to be set to tell WCF Data Services which is the Id property of the class entity.

5. Create a class derived from DbContext, UserContext

public class UserContext : DbContext
    {
        public DbSet<User> Users
        {
            get;
            set;
        }

        public UserContext(string connString) : base(connString)
        {
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Configurations.Add(new UserMapping());
            base.OnModelCreating(modelBuilder);
        }
    }

We can expose the User entity by the WCF Data Service using a public property returning a DbSet of that entity

The DbContext constructor accepts a string which represents the connection string to the database

6. Add a UserMapping class which implements the mapping of the User entity to database, so for Entity Framework to know how to do it

public class UserMapping : EntityTypeConfiguration<User>

{
        public UserMapping()
        {
            this.HasKey(t => t.Id);
            this.Property(t => t.FirstName).IsRequired().HasMaxLength(50);
            this.Property(t => t.LastName).IsRequired().HasMaxLength(50);
        }
    }

7. Right click on the web app project, and choose Add New Item/click on Web/WCF Data Service and choose UserService.svc as the name.

[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
    public class UserService : DataService<ObjectContext>
    {
        public static void InitializeService(DataServiceConfiguration config)
        {
            config.SetEntitySetAccessRule("*", EntitySetRights.All);
            config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
            config.UseVerboseErrors = true;
        }

        protected override ObjectContext CreateDataSource()
        {
            var connString = ConfigurationManager.ConnectionStrings["DBConnString"].ConnectionString;
            var context = ((IObjectContextAdapter)new UserContext(connString)).ObjectContext;
            context.ContextOptions.ProxyCreationEnabled = false;
            return context;
        }
    }

The ServiceBehavior attribute makes the web-service to include the exception in the fault details. It is important when debugging.

Fiddler is good to debug web-service issues and this attribute will make the exceptions visible in the response.

SetEntitySetAccessRule is important to set rights on entities.

TestQueryableSL is the name of the connection string to the database

8.  Add the following to the web.config:

<connectionStrings>
    <add name="DBConnString"
         connectionString="Server=YourPCName;Database=Test;Trusted_Connection=True"
         providerName="System.Data.SqlClient"/>
      </connectionStrings>

<system.serviceModel>
  <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
</system.serviceModel>

You should have a database called Test. If not, open SQL Server Management, and add a new database called Test.

In the Silverlight app:

9. Add a new class UserDataCtx:

public class UserDataCtx : UserContext
{
    public UserDataCtx()
        : base(new Uri("
http://localhost:4232/UserService.svc", UriKind.RelativeOrAbsolute))
    {
    }
}
Instead of 4232 port, use the port from right click on Web app project/ Web tab/Use Visual Studio Development Server/Specific port

This class is required in order to pass the Uri to the UserContext class

 

10. In MainPage.xaml add:

xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
xmlns:l="clr-namespace:SilverlightApplication1"

<Grid x:Name="LayoutRoot"
         Background="White">
       <Grid.RowDefinitions>
           <RowDefinition Height="*" />
           <RowDefinition Height="Auto" />
       </Grid.RowDefinitions>
       <telerik:RadDataServiceDataSource Name="customersDataSource"
                                         QueryName="Users"
                                         AutoLoad="True">
           <telerik:RadDataServiceDataSource.DataServiceContext>
               <l:UserDataCtx />
           </telerik:RadDataServiceDataSource.DataServiceContext>
       </telerik:RadDataServiceDataSource>
       <telerik:RadGridView Grid.Row="0"
                            ItemsSource="{Binding DataView, ElementName=customersDataSource}"
                            IsBusy="{Binding IsBusy, ElementName=customersDataSource}"
                            ShowGroupPanel="False" />
       <telerik:RadDataPager Grid.Row="1"
                             Source="{Binding DataView, ElementName=customersDataSource}"
                             PageSize="10" />
   </Grid>

11. Right click on the Silverlight project and add a new service reference. Click on the arrow button from Discover button, and choose ‘Services in this solution’

You should see UserService.svc in the list and selected.

In the Namespace text box, choose the name DataServices

12. Add reference to the Telerik.Windows.Controls/Data/DataServices/GridView/Input  and Telerik.Windows.Data

No comments: