Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Silverlight Application with Management Entity Framework and WCF Service

0.00/5 (No votes)
20 Dec 2014 1  
A guide to constructing an application using Expression Blend and Silverlight for the front-end which obtains data from a WCF service and SQL Server back-end

Introduction

Below is a guide to constructing an application using Expression Blend and Silverlight for the front-end which obtains data from a WCF service and SQL Server back-end.

This guide does not discuss using Silverlight with WCF RIA Services, because at the time of writing of this article, Microsoft has discontinued support of this technology. If you still desire to implement RIA Services, please consider this open source implementation: Open RIA Services.

Should you use WCF SOAP services or ASMX ASP.NET Web Services? They both are almost identical, except that WCF has more support for marshaling support. WCF services can support more types for input and return from the service.

Please note that I have also written another article similar to this one, except that I go into detail about implementing the Model-View-ViewModel pattern here which I hope you will find helpful. The MVVM pattern is targeted towards UI platforms that support event-driven programming, and it more clearly separates the UI from the business layer or back-end data layer.

Table of Contents

1. Designing in Expression Blend

Expression Blend ships as a standalone tool with Visual Studio 2013. At the time of writing of this article, you can download Blend for free with Visual Studio Express for Windows. It allows UX designers to create an attractive and intuitive user interface without having to write any source code.

When you first start Visual Studio 2013, you are presented with this page where can choose the project type. Select Silverlight Application, and do not check the "Enable WCF RIA Services" checkbox. We'll go into more detail on this topic in sections 3 and 4 below.

850354/DisabledWcfRiaServices.png

After clicking OK, then right-click MainPage.xaml and choose "Open in Blend..." The following page is displayed:

850354/ChooseProject.png

Drag and drop a button to the lower left, and a couple Border items from Blend assets window, and choose a background color for each if desired. Then put a text block item in the larger border, and a text box in the smaller border item.

850354/CreatingTextBlockTextBox.png

Next put a text block item in the upper left portion of the display:

850354/SmallTextBlock.png

You can get more tutorials and videos for Blend here.

2. Data Binding with Expression Blend and Visual Studio

Data binding is necessary to connect your data source to the user interface elements:

850354/ProcessOfDataBinding.png

There are three main forms of data binding:

  1. OneTime: Items that rarely change, such as a store name, or payment options.
  2. OneWay: On items that change often, but are essentially read only.
  3. TwoWay: Much the same as OneWay, except that the changes the user makes here are applied to the business layer, such as allowing a user to select their address from a previously used list, and edit that address.

850354/DataBindingTypes.png

The previous section described how to design the user interface in Blend, and next we will update the data context and complete the data binding in Visual Studio, so open the solution file in that application:

850354/StartVisualStudio.png

Next, add the following Product class to the application:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;

namespace WcfEntitiesSample.Model
{
    public class Product
    {
        private string _name;

        public string Name
        {
            get { return _name; }
            set 
            {
                NotifyPropertyChanged("Name");
                _name = value; 
            }
        }
        private string _description;

        public string Description
        {
            get { return _description; }
            set 
            {
                NotifyPropertyChanged("Description");
                _description = value; 
            }
        }

        private int _inventoryCount = 0;

        public int InventoryCount
        {
            get { return _inventoryCount; }
            set 
            { 
                _inventoryCount = value;
                NotifyPropertyChanged("InventoryCount");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public Product()
        {
        }

        public Product(string name, string description, int inventory)
        {
            _name = name;
            _description = description;
            _inventoryCount = inventory;
        }

        private void NotifyPropertyChanged(String propertyName = null)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

The PropertyChangedEventHandler is used with data binding to ensure hta updates to the source data pass to the bound user interface properties. Creating the PropertyChangedEventHandler identifies the method that will handle the event.

Then open the MainPage.xaml designer, click on the button, and create an event handler for you button by double-clicking on the "Click" item in the Properties window:

850354/ButtonEventHandler.png

Next, open the MainPage.xaml.cs file, and add the following code:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
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;

namespace WcfEntitiesSample
{
    public partial class MainPage : UserControl
    {
        private ObservableCollection<Product> Inventory;
        private int inventoryIndex = 0;

        public MainPage()
        {
            // Required to initialize variables
            InitializeComponent();

            // Initialize variables
            Inventory = new ObservableCollection<Product>();

            // Populate Inventory List
            Inventory.Add(new Product
            ("Stapler", "A hinged device that binds soft things together.", 32));
            Inventory.Add(new Product
            ("Tape", "Sticky strips with impressive binding.", 16));
            Inventory.Add(new Product
            ("Glue", "Unimpressive while wet, but can bind materials together.", 8));

            this.Resources.Add("items", Inventory);
            this.DataContext = Inventory[0];
        }

        private void NextProduct_Click(object sender, RoutedEventArgs e)
        {
            inventoryIndex++;
            inventoryIndex = inventoryIndex % Inventory.Count;
            this.DataContext = Inventory[inventoryIndex];
        }
    }
}

The ObservableCollection allows the data items in the user interface to remain synchronized with the data items in the underlying data source. It raises the CollectionChanged event whenever you add, remove, move, refresh or replace an item in the collection. Note that although ObservableCollection watches for changes to its elements, it does not care about changes to the properties of its elements.

The this.DataContext object gets or sets the data context for a user interface element when it participates in data binding.

Next, open MainPage.xaml, and update the XAML source code to the following. You use the Binding keyword to actually implement the data binding for use with the ObservableCollection and DataContext:

850354/XamlCode.png

If you click start in Visual Studio to debug the application, you should notice the user interface elements changing each time you click the "Next Product" button.

3. Setting Up Database

Download and install SQL Server Express with Advanced Services from MSDN.

Download and install the appropriate version of the AdventureWorks database from Codeplex.

Add this view to the database and call it vProductProductInventory:

SELECT        Production.Product.ProductID, Production.Product.Name, 
              SUM(Production.ProductInventory.Quantity) AS InventoryCount, 
              'Color = ' + Production.Product.Color + ', 
              Size = ' + Production.Product.Size + ', 
              Weight = ' + CAST(Production.Product.Weight AS NVARCHAR) + 
              ', Style = ' + Production.Product.Style AS Description
FROM          Production.Product LEFT OUTER JOIN
              Production.ProductInventory ON Production.Product.ProductID = Production.ProductInventory.ProductID
GROUP BY Production.Product.Name, Production.Product.Color, Production.Product.Size, Production.Product.Weight, Production.Product.Style, Production.Product.ProductID

4. Creating WCF Service Component

WCF services is a server-side technology that is different from XML Web Services in that it is an interface-driven technology. The interface has a ServiceContract attribute, and each of the class methods has an OperationContract attribute. WCF may use XML or JSON.

The data contract which is also in the interface file annotates any class that is going to be serialized over the internet.

Right-click your *.Web project in Visual Studio in the solution you have created (I've called my WcfEntitiesSample.Web) and choose to add a folder called "Services".

Then right-click this folder you have created and choose "Add New Item" and select "WCF Service."

850354/AddWcfService.png

Depending on what you named your service, this is what your code should look like:

namespace WcfEntitiesSample.Web.Services
{
    // NOTE: You can use the "Rename" command on the "Refactor" menu 
    // to change the class name "AdventureWorksService" in code, svc and config file together.
    // NOTE: In order to launch WCF Test Client for testing this service, 
    // please select AdventureWorksService.svc or AdventureWorksService.svc.cs 
    // at the Solution Explorer and start debugging.
    [ServiceContract(Namespace = "")]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class AdventureWorksService : IAdventureWorksService
    {
        public void DoWork()
        {
        }
    }
}

As well, your web.config should contain the following:

850354/WebConfigBindings.png

The binary message encoding is a feature that saves bandwidth.

Next, change the DoWork into something that applies to the Adventure Works database:

[ServiceContract]
public interface IAdventureWorksService
{
    [OperationContract]
    Product GetProductByID(int id);
}

[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class AdventureWorksService : IAdventureWorksService
{
    [OperationContract]
    public Product GetProductByID(int id)
    {
        return new Product()
        {
            ProductID = id,
            Name = "BB Ball Bearing",
            InventoryCount = 1109,
            Description = ""
        };
    }
}

For now, we are just creating a dummy constructor. Later, in the LINQ to Entities section, we will actually get this data from the AdventureWorks database.

To get your service method to show up in the generated proxy, it must be public and marked with the [OperationContract] attribute.

Since business logic today is becoming increasingly complex, it is important to separate out various components of your application into a presentation layer, a business logic layer, and a data layer. The Web service is merely a means of communication between the business logic layer and the presentation layer. Separating out the application into different components helps reduce complexity and makes unit testing easier.

Silverlight does not know anything about the ADO.NET types DataRow or DataTable, so we need to create a type to represent our data. Add the following class into a separate Silverlight Class 5 Library and link that file into another .NET 4 Class Library, which helps provide a better separation of concerns. If you choose to do this, it is important that both the Silverlight Class Library and the .NET Class Library have the same root namespace. This lets you share the files between the client and the server without having to be concerned about different namespaces.

public class Product
{
    public int ProductID { get; set; }
    public string Name { get; set; }
    public int? InventoryCount { get; set; }
    public string Description { get; set; }
}

The compiler will keep it in the service description when generating the client proxy. This is what the [DataContract] attribute does if you decide instead to keep the Product class in the same file as the AdventureWorksService.

Build the solution before starting the next step.

4.1 Adding the Service Reference

Now you should have a web project pointing to the full .NET model and a Silverlight project pointing to the Silverlight model, add a service reference to the WcfEntitiesSample project and generate the proxy:

The reason you want to have both the main project and the Silverlight project referencing their respective Silverlight Class Library and the .NET Class Library is so that the client proxy generator doesn't generate a copy of the model class on the client.

So the next step is to right-click the Silverlight client project and choose "Add Service Reference." On the next dialog that comes up, click the Discover button.

850354/AddServiceReference.png

After you click OK, the client configuration file will have been generated.

4.2 Updating the UI to Test the Service

Add a new textbox where you can enter the ID and a search button:

850354/UpdateUI.png

Next, update the MainPage.xaml.cs file with this code:

using System.Windows;
using System.Windows.Controls;
using WcfEntitiesSample.ServiceRefs;

namespace WcfEntitiesSample
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            // Required to initialize variables
            InitializeComponent();
        }

        private void btnSearch_Click(object sender, RoutedEventArgs e)
        {
            int searchId;

            if (int.TryParse(txtID.Text, out searchId))
            {

                var client = new AdventureWorksServiceClient();

                client.GetProductByIDCompleted += (s, ea) =>
                {
                    DataContext = ea.Result;
                };

                client.GetProductByIDAsync(searchId);
            }
            else
            {
                MessageBox.Show("Invalid customer ID. Please enter a number.");
            }
        }
    }
}

Now test your application by selecting the WcfEntitiesSample.Web as the startup project and start debugging. Try clicking the search button--it should return the values that you hard-coded into your dummy constructor above.

5. Adding LINQ to Entities

It is possible to have many different types of data layers, including LINQ-to-SQL, CLR object, a Web service, or LINQ-to-Enties. We are going to use the latter.

Create a new Class Library project and call it WcfEntitiesSample.Data. Then choose to add new item. In the dialog that is displayed, choose the ADO.NET Entity Data Model:

850354/ADOEntities.png

Then select "EF Designer from database:"

850354/EntityWizard.png

Next, we need to create a connection to our database, so type "ConnecctionString" in the textbox, and click on the new connection button.

850354/ChooseDataSource.png

In the following dialog, select your SQL Server machine name, and choose the AdventureWorks database, using Windows Authentication:

850354/ConnectionProperties.png

After this, you need to check all tables, views and stored procedures. Also, ensure that "Include foreign key columns in the model" is checked, and then click Finish:

850354/EntityWizardObject.png

You should then see a *.edmx file in your solution, with a display showing the tables you selected:

850354/EdmxFile.png

Now, we need to add the EntityFramework.dll reference the WcfEntitiesSample.Data project by browsing to this directory to select the file: ~\WcfEntitiesSample\packages\EntityFramework.5.0.0\lib\net40\EntityFramework.dll.

Next add a ProductManager class to the WcfEntitiesSample.Model.Net Class Library:

using System.Collections.Generic;
using System.Linq;
using WcfEntitiesSample.Data;

namespace WcfEntitiesSample.Model
{
    public class ProductManager
    {
        public Product GetProductById(int productID)
        {
            IEnumerable<Product> products = null;

                var db = new AdventureWorksEntities();
                products = db.vProductProductInventories
                    .Where(prod => prod.ProductID == productID)
                    .Select(p => new Product
                    {
                        ProductID = p.ProductID,
                        Name = p.Name,
                        InventoryCount = p.InventoryCount,
                        Description = p.Description
                    });

            return products.ToList()[0];
        }

Now go to the AdventureWorksService.svc.cs file and change this code:

[OperationContract]
public Product GetProductByID(int id)
{
    return new Product()
    {
        ProductID = id,
        Name = "BB Ball Bearing",
        InventoryCount = 1109,
        Description = ""
    };
}

To this:

[OperationContract]
public Product GetProductByID(int id)
{
    var mgr = new ProductManager();
    return mgr.GetProductById(id);
}

Next, in your WcfEntitiesSample.Web project, ensure you put the connection string into the web.config file:

850354/ModelConnectionString.png

Run your project to verify that the product changes when entering in different ProductID values:

850354/FinishedProduct.png

References

History

  • 19th December, 2014: Initial version
  • 20th December, 2014: Article updated:
    • Corrected instructions for VS 2013 and Blend and updated reference to sections 3 and 4 and corrected code
    • Also added link to details on Microsoft's end of support for WCF RIA Services
    • Added explanation of why n-tier applications are needed
    • Added hyper-linked Table of Contents
    • Added information about MVVM

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here