How to: Build an N-Tier application with WCF and DataSets in Visual Studio 2008

10 בפברואר 2008

23 comments

How to: Build an N-Tier application with WCF and DataSets in Visual Studio 2008


We've known ADO.Net DataSets since the day we started developing data centric applications with the .Net Framework. In the early days of Visual Studio 2003 we have designed DataSets using the XSD designer and had to program the Data Access Layer ourselves. Visual Studio 2008 brings many enhancements to DataSets, which help us to create data centric applications easier than ever. This post is a step by step guide for building an N-Tier application with WCF and DataSets in Visual Studio 2008 that shows all the new enhancements throughout it.


You can download a sample project that is the result of this guide (Download here).


Note: For this guide I am using the Blog Database Schema I published some time ago. Make sure to download the script and create the sample database.


Creating the Data Access Layer


WCF and DataSets Visual Studio 2008 VS20081. Create a new Solution in Visual Studio 2008. Click File-> New Project. In the Project Types tree, expand the Other Project Types node and select a Blank Solution and enter a name for your solution.


2. Create a new Class Library for the Data Access Layer. Note that the new project has a reference to a new assembly that is part of the .Net Framework 3.5 called System.Data.DataSetExtensions, that contains the new enhancements that this guide walks through.


3. Add a new DataSet to the Data Access project – this adds the related files and opens the DataSet Designer. From the Server Explorer, drag the tables of the Blog database to the designer. This creates the appropriate DataTables and their related TableAdapters.


WCF and DataSets Visual Studio 2008 VS2008


In Visual Studio 2005, when the TableAdapters were first introduced, they were generated in the same project as the DataSet class itself. In N-Tier applications, where both server side projects and client side projects had a reference to the DataSets project, we got a situation where the client application had the Data Access layer classes and could use it. This caused many customers to delete the TableAdapters from the DataSet Designer and not use them at all! In Visual Studio 2008, we can separate the generated Dataset class to another project.


WCF and DataSets Visual Studio 2008 VS20084. Separate the Typed DataSet generated code to another project. Create another Class Library for the Business Entities. In the DataSet Designer, display the properties of the DataSet, and select the DataSet Project to be the new project you have just created for the business entities.


This creates the generated code for the DataSet and other business entities in a new file in the selected project.


WCF and DataSets Visual Studio 2008 VS2008


5. Implement the Data Access Logic for getting a blog row. In the DataSet Designer, right click the BlogsTableAdapter and create a new Query. This starts the TableAdapter Query Configuration Wizard that lets you create a new query using an SQL statement or a stored procedure. Select the Use SQL Statement option, click Next, and choose the SELECT which returns rows option. In the SQL Editor, use the following SQL Statement:


WCF and DataSets Visual Studio 2008 VS2008SELECT BlogID, BlogName, Owner
FROM dbo.Blogs
WHERE BlogID = @BlogID


Click Next to choose the methods to generate. the TableAdapter Query Configuration Wizard creates 2 method for each query: GetDataByXXX that returns a new DataTable with the results of the query, and FillDataByXXX that takes an exiting DataTable (that may contain data from previous work) and inserts the query results to it. In this Wizard Step, complete the names of the methods to be GetDataByBlogID and FillDataByBlogID. Click Finish.


After creating the new query, you can preview it's data to make sure that the SQL statement is correct. Right click the new query method in the DataSet Designer and Preview the Data.


This will open the Preview Data Dialog where you should supply the value for the @BlogID parameter and can preview the query results for that value. Use the value bursteg to make sure you receive a single blog row.


WCF and DataSets Visual Studio 2008 VS2008


6. Implement the Data Access Logic for getting the blog posts. Repeat the last step instructions in order to create a new query for the PostsTableAdapter. This query should use the following statement to select the Posts Rows according to a blog ID:


SELECT PostID, BlogID, Title, Body, PublishDate
FROM dbo.Posts
WHERE BlogID = @BlogID


Exposing a Windows Communication Foundation (WCF) Service


7. Create a new Class Library for the Service Contracts. The Service Contract is actually an interface that defines the operations that the service exposes, and the data that goes in and out from it.


WCF and DataSets Visual Studio 2008 VS20088. Add a reference to the System.ServiceModel.dll. This assembly contains the [WCF] libraries required for exposing the service. Also, add a reference to the Business Entities project that contains the DataSet definition, so the service operations can receive or return an instance of that DataSet.


9. Create an Interface for the Service Contract. This interface should be decorated with the ServiceContract attribute above it, and each method should define the ServiceOperation attribute. In this guide, we'll have two operations – GetBlogPosts takes a blog id and returns a DataSet that contains the Blog Posts, and UpdatePosts takes a DataSet with updated / deleted / added posts and applies the changes back to the database.



[ServiceContract]


public interface IBlogService


{


    [OperationContract]


    BlogDataSet GetBlogPosts(string blogID);


    [OperationContract]


    void UpdatePosts(BlogDataSet dsBlog);


}


10. Create a new WCF Service. Add a new Web Site to the solution, and in the Add New Web Site Dialog, select the WCF Service template. This library contains the Service Implementation and exposes the service via HTTP using IIS or a local file system web server.


WCF and DataSets Visual Studio 2008 VS200811. Add references to all 3 projects you have created earlier – the Service Contract project contains the interface that the service implementation class should implement. The Business Entities project contains the DataSet definition that the implementation uses, and of course the Data Access project that is required to perform the data access. Adding those references will copy the dll's to the Bin directory of the WCF Service.


12. Remove the default Service Implementation. Notice that by default, the WCF Service project template creates the Service Contract and the Service Implementation classes with sample code in them under the App_Code directory of the WCF Service. In this guide, we will not be using any of this sample code, so we'll start by deleting the files IService.cs and Service.cs.


13. Create the Service Implementation. Under the App_Code directory, create a new class for the Service Implementation. The Service Implementation is actually a class the implements the Service Contract and performs the logic of the service, such as accessing the database, data validation and other business logic. Let the new class implement the Service Contract.



public class BlogService : IBlogService


{


    public BlogDataSet GetBlogPosts(string blogID)


    {


        throw new NotImplementedException();


    }


    public void UpdatePosts(BlogDataSet dsBlog)


    {


        throw new NotImplementedException();


    }


}


14. Implement the Service Operations that returns the blog posts of a blog. As an implementation for the GetBlogPosts method, create a new instance of BlogDataSet. Then, create an instance of BlogsTableAdapter and use the FillDataByBlogID method to get the blog row. Very similarly, get that blog's posts using the PostsTableAdapter. Finally, return the DataSet with the data.



public BlogDataSet GetBlogPosts(string blogID)


{


    BlogDataSet dsBlog = new BlogDataSet();


    BlogsTableAdapter blogsAdapter = new BlogsTableAdapter();


    blogsAdapter.FillByBlogID(dsBlog.Blogs, blogID);


    PostsTableAdapter postsAdapter = new PostsTableAdapter();


    postsAdapter.FillByBlogID(dsBlog.Posts, blogID);


    return dsBlog;


}


15. Implement the Service Operations that updates the changes made to the BlogDataSet back to the database using the TableAdapterManager. The TableAdapterManager is a new enhancement in Visual Studio 2008 that takes care of the hierarchic updates from a specific Dataset. When the TableAdapterManager code is generated, it looks on the relations between the DataTables in the DataSet, and decides which rows should be added before which, and which rows should be deleted before which. Before Visual Studio 2008, this logic was performed by the application developer and contained logic that was very similar among many applications. In Visual Studio 2008, this logic is being done using the TableAdapterManager.


As an implementation for this method, create a new instance of the TableAdapterManager, and set its properties with the related TableAdapters. In order to perform the actual update, use the method UpdateAll and pass the DataSet as the parameter.



public void UpdatePosts(BlogDataSet dsBlog)


{


    TableAdapterManager updateManager = new TableAdapterManager();


    updateManager.BlogsTableAdapter = new BlogsTableAdapter();


    updateManager.PostsTableAdapter = new PostsTableAdapter();


    updateManager.UpdateAll(dsBlog);


}


16. Expose the BlogService as a WCF Service. Creating a WCF Service as a Web Site using IIS of a local file system web server, creates a .svc file that defines the name of the Service Implementation class and when its code behind is located. Edit the Service.svc file in the WCF Service project, change the value in the Service attribute to "BlogService" and the name of the CodeBehind file to "~/App_Code/BlogService.cs". When done, the Service.svc should look like:




<%@ ServiceHost Language="C#" Debug="true" Service="BlogService" CodeBehind="~/App_Code/BlogService.cs" %>


17. Configure the WCF Service. The WCF Service configuration is located in the web.config file in the WCF Service project, and contains the information that the web server requires in order to host and expose it. Right Click the web.config file, and choose Edit WCF Configuration. This opens the WCF Service Configuration Editor and lets you edit the configuration in a very convenient way.


WCF and DataSets Visual Studio 2008 VS2008As I mentioned earlier, the WCF Service project template creates a sample WCF Service and configures it. Since we are not using the sample service in this guide, we have to change the configuration to work with our service. To do so, make the following changes in the configuration editor:



  • Under the Services Node, select the Service Node, and in the properties pane, rename the service to be  BlogService.

  • Under the BlogService Node, Under the Endpoints Node, click the first endpoint, and choose the IBlogsService interface. To locate this interface, click the … button the in the contract property, navigate to the Bin directory, click the Contracts project and select the contract.

When finished, save the changes, and close the WCF Configuration Editor.


To make sure the service is configured well, right click the Service.svc file, and choose View in Browser. The browser should navigate to the .svc file and you should see a web page that like like this one:


WCF and DataSets Visual Studio 2008 VS2008WCF and DataSets Visual Studio 2008 VS2008


Building a Client Application


WCF and DataSets Visual Studio 2008 VS200818. Create a new Windows Forms application as the client application, and add references to the Business Entities project and the Service Contract Project.


19. Create the client side proxy for the WCF Service. Right click the Client Application project, and add a Service Reference. In the Add Service Reference Dialog, click Discover to find the service we have created earlier. Type the name of reference and click the OK.


WCF and DataSets Visual Studio 2008 VS2008 


This creates a proxy class in the client application, and the relevant configuration sections. This makes the consumption of the WCF Service very easy. If you take a look at the generated code (Show all files and look for the Reference.cs file ), you should see that the definition of the DataSet itself and the Service Contract were not generated again. This is because we have referenced their assemblies and the generator used the original definition.


WCF and DataSets Visual Studio 2008 VS200820. Show the blog details. Go to the Windows Forms designer to edit the form in the client application. Display the Data Sources Pane (Data -> Show Data Sources), expand the DataSet Node and from the drop down list of the Blogs DataTable, select Details. Also, expand the Blogs node, and choose Label as the control for the field BlogID.


Select the Blogs Node and drag it onto the form. This creates the appropriate controls for the blog details, and the additional navigation toolbar.


21. Display the blog posts. From the Data Sources Window, drag the list of Posts to the form, and create a DataGridView control. This grid will display the posts of the current blog.


WCF and DataSets Visual Studio 2008 VS2008


22. Get the Data from the WCF Service. Double click the Form to go to the Load event handler. In this handler, create a new instance of the proxy, and call the GetBlogPosts operation. Assign the returned DataSet to the binding source of the blogs table.




private void ClientApplication_Load(object sender, EventArgs e)


{


    BlogServiceClient proxy = new BlogServiceClient();


    this.blogDataSet = proxy.GetBlogPosts("bursteg");


    this.blogsBindingSource.DataSource = this.blogDataSet;


}


Run the application, and make sure you receive the details of the blog you requested, and its posts.


23. Add the support for updates. In the Form Designer, select the Save button in the navigation toolbar which is disabled by default. In the properties pane, set the Enabled property of this button to True, and double click it to create the event handler. In the handler method, create an instance of the proxy, and call the UpdatePosts method.




private void blogsBindingNavigatorSaveItem_Click(object sender, EventArgs e)


{


    this.blogsBindingSource.EndEdit();


    this.postsBindingSource.EndEdit();


    BlogServiceClient proxy = new BlogServiceClient();


    proxy.UpdatePosts(this.blogDataSet);


}


 


Run the application and perform changes to the blog details, add and remove posts, and then click save.


You can download a sample project that is the result of this guide (Download here).


Conclusion


Building an N-Tier data centric application is easier than ever with WCF and DataSets in Visual Studio 2008. The application developer productivity is increasing, the amount of code to write, test and maintain is decreasing, and the results are much better!


Enjoy!

Add comment
facebook linkedin twitter email

23 comments

  1. Maor David11 בפברואר 2008 ב 1:08

    Great job Guy. I like it!

  2. Valentin14 באפריל 2008 ב 23:49

    run your client application more that 10 instances
    and you see, – your application hang
    because max concurent connection by default set to 10
    you must or close proxy instance after each call or increase this parameter more than 10

    p.s. sorry for my bad english

  3. Ratnakar Garikipati15 באפריל 2008 ב 4:58

    I did a quick run through your project and puzzled to see that your win forms client app has references to the “Services Contracts” and “Business Entities” projects – not sure why!!!

    http://ratnakarg.wordpress.com

  4. e06637729 באפריל 2008 ב 23:32

    have you ever tried it to run standalone (not under vs2008), is it running?
    even in vs2008, have tried to update the data 2 times back to back in one run, is it still working?
    i examined some other tutorials too, and non is working with the specified conditions, all works in debug mod only and cant update data 2 times in a single run, and cant open 2 instances of the program(i need n-tier for more than 1 user).
    who made wcf must check it again

  5. tim29 במאי 2008 ב 0:26

    why do you have references to “Services Contracts” and “Business Entities” projects in your client app? what's the purpose of that? can you explain?

  6. James5 ביוני 2008 ב 19:58

    It seems that the following things should be done to make this function correctly consistenly:

    1) Add in proxy.Close() methods as mentioned in an earlier comment.

    2) Factor out the method to get data:

    private void GetData()
    {
    BlogServiceClient proxy = new BlogServiceClient();
    this.blogDataSet = proxy.GetBlogPosts("bursteg");
    this.blogsBindingSource.DataSource = this.blogDataSet;
    proxy.Close();
    }

    3) Call GetData() after updating. The concurrency error that people have seen is related to saving the dataset, but then updating a previous "version" of it would seem.

    James

  7. Ratnakar Garikipati30 ביולי 2008 ב 5:32

    Hi – instead of referring to the business entities within your client app directly, write a translater class to translate between your data contract and the business entities. This translation will have to take place within the service implementation layer.

  8. Randy S.4 באוגוסט 2008 ב 9:02

    If i used a Web Form instead of a WinForm for my client, would i use Web App Reference instead of Service Reference?

  9. Dan L.13 באוגוסט 2008 ב 9:36

    Our company want's to re-architect our apps to use shared wcf services consumed by web pages. Would it be wise to use your technique and replace winforms with webforms and the objectdatasource?

  10. Wissam23 באוגוסט 2008 ב 2:36

    Greetings,
    thank you for the useful article.

    i have a question about the design of the database design,why you do not put the categoryid directly in the Posts Table since according to me there is no need to make a separate table PostCategories since one category can have multiple posts but the same postid cannot be in two different categories.

    so if you have another concept so can you let me understand it why you create the additional table PostCategories.

    your help is highly appreciated
    Regards.

  11. Andrey Yemelyanov17 בספטמבר 2008 ב 3:37

    Great article! Very illuminating, even though I am working with XML Web Services. And, data binding rocks!

  12. R Figueiredo20 באוקטובר 2008 ב 11:08

    Hi,

    I don't understand why do you uses a Typed-Dataset?

    I don't understand the real advantage of a Typed-Dataset in this sample.

    Regards,

    R Figueiredo

  13. Christian4 בנובמבר 2008 ב 17:54

    Hey,
    great article.
    you should publish it on Codeproject.com.
    It's great value to other developers 🙂

  14. Sandeep Verma25 בפברואר 2009 ב 12:29

    This is the most beautiful way to describe the technology in a very simple and detailed way. hats off to u
    i was searching for such a blog which can describe in a step by step form.
    Thanks it helped a lot.

  15. Bala26 בפברואר 2009 ב 5:39

    its good for the beginners.But it will be better if you would have explained thru design patterns

  16. Lakshmi Narayana9 באפריל 2009 ב 10:56

    Hi,

    you didnot use the local database cache toll provided by vs2008.Pleae explain the steps for device with sync services for ado.net

  17. lingoman19 במאי 2009 ב 18:44

    Overall a good tutorial. Though you need to add one line to the ClientApplication_Load method to get the data to show in the grid:
    postsDataGridView.DataSource = blogDataSet.Posts;

  18. Bing Chen20 במאי 2009 ב 23:47

    I tried to implement a sample application using your method above. I created two dataset instead of one as you used. For some reason the reference.cs only uses the first dataset.

    This is Interface defined:

    [ServiceContract]
    public interface IServiceCastAdmin
    {
    #region Function
    [OperationContract]
    FunctionDataSet GetFunctionData();

    [OperationContract]
    Boolean SaveFunctionData(ref FunctionDataSet DS);
    #endregion

    #region BillingType
    [OperationContract]
    BillingTypeDataSet GetBillingTypeData();

    [OperationContract]
    Boolean SaveBillingTypeData(ref BillingTypeDataSet DS);
    #endregion
    }
    This is the reference.cs:

    public CastAdminEntities.FunctionDataSet GetFunctionData() {
    return base.Channel.GetFunctionData();
    }

    public bool SaveFunctionData(ref CastAdminEntities.FunctionDataSet DS) {
    return base.Channel.SaveFunctionData(ref DS);
    }

    public CastAdminEntities.BillingTypeDataSet GetBillingTypeData() {
    return base.Channel.GetBillingTypeData();
    }

    public bool SaveBillingTypeData(ref CastAdminEntities.FunctionDataSet DS) {
    return base.Channel.SaveBillingTypeData(ref DS);
    }

    I don't underdstand why SaveBillingTypeData() in the reference.cs uses CastAdminEntities.FunctionDataSet not BillingTypeDataSet instead.

    Is something wrong with my coding?

  19. Henry S.2 ביוני 2009 ב 1:05

    your example is N-Layer, but not N-Tier.

  20. Strongangel5 במאי 2010 ב 6:50

    in Bursteg.Samples.DataAccess I create new classes "DBConnection.cs" and "AccountDAL.cs" this code :
    DBConnection.cs :

    public static SqlConnection Conn;
    public DBConnection()
    {
    try
    {
    string strCon = "server=localhost\\SQLEXPRESS; Initial Catalog= Blog; Integrated security = True";
    Conn = new SqlConnection(strCon);
    Conn.Open();
    }
    catch (System.Exception ex)
    {
    throw ex;
    }
    finally
    {
    Conn.Close();
    }
    }

    AccountDAL.cs :
    public DataTable GetAllAccount()
    {
    string strselect = "select * from Blogs";
    SqlDataAdapter daAccount = new SqlDataAdapter(strselect, DBConnection.Conn);
    DataTable dtAccount = new DataTable();
    daAccount.Fill(dtAccount);
    return dtAccount;
    }
    in Bursteg.Samples.Services.Contracts in definde new operation :
    [OperationContract]
    System.Data.DataTable GetAllAccount();
    and implement service operations :

    public DataTable GetAllAccount()
    {
    DataTable dtAccount = new DataTable();
    AccountDAL accountdal = new AccountDAL();
    dtAccount = accountdal.GetAllAccount();
    return dtAccount;
    }
    in Bursteg.Samples.Client I create new datagridview

    private void button1_Click(object sender, EventArgs e)
    {
    BlogServiceClient proxy = new BlogServiceClient();
    DataTable dtAccount = new DataTable();
    dtAccount = proxy.GetAllAccount();
    dataGridView1.DataSource = dtAccount;
    }
    I am greeting a error : "The underlying connection was closed: The connection was closed unexpectedly."
    I try to config file app.config at : closeTimeout,opentimeout, receivetimeout… and enable tracing at the server .. but it does not work. please help me ! this is my source code :
    link : http://cid-2911c9b1d5d570fe.skydrive.live.com/self.aspx/.Public/Bursteg.Samples.VS2008Datasets.rar

Comments are closed.