VB & SQL

Download as doc, pdf, or txt
Download as doc, pdf, or txt
You are on page 1of 43

Visual Studio 2005 and SQL Sever 2005 are fairly new products with many new concepts.

Those
who are eager adopters of new technology have plunged in and written many great articles about
the new productivity. But others have taken a "wait and see" attitude, and need a little
encouragement and even hand-holding to get over the learning curve with less frustration.

For those who prefer to learn with a guided training environment, I have written this series of
hands-on tutorials that will take you through all the steps to build a complete application using
the new database and IDE tools. This application is architected in a 3-tiered model with a
business/data access layer against a SQL Server data layer, both a Windows (Smart Client) and
Web user interface layers and finally a Web Service to communicate between the layers.

Project Description

The Internet is a great place to find documentation and training on new products like Visual
Studio 2005. There are many great websites that publish articles that can help us get up to speed
quickly. The problem is that with these articles scattered all over the net, it is hard to find the
information you need. Search engines are a good start, but since they index on words instead of
topics, it takes a lot of searching to find the right articles.

The .NET WebLinks project is a collection of Internet sources for information about .NET 2.0,
Visual Studio 2005 and SQL Server 2005. Links to these articles are collected into a searchable
database and categorized by subject. The project will allow all the .NET enthusiasts to add to the
database, so that it will grow to become a very valuable tool for the .NET community.

Part 1 will define the database using SQL Server 2005 and business logic using datasets.

Part 2 is a Windows interface to the database, allowing individuals to import links from their
Favorites directory, edit the links and upload to the master database.

Part 3 is a Web based interface to allow the general community access to search the database for
the desired links.

Part 4 will define the Web services for the project to communicate between the layers, how to
define and consume them.

Part 1 - Database and Business Logic

Designing the SQL Server Database

Before beginning a database design, a few items need to be discussed like methodology and
naming conventions. While there are a variety of opinions, many database designers prefer using
table names in the singular form (name each as if there were only one record), design to 3rd
normal form, and using Identity integer fields as primary keys. In naming fields, the primary key
is always the table name followed by "ID", foreign key fieldnames are the same as the primary
key they point to, and the field names or context should imply the field type. If a database can be
updated by disconnected applications, primary keys may need to GUIDs instead of (or in
addition to) identity integers to avoid duplication of keys.

Visual Studio has a panel (exposed by selecting Server Explorer from the View menu) which has
almost all the functionality needed to design and build a database. The only function that is
missing is the ability to rename tables (so take care to get them right the first time.)

Note: The Server Explorer is known as the Database Explorer in the Express versions of Visual
studio.

1. Begin this session by opening Visual Studio 2005 and opening the Server Explorer
(under the View menu).
2. Right click "Data Connections" and select "Create New SQL Server Database...". This is
also where you can select "Add Connection..." to connect to an existing database.

3. Enter (or select) the name of the server. A shortcut is to enter a period (.) to select the
default localhost server.
4. Select the Authentication desired--normally "Windows Authentication" is sufficient.
5. Enter WebLinks as a new database name and click OK.
6. The wizard automatically creates a connection to the new database. A tree view shows
the default schema.

The WebLinks database is made up of four tables: the hierarchical Topic table to define
structure to categorize the links; the Link table to store information about each web
reference, a lookup table to define the different Link Types, and a Topic Link table for
the many-to-many resolution table between Topic and Link so a reference can be
classified under multiple topics.

7. Next, create the tables. Right click on the Tables entry and select Add Table. In the grid
provided enter the fields for the Topic table.

8. In the column properties detail section below the grid, specific properties can be added
for each column (the displayed properties are for the selected field in the grid). For the
TopicID field, scroll down in the properties to the Identity Specification. Click the plus
(+) next to the entry to expand it and set the "Is Identity" to true, leave the seed and
increment to 1.
9. In the toolbar above the Server Explorer, click the key icon to set the TopicID field to be
the primary key for the table.
10. In each field, you can fill in the Description property, and in the InactiveFlag field, fill in
the Default Value property to 0. (Bit flag fields are difficult to handle when they are null.
By forcing a default value, the field should never be null.)
11. After entering all the fields and properties, click the save button in the toolbar (diskette
icon) and save the table with the name Topic.
12. Repeat this procedure for each of the other three tables using the following fields:

LinkTable
LinkID int Primary Key
LinkTitle varchar(200) Title of the work
LinkTypeID int NULL
Description varchar Paragraph describing contents of article
URL varchar(500) Actual link to article
Author varchar(150) NULL
PrimaryLinkID int ID of primary URL for article
Type of entry (Primary, Print, Page, Alternate, Language,
SecondaryType varchar(50)
etc.)
InActiveFlag bit NULL
ArticleDate datetime Original Date published to the Web
AccessCount int Number of users accessing this link
RatingCount int Number of users offering a rating
RatingTotal int Total Rating score
AvgRating real current average rating
DateCreated datetime Date added to the database
DateModified datetime Date of last modification
ModifiedBy varchar(50) Username of person making the modification
LinkType Table
LinkTypeID int Primary Key
LinkTypeDesc varchar(50) Link Type Description
Topic Table
TopicID int Primary Key
TopicName varchar(50) Name or Description of the topic
ParentTopicID int FK to Topic for Parent Topic (self-join)
DateCreated datetime Date Created
CreatedBy varchar(50) Created By
InActiveFlag bit Inactive Flag (no longer active)
TopicLink Table
TopicLinkID int Primary Key
TopicID int FK to Topic
LinkID int FK to Link
Comment varchar(500) Freeform comment on why topic applies to link.
InActiveFlag bit NULL
DateCreated datetime Date Created
CreatedBy varchar(50) Created By Username
16.
17. You can click the icon in the toolbar to create relations manually, but an easier way is to
use the Database Diagrams wizard. In the Server Explorer, right click the Database
Diagrams folder and select Add New Diagram.
18. In Add Tables dialog, select all four tables and click OK. (If you prefer, you may skip the
dialog and drag and drop the tables from the Server Explorer.) Each of the four tables
will be shown in the diagram with a list of fields below.
19. To create a relation, select the primary key of the parent table and drag and drop it on the
foreign key of the child table. Create the following relationships:
20. Save the diagram as Main and confirm the creation of foreign keys. SQL Server will also
create indexes for all primary and foreign keys.

Adding Functionality

The WebLinks application needs several back-end functions to process the data more efficiently.

1. The hierarchical Topic table needs a function to help process the structure. Right click on
the Functions folder in Solution Explorer and click, Add New, Table-valued Function.
2. There is no Wizard to help you here--you have to write the TSQL code yourself. VS does
add a decent template to pattern from. Paste in the following code:

/* <FunctionName>udfTopicTree</FunctionName>
// <Summary> Returns table of all rows with level and sort
//           made up of hierarchy of TopicName
// </Summary>
*/

CREATE FUNCTION udfTopicTree()


RETURNS @Tree TABLE (TopID Int, ID Int, Lev int, Sorted VarChar(255),
HasChildren int)

AS
BEGIN
    --declaration and initialization
    Declare @Level Int, @Count Int
    Set @Level=0
    --Get top level records
    INSERT INTO @Tree
    SELECT Topic.TopicID AS TopID, Topic.TopicID AS ID, 0 AS Lev,
        RTRIM(Topic.TopicName) AS Sorted,
        ISNULL(COUNT(Topic_1.TopicID), 0) AS HasChildren
    FROM Topic LEFT OUTER JOIN
        Topic AS Topic_1 ON Topic.TopicID = Topic_1.ParentTopicID
    WHERE (Topic.ParentTopicID IS NULL)
    GROUP BY Topic.TopicID, RTRIM(Topic.TopicName)
    Set @Count= @@RowCount

    While @Count>0 Begin        --While records are still being added


        Set @Level=@Level+1
        IF @Level>9 Return      --in case of infinite reference loop

        INSERT INTO @Tree


        SELECT TopID, Topic.TopicID AS ID, @Level AS Lev,
            Sorted + '--' + RTRIM(Topic.TopicName) AS Sorted,
            ISNULL(COUNT(Topic_1.TopicID), 0) AS HasChildren
        FROM Topic LEFT OUTER JOIN
            Topic AS Topic_1 ON Topic.TopicID = Topic_1.ParentTopicID
        INNER JOIN
            @Tree ON [Topic].ParentTopicID = [ID]
        WHERE Lev = @Level - 1
        GROUP BY TopID, Topic.TopicID, RTRIM(Topic.TopicName), Sorted
        Set @Count= @@RowCount
    End
    Return
End

3. VS does have some tools to help you. Notice the blue boxes around the Select statements.
You can right click in these boxes and select Design SQL Block to show a visual query
designer. If the query is complex, you can highlight a certain section and just build that
part of the query. Open the query builder for the first Select by right clicking inside the
blue box and choosing "Design SQL Block".

This function will return several fields that are important for the processing of
hierarchical data. For each record in the table, you will need to know the ID of the top
level parent, the level in the tree, a composite field showing the names of all previous
levels in the tree, and a field for showing whether or not (or how many) children are
present for a given node.

The first part of the function selects all of the top level nodes. The following shows the
builder for the first part of the query.

The first panel shows the tables and the joins between them. In this case, the function
needs to know if there are child records, so the table has an outer join with itself. You can
right click on the join icon between the tables to edit the join type.

The second panel shows a list of the fields or calculations that are returned as fields. You
can also define sort, group and filter for fields.

The third panel shows the resulting SQL statement and the forth panels shows the
resulting rows when you click the Execute Query button. Click the "Cancel" button on
the Query Builder dialog.
The second half of the function selects the next level nodes and joins them with the
results of the previous query(ies) and loops through each succeeding level appending the
nodes to the temporary table.

4. Click the Save (diskette) icon in the toolbar to save the function. If there are any errors,
the SQL engine will give a very nondescript error message. If the Save icon is disabled,
click outside the first SQL Select box in the function.

Another function needed is to do a word search of the Link table for keywords entered by
the user. The search follows the basic rules of Google and other search engines. This
function will be implemented through a Stored Procedure.

5. Right click on the Stored Procedure folder and click Add New Store Procedure.
6. Once again, all you get to work with is a template and the query builder. Here is the T-
SQL for the search function:

CREATE PROCEDURE dbo.SearchByKeywords


    (@Search VarChar(255))
    --Search for Links by Keyword
    --returns a data table of Links
AS
    /* SET NOCOUNT ON */
    --get a set of search words
    declare @where varchar(8000)
    set @where=''
    declare @word varchar(255)
    DECLARE @pos int
    --check for injection - may need to add other checks here
    if charindex('''',@search)>0 or charindex(';',@search)>0 begin
        raiserror('SQL Injection error - Cannot use single quotes ' ,
16,1)
        return
    end
    set @search=ltrim(@search) + ' '
    while len(@search)>0 begin
        --is next word in quotes - take whole quoted section as word
        if @search like '"%"%' begin
            set @pos=charindex('"',@search,2)
            set @word=substring(@search,2,@pos-2)
        end
        -- select the next word in search words list
        else begin
            set @pos=charindex(' ',@search,1)
            set @word=substring(@search,1,@pos-1)
        end
        --create a where clause for the search of the following pattern
        --if the word is "DotNet"
        -- Where PatIndex('%DotNet%',[LinkTitle])>0
        -- OR PatIndex('%DotNet%', [Description])>0
        if len(@where)>0 set @where= @where + ' OR '
        --append a space on the begin and end to handle first and last
words
        set @where=@where + ' PatIndex(''%[ -]' + @word + '[ ,.;-?!:]
%''' +
            ','' '' + [LinkTitle] + '' '' + ISNULL(Description, '''') +
'' '')>0'
        --remove the word from the search string
        set @search=ltrim(substring(@search,@pos+1,9999))
    end --loop
    --return a set of rows that have the search words - in the same
format as the
    --TopicLink table since that is where they will be displayed
    exec('SELECT LinkID AS TopicLinkID, LinkID, 0 AS TopicID, NULL AS
Comment,
              0 AS InactiveFlag, NULL AS DateCreated, NULL AS CreatedBy,
LinkTitle
          FROM Link WHERE ' + @where)
    RETURN

7. Click the Save button to process and save the stored procedure. Notice that the editor
changes the Create keyword to Alter after it is saved successfully.
8. One more stored procedure is needed to accept votes on the article rating. Right click on
Stored Procedures and Add New Stored Procedure. In the code window, add the
following:

-- Record a new vote on a Link and calculate the new average


CREATE PROCEDURE dbo.LinkRatingVote
    (
    @LinkID int,
    @Rating int, --new rating
    @AvgRating int OUTPUT
    )
AS
    SET NOCOUNT ON
    Declare @RatingCount int
    Declare @RatingTotal int
    --get the Link record
    Select @RatingCount=RatingCount, @RatingTotal=RatingTotal
    From Link Where LinkID=@LinkID
    --calculate new values
    Set @RatingCount=ISNULL(@RatingCount,0)+1
    Set @RatingTotal=ISNULL(@RatingTotal,0)+@Rating
    Set @AvgRating=@RatingTotal/@RatingCount
    --Update the link record with new values
    Update Link
    Set RatingCount=@RatingCount, RatingTotal=@RatingTotal,
AvgRating=@AvgRating
    Where LinkID=@LinkID
    RETURN

9. Click the save button.

Add a Connection to an Existing Database...

If you wish to save time and have a populated


database, you can download the above database
already created as part of the code samples.
Follow the instructions there to restore the SQL
Server backup to your server. Then you can
connect it to the Server Explorer as follows:

Right click in the blank area of the Server


Explorer and click Add New Connection or click
the Connect to Database icon at the top of the
Server Explorer.

1. In the first page of the connection wizard,


make sure the Data Source is set to "SQL
Server (SqlCLient)". If it is not, click the
Change button and select SQL Server
from the list of options.
2. Select the server from the list or enter a
period (.) for the localhost option.
3. If the database has been set up for
integrated security, choose Windows
Authentication, otherwise select "SQL Server Authentication and enter the user name and
password given by your DBA.
4. In the bottom section, select the WebLinks database from the list.
5. Click the Test Connection button to verify access to the database.
6. Back in the Server Explorer, you will see a tree view showing the schema of the database.

7. At this point you can right click on any of the tables and select Open Table Definition to
make edits to the design or you may select Show Table Data to get a grid showing the
data in the table. You can even use this grid to make manual changes to the data.

Creating the Business Logic DataSet

It is important to build the Business Logic portion of the project first since the other three parts
will use this middle tier in the form of a dataset to store the data locally.

Open Visual Studio 2005 development environment.

Note: there is a setting under Tools, Options, Projects and Solutions, General to always see the
solution in the explorer. With the solution showing, it is easier to right click and add a new
project to the solution.

If you have not done so yet, use the following procedure to setup a properly structured Solution
with multiple projects.

1. Open Visual Studio 2005


2. From the File Menu, select New, Project...
3. In the left pane choose Visual Studio Solutions under Other Project Types, and in the
right pane, Blank Solution. Name the new solution "WebLinks" and choose a directory
where you wish to store the solution.

Note: This procedure will create the solution file in the proper directory. If you create
the solution at the same time as the project, the solution file gets placed under the project
folder which makes it hard to find the solution when multiple projects are created in the
solution.

4. Now add a project to the solution for the shared business classes. Either from the File
menu or by right-clicking on the Solution in the Solution Explorer, select Add, New
Project, Visual Basic, Class Library. Name the project Biz.
5. VS automatically creates a new class called Class1. In the Solution Explorer, right click
Class1 and select Rename and change the name to Business.vb. This will be our base
class for our business objects to inherit from. We will come back to this later. For now,
close the Business class using the small "x" in the upper right of the code pane window.

Note: It is easy to click the wrong close button. The big red "X" in the upper right of the
screen will close the whole Visual Studio program, sometimes without prompting, and
takes some time to get restarted. Be careful to use the small grey "x" below the tool bar
to close files.

6. Visual Studio has a great visual tool to build a Typed Dataset. Right-click on the Biz
project in solution explorer and from the context menu, select Add, New Item. From the
dialog, select Dataset and name it WebLinksDataSet.xsd.
7. The Dataset Designer will present a blank screen to build the tables in the dataset. The
easiest way to do this is to drag the tables from the Server Explorer. Under the View
menu select Server Explorer (or press Ctrl+Alt+S) to add the pane to the left side of your
designer.

Note: If you are using an Express version of Visual Studio, this will be called Database
Explorer instead of Server Explorer.

8. If your connection to WebLinks is already open, you can jump to step 14 and drag all
four tables to the designer as well.
9. Right-Click on the top DataConnections node and select Add Connection...
10. In the Add Connection Dialog, fill in the name of the server. If you are using the default
installation of SQL Server on the local system, you can use a single period as a shortcut.
Otherwise, type or select from the list the server to which you wish to connect.
11. You should be able to use Windows Authentication. If you do not control the Windows
server, you may have to switch to SQL Server Authentication, but this is less flexible and
requires setting up usernames and passwords on the SQL Sever.
12. Select the WebLinks database from the list provided in the database section of the dialog.
13. Click the Test Connection button to ensure all the permissions are set correctly and then
click OK.
14. The new connection should now show as a node under the Data Connections root. Click
the "+" to the left of the node to expand and see the database items. Click the "+" next to
the Table node to see the tables in the database. Click the first table and shift click the last
table to highlight all four tables. Now drag and drop these tables onto the Dataset
Designer screen.

15. The wizard will read the metadata from the database and build Data Tables for each of
the tables and add relations for each of the relationships in the database. You can now
organize these tables by clicking in the header and moving them around. You should end
up with a diagram similar to the one shown below.
16. From the File menu, select Save (or Ctrl+S or click the diskette button on the toolbar) to
save the dataset. The wizard will now generate over 4000 lines of code for you in the
WebLinks.Designer.vb file. You need to click the Show All button at the top of Solution
Explorer to see this hidden file as well as the partial class to be created next. You should
end up with a diagram as follows:
17. You can edit the query for each table by right clicking on the header or Table Adapter
band and select Configure. The Wizard will open and prompt you for information. Right
click the Topic table and select Configure.
18. The first screen shows the query being used by the Topic table adapter to select and
update data.

19. If you click on the Previous button, you can change from using imbedded SQL to Stored
Procedures. You can use your own or allow Visual Studio to generate them
automatically. While using Stored Procedures is a good idea, we will continue to use the
embedded SQL for this exercise. You can come back at a later time and generate the
procedures. Click Next to return to the SQL Statement screen.
20. Click the Query Builder button to view the query graphically. This is very similar to the
query builder used on the Stored Procedures. You can manipulate the query as necessary
to get the results you need for the program. When finished, click the OK button to return
to the SQL Statement screen
21. If you click the Advanced Options button, you will be given several options. If your table
is read only in this dataset, you can deselect the "Generate Insert, Update, and Delete
statements" option. Such is the case for the LinkType table.

The "Use optimistic concurrency" compares every field in a record before updating or
deleting the record. If you don't care about two users changing the same record at the
same time, but prefer to let the latest edits win, you can turn this off. It does simplify the
number of parameters passed to the Update and Delete queries.

The "Refresh the Data Table" option causes the Insert and Update queries to return the
updated record so the dataset knows about database generated fields. This is important
when using Identity fields as primary keys so the dataset can update the keys properly.
22. Back on the SQL Statement screen, click Next to go to the next screen. The Choose
Methods to Generate screen allows you to choose which type of methods you wish the
wizard to generate. The Fill methods will add records to an existing table in the dataset.
The Get methods return a data table object to your application. You can turn off the
Update methods for read-only tables (like LinkType).

23. The next screen gives a summary of the methods generated. These are available for your
application to call. Click Finish to update the Table Adapter and return to the DataSet.
24. Now you need to add several additional queries for selecting specific data. Right Click on
the TopicTableAdapter band and click Add Query. This will take your through the same
wizard and allow you to modify the query to provide parameters that limit the rows
returned. One of the ways we want to limit the Topic table is to show only nodes that
belong to a certain root. (The root of the tree is the username of the person importing
favorite links. You may want to limit the tree to a certain user).
25. In the Wizard, click Query Builder to view the query. In the top panel, right click and
select Add Table.

26. In the Add Table dialog, select the Functions tab and the udfTopicTree function.

27. Join the function to the Topic table by dragging the TopicID primary key from the Topic
table and drop it on the ID field of the udfTopicTree table. This will create a join line
between them in the diagram and add the Inner Join phrase to the SQL.
28. In the second panel, scroll down to the first empty row and add a row for the TopID field
of the udfTopicTree_1 table. Uncheck the Output box and in the Filter column add the
condition "=@TopID".

29. Click OK on the Query Builder and then Next on the wizard to the Method Name screen.
Change the name of the new methods to "FillByTopID" and "GetByTopID". (The wizard
defaults to "GetDataBy". Take out the word Data and add TopID.)
30. Click Finish and back on the DataSet screen, you will see a new line added in the Table
Adapter section of the Topic table showing the new query. The corresponding methods
were generated in the class.

The TopicLink table needs to be modified to show the corresponding LinkTitle for each
record. In the Windows interface, it is not too difficult to show related data using a
combo box in the grid, but in the Web interface, it would require converting the column
to a Template, making it more complex. It is easier to modify the query to join the tables
and return the Title information. This join will be added in another query, but the field
needs to be accounted for in the default query so the schemas will be consistent.

31. Modify the configuration of the TopicLink table (right click, configure) and add an empty
field with the alias "LinkTitle". The SQL should now look like this:

SELECT TopicLinkID, TopicID, LinkID, Comment, InActiveFlag, DateCreated,


    CreatedBy, '' as LinkTitle
FROM dbo.TopicLink

32. When VS added this new field to the TopicLink table, the default size was set to 1
character. This will give an error when the table is filled with actual titles in the next step,
so the parameter must be changed manually. In the DataSet Designer screen, select the
LinkTitle field in the TopicLink table and in the properties panel, change the MaxLength
property to 2000.
33. In the TopicLink table, follow the above procedures to create a new query to return only
records for a specific TopicID. Add a new query and in the Query builder, add the
parameter @TopicID to the filter column for the TopicID field. Also add a join for the
Link table and replace the empty field. The SQL should look like this:

SELECT TopicLink.LinkID AS TopicLinkID, TopicLink.TopicID,


TopicLink.LinkID,
    TopicLink.Comment, TopicLink.InActiveFlag, TopicLink.DateCreated,
    TopicLink.CreatedBy, Link.LinkTitle
FROM TopicLink INNER JOIN
    Link ON TopicLink.LinkID = Link.LinkID
WHERE (TopicLink.TopicID = @TopicID)

34. Click Next and name this query "FillByTopicID" and "GetByTopicID".
35. Create another query for the TopicLink table that filters the LinkID by a parameter
@LinkID. Use the following SQL and give it a name of FillByLinkID, GetByLinkID.

SELECT TopicLinkID, TopicID, LinkID, Comment, InActiveFlag, DateCreated,


    CreatedBy, '' AS LinkTitle
FROM TopicLink
WHERE (LinkID = @LinkID)

36. The third query needed for the TopicLink table is to get the records using the keyword
search stored procedure. Right click on the TopicLink Table Adapter and select new
Query. In the first wizard page, select "Use Existing Stored Procedure."
37. In the second wizard page, select the stored procedure "Search by Keywords."
38. In the third screen, select the "Tabular Data" option.
39. In the last screen, name the query FillByKeyWords and GetByKeyWords and click
Finish.
40. For the Link table, create a query that gets the data by LinkID using the paramerter
@LinkID using the following SQL.

SELECT AccessCount, ArticleDate, Author, AvgRating, DateCreated,


DateModified,
    Description, InActiveFlag, LinkID, LinkTitle, LinkTypeID,
ModifiedBy,
    RatingCount, RatingTotal, URL
FROM Link
WHERE (LinkID = @LinkID)

41. Also in the Link table, add another query to filter by TopicID. This will require adding a
join for the TopicLink table to the query and filtering by @TopicID, but do not include
the TopicID field in the output of the query.

SELECT Link.AccessCount, Link.ArticleDate, Link.Author, Link.AvgRating,


    Link.DateCreated, Link.DateModified, Link.Description,
Link.InActiveFlag,
    Link.LinkID, Link.LinkTitle, Link.LinkTypeID, Link.ModifiedBy,
    Link.RatingCount, Link.RatingTotal, Link.URL
FROM Link INNER JOIN TopicLink ON Link.LinkID = TopicLink.LinkID
WHERE (TopicLink.TopicID = @TopicID)

42. One last thing, open the Server Explorer panel and drag the LinkRatingVote stored
procedure and drop it on the Dataset screen. The wizard will create a special table adapter
called QueriesTableAdapter for it and will generate the ADO code necessary to call the
stored procedure.
43. You should end up with your Dataset looking like this:

Data Access Logic


Now you need to create the Data Access logic to read information from the database to fill the
dataset. This is an important part of an n-tiered architecture. Only the Business layer should have
access to Data Access logic. Therefore, you need to provide a wrapper for the data access logic
that can be referred to in the presentation layer. To begin with, use direct access to the database.
In a later section, you will come back and provide the option getting the data through Web
Services. This will show the importance of separating these layers as the business layer can be
switched to access data via a Web Service without any changes necessary to the front-end.

Right-click anywhere in the Dataset designer screen and select View Code. This will create a
partial class into the dataset where you can enter customized code. The Partial Class for the
WebLinksDataset will be created automatically, but you can also create sub classes for each of
the tables, rows, change events, and even the table adapters (in a different namespace.)

The goal is for each table to create a method to fill for each of the queries and to update.
Additional methods will be created for the Web interface to return a table rather than fill an
existing table so it will remain stateless.

Enter the following code:

Imports ta = Biz.WebLinksDataSetTableAdapters

Partial Class WebLinksDataSet

    Protected Shared taTopic As New ta.TopicTableAdapter


    Protected Shared taTopicLink As New ta.TopicLinkTableAdapter
    Protected Shared taLink As New ta.LinkTableAdapter
    Protected Shared taLinkType As New ta.LinkTypeTableAdapter
    Protected Shared taSP As New ta.QueriesTableAdapter

    Public Sub FillAll()


        LinkType.Fill()
        Link.Fill()
        Topic.Fill()
        TopicLink.Fill()
    End Sub

    Public Function SubmitRating(ByVal LinkID As Integer, ByVal NewRating As


Integer) As Long
        Dim AvgRating As Long = 0 'output parameter returned
        AvgRating = taSP.LinkRatingVote(LinkID, NewRating, AvgRating)
        'update the avg rating in dataset
        Dim row As LinkRow = Link.FindByLinkID(LinkID)
        row.AvgRating = AvgRating
        row.EndEdit()
        row.AcceptChanges() 'no need to pass to the database
    End Function

#Region "Data Tables"


    Partial Class LinkDataTable
        Public Sub Fill()
            taLink.Fill(Me)
        End Sub
        Public Sub FillByLinkID(ByVal LinkID As Integer)
            taLink.FillByLinkID(Me, LinkID)
        End Sub

        Public Sub FillByTopicID(ByVal TopicID As Integer)


            taLink.FillByTopicID(Me, TopicID)
        End Sub

        Public Function GetByID(ByVal LinkID As Integer) As LinkDataTable


            Return taLink.GetByLinkID(LinkID)
        End Function

        Public Sub Update()


            taLink.Update(Me)
        End Sub
    End Class

    Partial Class TopicDataTable


        Public Sub Fill()
            taTopic.Fill(Me)
        End Sub

        Public Sub FillByTopID(ByVal TopLevelID As Integer)


            taTopic.FillByTopID(Me, TopLevelID)
        End Sub

        Public Function GetData() As TopicDataTable


            Return taTopic.GetData()
        End Function

        Public Sub Update()


            taTopic.Update(Me)
        End Sub
    End Class

    Partial Class TopicLinkDataTable


        Public Sub Fill()
            taTopicLink.Fill(Me)
        End Sub

        Public Sub FillByTopicID(ByVal TopicID As Integer)


            taTopicLink.FillByTopicID(Me, TopicID)
        End Sub

        Public Function GetByTopicID(ByVal TopicID As Integer) As


TopicLinkDataTable
            Return taTopicLink.GetByTopicID(TopicID)
        End Function

        Public Sub Update()


            taTopicLink.Update(Me)
        End Sub

        Public Function GetByKeyWords(ByVal KeyWords As String) As


TopicLinkDataTable
            taTopicLink.FillByKeyWords(Me, KeyWords)
            Return Me
        End Function
    End Class

    Partial Class LinkTypeDataTable


        Public Sub Fill()
            taLinkType.Fill(Me)
        End Sub
    End Class
#End Region
End Class

Save and close the Code window and the Dataset window.

The last step needed is to build the Biz project. In the Solution Explorer, right click on the Biz
project and select Build.

This project will be referenced by the other projects making this logic available for other parts of
the program.

Conclusion

In this first part of the tutorial, you have seen how Visual Studio 2005 can be used as a great tool
for designing and building SQL Server 2005 (and previous versions) databases, whereas in
previous versions, you needed several different tools.

With the significant advancements in the DataSet Designer and the partial classes, it is very easy
to use visual tools to generate the entity objects and methods needed for our business logic. With
Typed DataSets, our business logic layer can make use complex business objects with multiple
tables, leveraging the relationships between tables rather than using simple business objects
based on single tables.

In our next tutorial, you will create a Windows Forms based user interface for our application.

Microsoft is known for providing good tools for Rapid Application Development (RAD) tools
for developers. MS Access, Visual Studio 6, Visual Interdev and others have provided an
environment to visually combine controls, properties, and generated code for quick applications.
Visual Studio .NET has combined the best of the previous tools into a great development
environment with several different coding languages.

There are many wizards in Visual Studio .NET (VS) that assist new users in creating
applications. Unfortunately, they tend not to promote good programming habits--specifically
there is not a separation of code into layers and they do not adhere to OOP standards isolating
business logic. The default model of VS is to drag and drop data objects onto the form we are
building. VS generates the code necessary, but mixes the data access code and the business logic
code directly in the user interface form object. This limits the reusability of the code and tends to
duplicate code where the same table data is used on more than one form.

Most architects recommend more of an n-tiered approach where the code is separated into
different layers. There should at least be a data access layer, business logic layer, and a
presentation layer. When working with distributed applications (different parts of the application
running on different servers) more layers may need to be added.

There are two primary advantages for separating the layers: code reusability and de-coupling
code from the database. By separating out the data access and business logic layers, many
different forms can be used to collect and display the information without rewriting or
duplicating the code to access and use the information. The de-coupling helps keep your code
from breaking when the database schema changes and makes it easier to even change data
storage providers without much code change.

Microsoft .NET has made great strides in supporting and encouraging developers to move
toward Object Oriented Programming (OOP) standards. While OOP is a large (and deep)
subject, one facet that should interest developers of database applications is the concept of a
business object. While database tables and fields do a good job of implementing entities and
attributes, business objects bring together both the properties and behavior of a particular entity.
When the application instantiates a business object class to represent each entity (table), the
attributes can be accessed through properties and the behavior of the entity can be called through
methods. This concept also brings all the code for an entity together into one place that can be
referenced easily from other parts of the application. Through inheritance and polymorphism, the
business objects can be quite flexible and have substantial power to accomplish much with little
code.

Part 2 - Using Visual Studio 2005

The new version of .NET and Visual Studio offers several enhancements that change the way we
develop database centric applications. In the context of this article, we will be looking
specifically at Data Sources and Partial Classes.

The Binding Context Manager has been expanded and greatly simplified into Binding Sources
component that allows forms to provide better data binding to datasets, Web services, and
business objects.

Partial Classes provides a way to extend classes across multiple files which will be combined
together at compile time. The Typed Dataset class has been extended to include partial classes,
separating generated code from hand crafted code, allowing us to add business logic to the
dataset class without the complications of inheritance.

Getting Started
After opening VS2005, create a new project (File / New Project) and let's start with a VB
Windows project. In the dialog at the bottom, name your project NorthwindWin and the Solution
Northwind.

Figure 1 - New Project in VS2005

The Data Object

The first step in creating a data application is to create a Typed DataSet object to define the data
object to be used. You can add a new DataSet object to you project from the Solution Explorer
window by right-clicking on the project and selecting Add / New Item. The design surface of the
DataSet has been enhanced to automatically include relations from the database when more than
one table is added to the dataset (by dragging from the Server Explorer or Database Explorer
(Express Version) window). The dataset has been expanded to include one or more data adapters
to handle the "Fill" and "Update" methods so we don't need to create an inherited class to
implement them. The Configuration Wizard has been changed slightly, but accomplished the
same effect.

A better way to create a DataSet is to use the Data Sources wizard. With the proper project
selected, click the top line Data menu and select "Add New Data Source". The Data Source
Configuration Wizard will take over and step you through creating a DataSet with the proper
connection. Just click Next on the first screen. The next screen allows you to choose the type of
Data Source.
Figure 2 - Create DataSource Wizard, Screen 2

This time we will choose the Database type, but take note of the other options to use a Web
Service or existing Business Object, because we will come back to them.

The second screen allows you to choose an existing connection or create a new one.

Figure 3 - Data Source Wizard, Screen 3


If you do not have a connection to the Northwind database, use the New Connection button to
create one now. If you have not previously set up a Project Setting for this connection, the
Wizard will do it for you now.

Figure 4 - Data Source Wizard, Screen 4

In the next screen of the Wizard, you will be shown a tree list of all the metadata in the database.
Expand the Tables node and select the tables needed in this DataSet (Orders, Order Detail, and
Products)
Figure 5 - Data Source Wizard, Screen 5

Before leaving this screen, be sure to correctly name your DataSet. (Some of the earlier Betas
had problems renaming DataSets correctly.) In this case, we need to change the name of the
dataset from NorthwindDataSet to OrdersDataSet.

After finishing the Wizard, you will have a dataset added to your project with 3 tables in it.
Figure 6 - Resulting DataSet Visual representation

With this design surface, you can continue to drag more tables from the Server Explorer
(Database Explorer in Standard version) to add to the dataset. Looking ahead, we know we will
need to add the Customers and Employees tables for combo box lookups.

You can edit the tables by right-clicking on the header and selecting Configure... from the drop
down menu (or use top line Data menu, Configure).

Figure 7 - Configuration Wizard page 1

Click the Query Builder button to visually edit the SQL for the Table Adapter. In this case, the
combo box will need a Full Name field that combines the last and first names.

You can also click the Advanced Options button to deselect the option to generate Update and
Delete methods since this table is for lookup only and will not be edited. Click the Next button
for more options on the Fill, GetData and Update methods and finish the Wizard. Repeat this
configuration for the Customers table.

Presentation Layer

Data Sources RAD Tool

Once the dataset is created, you can open the Data Sources
pane from the top line Data menu. This pane will already
show the dataset created, but you may also add other
sources from a database server, local database file (such as
Access or FoxPro), a Web Service, or a predefined
Business Object class. The wizard will proceed through
each step required for adding data sources. You may also
expand the table node to see all the fields available in the
data table.

When you have a form open in the IDE, the Data Sources
pane exposes some additional functionality. As each node
is selected, a dropdown (combo) box allows the user to
choose how the object should be rendered when dragged to
the form. For tables, the choices are GridView or Details
view. The GridView will create a DataGridView control
that implements each field in a default form, but can be
edited to render different embedded controls. The Detail
view creates a label and textbox (or other component) for
each field showing a single record at a time.

Other choices for field controls are: Textbox,


NumericUpDown, Combobox, Label, Linked Label, and
Listbox. You can also create custom user controls and add them to the list.

The Data Sources tool fosters a Rapid Application Development (RAD) environment where
developers can quickly create a User Interface (UI) based on database schema. Rather than
binding controls directly to dataset fields, VS2005 adds another layer call the Binding Source
which correlates to the data object stored in the dataset. The controls on the UI are bound to the
Binding Source fields. This is a new component that is a combination of the Binding Context and
DataView objects.

The Controls have simplified binding by reducing the number of properties available for binding
to a couple of the more popular (tag, text, selected value, etc.). You can still bind to the other
myriad of properties through the Advanced settings if needed, but the common bindings are
prominent and defaulted in most cases by the Data Sources wizard.

Building the Orders View/Edit screen


Now it is time to build a form to edit the Orders data. Right-click on the project in Solution
Explorer / Add / New Item and select the Windows Form icon, giving it a name of
OrdersForm.vb. VS will present a blank form to work with.

If your Data Sources window is not open, click the top line Data
menu and select Show Data Sources. In your tree, you should
see the dataset and data tables. To the right of each table is a
dropdown that allows you to choose between implementing it as
a DataGridView or free form Details controls. You can further
expand the table to see the data fields and similarly select which
control to use to implement the field in free form controls.
(Note: In the Beta, you could configure how the field would be
rendered in the DataGridView as well, but this was removed in
the final version so that only Textboxes are rendered, but they
can be changed by editing the columns later.) This Wizard can
be modified through the Tools / Options / Windows Forms
Designer / Data UI Customization to allow custom controls to be
listed as well.

Most presentations usually prefer to have parent screens shown in Details format so in Data
Sources, change the format for the Orders table to Details. Now comes the exciting part--drag the
Orders table from the Data Sources window and drop it in the upper left corner of the blank form
in the design surface of Visual Studio. The Data Sources Wizard will create each of the controls
in the table, according to the default selected. You may also drag the fields one at a time from
Data Sources pane, but it is much faster to just have them all created at once. You will notice that
each control is properly named and bound to the correct field in the table. The wizard also
creates a label for each control, using the description property if available and otherwise, the
name of the field, inserting spaces where needed.

More than just the controls, the Wizard has added functionality to our form by adding a Data
Navigation strip that allows the user to switch between records using VCR style buttons. The
Wizard also added the components necessary to provide connectivity and binding with the
dataset and database. If you open the code window, you will see that the Wizard has even added
a line of code to call the Table Adapter component to fill the DataSet component. If you set the
project to be the Startup project and the properties of the project to show this form at startup, you
can build and run the solution now and see the form fully functional, showing all the records in
the Orders table. There is a little work to do to get the Combo Box controls working correctly,
but the form is basically functional.
Figure 8 - Orders Form Master

To get the Combo Boxes functional, we can open the DataSet and add the Customer and
Employee tables (or create separate datasets for them). After building the project, you can refresh
the Data Sources window to see the new tables or add new data sources if you put them in a
different DataSet.

Back on the form, drag the Employee table from the Data Source and drop it on the combo box.
The first thing you notice is another Binding Source component is added to for the Employee
table. If you select the employee combo box control, you will notice that the proper settings for
the Data Source, Display Member and Value Member have been set and the binding has been
changed from Text to Selected Value. This is what Microsoft calls "Connect the dots"--you can
drag and drop a table to any list driven control. The wizard takes the primary key for the Value
Member and the first character field for the Display Member property.

You can drag the Customer table to the Customer combo box in the same way and a Binding
Source component will be added for it also. If you open the code behind the form, you will also
notice that VS has added code to the OrdersForm_Load class to fill the tables with data from the
dataset for each binding source added to the form.
After a little rearrangement of controls, here is what the form should look like:

Figure 9 - Order Form Master final

Master Detail Presentation

The heart of a good relational application is the ability to display parent-child relationships.
Visual Studio 2005 offers a fairly flexible tool to help rapid development of this functionality. In
the Data Sources pane if you expand the tables, you will see below the fields, an entry for each
child relationship of the table. In our example, expanding the Orders table node, you will see an
entry for the relationship to the Order Details table. If you select the DataGridView
representation and drag the relation node to the form, VS assumes you want a master-detail
arrangement and will created the needed links for you.

VS will add a new BindingSource component to the form for the table and a table adapter to fill
the table on form load. The Smart Data tag will expand (or you can click on the arrow in the
upper right of the grid control) to show several properties and actions for the control. The
"Choose Data Source" should be set to the new binding source based on the relation between the
tables. This is all that is necessary to set the detail grid to link with the master record. If you look
at the properties, you will see that the Data Source for the newly added Binding Source is the
OrdersBindingSource and the Data Member is the relationship.
Figure 10 - Adding Details to the Order Form

If you compile and run the project now, you will see the linking between the master and detail
parts. But the grid needs some editing to make it show productive data.

You will need to replace the ProductID with a combo box to display the name of the product
instead. This will require another binding source to be added to the form. Drag a Binding Source
component from the Data section of the Toolbox, name it ProductsBindingSource and set the
Data Source property to OrdersDataSet and the Data Member property to the Products table.

Now expand the Smart Tag on the DataGridView control again (or look at the Properties pane)
and click the "Edit Columns" action. This allows you to edit width and style of columns, reorder
the columns, and add / delete columns. You can remove any columns that are not necessary and
hide (Visible property equals False) the Primary Key and Foreign Key fields. You should also
convert the ProductID column from a Text Box control to a Combo Box control to display value
of Product Name. Set the Data Source property to ProductsBindingSource, the Display Member
to ProductName and the Value Member to ProductID.

When the Order Detail Binding Source was added, VS also added the following lines of code to
the OrdersForm_Load event to fill the Order Detail table:
Dim OrdersDetailTableAdapter As New Order_DetailsTableAdapter
OrdersDetailTableAdapter.Fill(Me.OrdersDataSet.Order_Details)

That is all we need to do. We can build and run our application by pressing F5 (or selecting
"Start Debugging from the Debug Menu.) Here is an example of the final form running. Notice
that the Navigation Bar allows us to move between records and the Order Detail information is
linked to the Orders record selected. Also in the navigation bar is a yellow asterisk button to
create a new record, a red X button to delete the current record, and a diskette button to save
changes.

Figure 11 - Final Application Running

Whereas VS2003 provided one inflexible wizard for RAD construction of master-detail form,
VS2005 has an infrastructure to allow RAD construction of a wide variety of form styles. The
Data Sources tool is a welcome improvement to Visual Studio.

Separating the Tiers

Once again Visual Studio has fostered bad habits in promoting RAD. To get the functionality
described above, the datasets need to be in the same project as the User Interface. But in the real
world, it is better to separate your business layer from your UI so if you decide to implement a
Web interface instead of Windows, much of the application will not need to change.
We can correct this easily enough. Create a new Class Library project in your solution for your
business objects. It is best to start out with this project and create the original DataSet there
instead of in the UI project.

Note: If you have all ready created the application as in the above tutorial, you can move the
DataSet we just created to the new project, but you need to also move the app.config file to keep
the connection strings.

Create a new Class Library project in your solution named NorthwindBiz and move the dataset
there. VS will actually make a new copy of the dataset, leaving the original alone. After copying
the dataset, you will need to remove the dataset in the Windows project. Your will also need to
search and replace the hard coded Namespace references, changing "NorthwindWin" to
"NorthwindBiz".

After the data set has been created, go


back to the form and open the Data
Sources pane. This time when you add
a new data source, instead of using the
Database tool, select the Object tool.
On the next screen, you will see a list
of referenced projects you could use. If
the reference is not shown, you will
need to click the "Add Reference"
button and select the NorthwindBiz
project. Expand the NorthwindBiz
project and select the OrdersDataSet
object. Click Next and Finish and you
will see the OrdersDataSet listed in the
Data Sources Pane.

If you actually create the business


project before creating the windows
project, you will notice a couple of
differences. The Wizard no longer
automatically instantiates the table
adapter in the form load event. Since
we are using an Object Data Source
which may not even be a dataset, VS
leaves the Data Access logic up to us.
We must write that code ourselves
now, but rather than write it in the Presentation layer, we need to write it in the Business layer.

Business Logic Code

If you wish to add business logic to your application, VS 2005 adds a Partial Class to the DataSet
to facilitate this. Right-click on the OrdersDataSet and select View Code to generate another file
under the DataSet with code stubs for the Partial Public Class where you can add code that will
not be overwritten when the dataset is rebuilt. Partial Classes are a great tool in .NET 2.0 where a
class may be separated into different physical files. When you build the project, the code will be
merged and compiled together. This is valuable where we have generated code and developer
code in the same class.

You can add partial classes for the DataTable, DataRow, and TableAdapter as follows:

Partial Public Class OrdersDataSet


Partial Public Class OrdersDataTable

End Class
Partial Public Class OrdersRow

End Class
End Class

Partial Public Class OrdersDataAdapter

End Class

If your business logic has to do with the whole table, add your code to the DataTable class. If
your logic has to do with validating a row, add the code to the DataRow class. If you wish to add
logic to the Data layer of your application, add the code in the Data Adapter class (note how it is
outside the DataSet class.)

When you build the dataset in the Windows project, VS automatically adds code to fill the
dataset and update back to the database. When you build the dataset in a separate business
project, VS assumes that logic will be built into the business object. This is wise because you
want to separate any data access logic away from the presentation layer. To build these
functions, we need to add a couple functions to the partial class for the dataset as follows.

Imports ta = NorthwindDataSetTableAdapters
Partial Public Class NorthwindDataSet

Private taOrders As New ta.OrdersTableAdapter


Private taOrderDetail As New ta.Order_DetailsTableAdapter
Private taCustomer As New ta.CustomersTableAdapter
Private taEmployee As New ta.EmployeesTableAdapter
Private taProduct As New ta.ProductsTableAdapter

Public Sub FillDataSetAll()


Me.taOrders.Fill(Me.Orders)
Me.taOrderDetail.Fill(Me.Order_Details)
Me.taCustomer.Fill(Me.Customers)
Me.taEmployee.Fill(Me.Employees)
Me.taProduct.Fill(Me.Products)
End Sub

First, we need to instantiate an object for each of the table adapters. Then we add a method that
will call the Fill method for each of the table adapters and fill dataset.
Other methods need to be added to update the tables. In this dataset, only the Orders and Order
Details tables need to be updated since all the others are for lookup only.

Public Sub UpdateOrders()


Me.taOrders.Update(Me.Orders)
End Sub
Public Sub UpdateOrderDetails()
Me.taOrderDetails.Update(Me.Order_Details)
End Sub
End Class

These few lines of code will provide access to the Data Access layer through the Business Logic
layer, providing the necessary separation from the presentation layer.

Now, back in the presentation layer, in the code behind the form, we need to call these methods
of the business object. You can create the code stub for the load even by double-clicking in the
title bar of the form and calling the method just created in the Business Layer of our application.

Partial Public Class OrderForm

Private Sub OrderForm_Load(ByVal sender As System.Object, _


ByVal e As System.EventArgs) Handles MyBase.Load
Me.NorthwindDataSet1.FillDataSetAll()
End Sub

Another piece of code needs to be added to save updates back to the database. There is a an icon
in the Navigation Bar for this for which code is written automatically when the dataset was in the
Windows project, but when using Object Data Sources, the Wizard leaves that for us to write.
You may need to change the enabled property for the button and then you can build a code stub
for the save event by double-clicking on the Save Item button in the navigation bar. The
following code needs to be added. Note that you must call the EndEdit method of the binding
source to force the form to update the dataset.

Private Sub bindingNavigatorSaveItem_Click(ByVal sender As System.Object,


ByVal e As System.EventArgs) Handles bindingNavigatorSaveItem.Click

Me.Validate()
Me.Order_DetailsBindingSource.EndEdit()
Me.OrdersBindingSource.EndEdit()
Me.NorthwindDataSet1.UpdateOrders()
Me.NorthwindDataSet1.UpdateOrderDetails()
End Sub
End Class

You can once again press F5 to run the application to see that the data is filled in and all the
same functions exist. If any changes to the data are made, the user must click the save button to
write the changes back to the database.

Summary
The new version of Visual Studio 2005 offers significant improvements in the flexibility of
creating multi-tiered application using RAD tools. The Typed Dataset generation tool allows
data abstraction between the database and the UI/Business Logic and also provides Object
Oriented access to the entities and attributes of a database. The Dataset generator also provides
the Data Access code virtually separated into a separate layer. The Partial Classes feature gives
us a nice place to implement Business Logic layer with complete access to the Dataset
component.

The Binding Sources component isolates the UI from the Business Logic layer and provides a
smooth data binding tool, transforming our application into more of a 4-tiered application.
Finally, the Data Sources window allows rapid generation of the Presentation layer and
preserving the n-tiered environment.

While we do have to write a few lines of code to fill the dataset--in a few minutes, we have
generated a good multi-tier application and the only code we have to write is to implement our
unique business logic.

What's Next?

In Part 3 of the series, I will look more closely at what can be done in the Business Logic layer to
enhance our application. I will also show some more code generation tools to help write some of
the Data

You might also like