A Walkthrough of WCF Support in Visual Studio 2008

A Walkthrough of WCF Support in Visual Studio 2008

  • Comments 17

One feature in Visual Studio 2008 I would like to highlight is our support of Windows Communication Foundation. WCF was introduced with .NET Fx3.0 as a next generation communications API. Previously if you wanted apps to talk you needed to make a lot of up-front decisions:

o How important is transmission speed?
o How will apps talk? Over the internet, intranet, or on the same machine?
o Is reliable messaging important?
o Is message encryption important?

Once you made those decisions and started writing your app you were forced to learn a new protocol {.Net Remoting, ASMX web services, MSMQ, Named Pipes, etc.} and implement a solution specific to that API. The problem with this is that your code doesn’t age gracefully. If the requirements change too much, you need to rewrite the entire communication layer using an API which supports whatever mission-critical feature you need.

WCF fixes this problem and more by providing a unified programming model for all the above scenarios so you now just have to learn one API and toolset. It does this by separating the various pieces of communication, E.g., protocols, message contracts, etc. Rather than worrying about the specifics of how each message is transferred between client and server, WCF just asks you to focus on the service and message contracts. That is, what methods will you be able to call from a client and the structure of the data returned. All the goo about communication protocols is highly configurable and out of code (in the app.config). That is WCF from a mile-high view, but let’s dive into what it can do for you in Visual Studio 2008.

We will be creating a three-tiered application called Northwind Traders. Our solution will comprise of four projects: a data access layer, a WCF service, a WinForms client, and a shared business logic component. Along the way we will highlight key features new to Visual Studio 2008 and demonstrate just how easy it is to write killer data-centric apps.

Creating Data Access Layer

Let's start our app by creating a data access layer by creating a new class library aptly titled DataAccessLayer.

One of the common problems developing N-tier applications in Visual Studio, is that the tools put the TableAdapters and the typed DataSets in the same project. Which unfortunately leaks out sensitive information such as connection strings. If you wanted to reuse your typed DataSets but keep your TableAdapters private, you have to resort to fighting against the tools which doesn’t provide a great developer experience. Fortunately in Visual Studio 2008 we’ve addressed this.

Once you've created the data access layer in Visual Studio 2008 you can configure the DataSet Designer to generate the DataSet code into a separate project in your solution. So you can share/reuse your typed DataSets without exposing things like connection strings.

Add another class library project to your solution called NorthwindBuisnessObjects.

DataSet into Diff Project

Then, reopen the NorthwindDataSet and change the ‘DataSet Project’ property on the properties window to be NorthwindBuisnessObjects. Now, once you build your project, NorthwindDataSet.DataSet.Designer.vb will be generated in the NorthwindBuisnessObjects project.

Next let's add some simple validation logic to our DataSet. Double click the DataSet designer and insert the following code snippet. (Note that it automatically creates DataSet.vb in the correct project.)

Partial Class NorthwindDataSet

    Partial Class OrdersDataTable

 

        Private Sub OrdersDataTable_OrdersRowChanging(ByVal sender As System.Object, ByVal e As OrdersRowChangeEvent) Handles Me.OrdersRowChanging

 

        End Sub

 

        Private Sub OrdersDataTable_ColumnChanged(ByVal sender As Object, ByVal e As System.Data.DataColumnChangeEventArgs) Handles Me.ColumnChanged

            If e.Column.ColumnName = Me.OrderDateColumn.ColumnName Or e.Column.ColumnName = Me.ShippedDateColumn.ColumnName Then

                ValidateDates(e.Row)

            End If

        End Sub

 

        '*********************************************

        'CUSTOM VALIDATION CODE FOR MY DATASET

        '*********************************************

        Private Sub ValidateDates(ByVal row As OrdersRow)

            If row.OrderDate > row.ShippedDate Then

                row.SetColumnError(Me.OrderDateColumn, "Can't ship before it's been ordered")

                row.SetColumnError(Me.ShippedDateColumn, "Can't ship before it's been ordered")

            Else

                row.SetColumnError(Me.OrderDateColumn, "")

                row.SetColumnError(Me.ShippedDateColumn, "")

            End If

        End Sub

    End Class

End Class

 

Let’s finish our data access layer by adding a class called NorthwindManager, which connects to the database and returns our tables.

 

Imports NorthwindBuisnessObjects

 

Public Class NorthwindManager

 

    Public Shared Function GetCustomers() As NorthwindDataSet.CustomersDataTable

 

        Dim customerAdapter As New NorthwindDataSetTableAdapters.CustomersTableAdapter()

 

        Return customerAdapter.GetData()

 

    End Function

 

    Public Shared Function GetOrders() As NorthwindDataSet.OrdersDataTable

 

        Dim orderAdapter As New NorthwindDataSetTableAdapters.OrdersTableAdapter()

 

        Return orderAdapter.GetData()

 

    End Function

 

End Class

 

Creating a WCF Service

Now let's move onto creating our WCF service. The tooling support should be on par with the ASMX Web Reference experience. Let’s begin by creating a new WCF Web Service project, which creates a new WCF service hosted in IIS.

New Website Dialog

Note that WCF doesn't have to just be a web-based service. In Visual Studio 2008 we also have project templates for service libraries, which are WCF services baked into class libraries. In addition we have a Test Form which allows you to debug you WCF services (not discussed here).

Once our service project has been created, let's add references to both our DataAccessLayer and BuisnessObjects projects.

Next, let’s replace the default service contract from the project template with our own. Those <ServiceContract()> and <OperationContract()> interfaces indicate to the WCF runtime that this is a WCF service contract.

Imports NorthwindBuisnessObjects

 

<ServiceContract()> _

Public Interface IService

 

    <OperationContract()> _

    Function GetCustomers() As NorthwindDataSet.CustomersDataTable

 

    <OperationContract()> _

    Function GetOrders() As NorthwindDataSet.OrdersDataTable

 

End Interface

Let’s change the service contract implementation accordingly.

Imports NorthwindBuisnessObjects

Imports DataAccessLayer

 

Public Class Service

 

    Implements IService

 

    Public Function GetCustomers() As NorthwindDataSet.CustomersDataTable Implements IService.GetCustomers

 

        Return DataAccessLayer.NorthwindManager.GetCustomers()

 

    End Function

 

    Public Function GetOrders() As NorthwindDataSet.OrdersDataTable Implements IService.GetOrders

 

        Return DataAccessLayer.NorthwindManager.GetOrders()

 

    End Function

 

End Class

 

Creating a WCF Client

Now that our service is complete let’s build a simple WinForms app to consume it. Once you’ve added a new WinForms application to the project, to add the WCF service reference simply right click on the project in solution explorer and select "Add Service Reference…". From there you will see the following dialog:

ASR Dialog

You'll notice that in the picture our service is already listed. For existing web services (both ASMX and WCF) you can type in the service URI and click Go; however, for services in your solution all you need to do is click ‘Discover’ and all services in your current solution will be brought up.

Something I would like to point out is the Service Reference Settings dialog, which appears if you click "Advanced…".

Service Reference Settings

This dialog allows you to configure a few aspects of your WCF service reference, but for the 80% case the defaults should be fine. You can also bring this dialog up to configure a service reference after you’ve added it. In Visual Studio 2005, in order to change advanced sorts of properties for a Web Reference you had to break into the command line and use SDK tools to regenerate your proxy. Whereas in Visual Studio 2008 you can configure your service reference while staying in the rich IDE experience.

Since we will be sticking with the defaults, you can dismiss the Service Reference Settings dialog and Click OK to add the service reference. This generates the appropriate configuration elements in the project’s app.config as well as generates the proxy code required for interacting with your service.

Proxy Type Reuse

Here is where I would like to point out one of the coolest features for WCF in Visual Studio 2008 - Proxy Type Reuse.  Before I go into what it solves, let me frame the problem. If we look at the generated proxy code, we see that the service returns ‘NorthwindDataSet’. However, it isn’t referring to the BuisnessObjects.NorthwindDataset type but rather Client.ServiceReference.NorthwindDataSet. The generated type is a fake, it looks identical and can be serialized/deserialized back and forth but doesn’t contain the actual methods of the DataSet, namely our validation logic.

Fake DataSet

This enables what is known as ‘Service Oriented Architecture’. As long as the client or server can serialize an object into the same format, it doesn’t matter exactly what that type is. (Or which language, for that matter. In fact a Java program could consume our WCF service!)

However, we don’t want to use the ‘fake’ NorthwindDataSet since that would mean we would have to maintain two versions of our validation logic in two places. What proxy type reuse allows us to do is that since we control both the client and the server we can reuse the same underlying DataSet type, with combined validation logic. With ASMX web references you would have to break into command line tools to enable this type of code reuse, but in Visual Studio 2008 it is enabled and on by default.  

To enable proxy type reuse, simply add a reference to NorthwindBuisnessObjects in our client project. Then right click the ServiceReference node in solution explorer, and select "Update…". This will update the service reference, regenerating the service proxy. Since by default we scan all project references for types to reuse, the generated proxy will now expose the datatables from our NorthwindBuisnessObjects project.

Another aspect of the WCF service reference I'd like to point out is the configuration we generate. If you open the client project’s app.config file you will notice a new <system.serviceModel> section. This contains all of the settings for your WCF service reference. In fact the service has a similar configuration section in the service’s web.config file.

As a security precaution the client configuration we generate has a default MaxRecievedMessageSize value of 65536, meaning that an exception will be thrown at runtime if the service sends more data to the client than it expects. Since we know we will be passing large DataSets over the wire, please open up the app.config and set the element <bindings>\<wsHttpBinding>\<binding>’s maxReceivedMessageSize attribute to 5000000. Or, if you feel uncomfortable editing your service’s configuration by hand, you can open up the Microsoft Service Configuration Editor available under Visual Studio’s Tools menu.

Now that our service has been added and configured correctly, let’s do some data binding to the data tables returned from our service. Open up the Data Sources window and drag and drop the Orders table onto Form1.

DataSet Drag and Drop

And finally, the last thing we need to do is add code to Form1’s OnLoad event to get data from our WCF service.

Public Class Form1

 

    Private m_serviceClient As New ServiceReference.ServiceClient()

 

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

 

        Me.OrdersBindingSource.DataSource = m_serviceClient.GetOrders()

 

    End Sub

 

End Class

 

Now press F5 and let’s see our app in action!

Application Running

You can see now that we have a three-tiered application which retrieves data from a WCF service through an isolated data access layer. But remember when I said ProxyTypeSharing was such a great feature? Here is why: since we are reusing the same DataSet type we can take advantage of the validation logic in our BuisnessObjects layer. So try editing the data grid such that date in the ShippedDate field occurs before the date in the OrderDate field.

Now with Data Validation

Notice that the DataGrid picks up on the error and displays the correct message.

Conclusion

We've demonstrated how to take advantage of WCF and ProxyTypeSharing as well as some new features for better organizing your data layers. The end result is that you can quickly create powerful applications with a minimal amount of plumbing. But that only scratches the surface of what WCF can do.

For more information on WCF you can check out the following resources:
WCF page on MSDN - http://msdn2.microsoft.com/en-us/library/ms735119.aspx  
That Indigo girl, a blog on WCF - http://www.thatindigogirl.com/default.aspx

Thanks!
-Chris Smith and John Stallo

Edit: Corrected the Service Contract Implementation code, which was missing an Imports statement.

Leave a Comment
  • Please add 8 and 1 and type the answer here:
  • Post
  • if you want to implement this in c#, you'll need to modify the OrdersDataTable partial class somewhat.

           partial class OrdersDataTable {

               public override void EndInit() {

                   base.EndInit();

                   this.ColumnChanged += new System.Data.DataColumnChangeEventHandler(OrdersDataTable_ColumnChanged);

                   this.OrdersRowChanging +=new OrdersRowChangeEventHandler(OrdersDataTable_OrdersRowChanging);

               }

               void OrdersDataTable_OrdersRowChanging(object sender, OrdersRowChangeEvent e) { }

               void OrdersDataTable_ColumnChanged(object sender, System.Data.DataColumnChangeEventArgs e) {

                   if (e.Column.ColumnName == this.OrderDateColumn.ColumnName | e.Column.ColumnName == this.ShippedDateColumn.ColumnName) {

                       ValidateDates(e.Row as OrdersRow);

                   }

               }

               void ValidateDates(OrdersRow row) {

                   string message = string.Empty;

                   if (row.OrderDate > row.ShippedDate) {

                       message = "Can't ship before it's been ordered";

                   }

                   row.SetColumnError(this.OrderDateColumn, message);

                   row.SetColumnError(this.ShippedDateColumn, message);

               }

           }

  • How about dynamic dataset and sqldataadapter?

Page 2 of 2 (17 items) 12