Have you ever needed to say replace the location of your images in your application from one location to another, or had a database routine that ran nightly and you wanted to gracefully control access to your application during that process?  In this article I am going to show how I approached this on one of my projects and have been pretty happy with it so far.

Here's the 10,000 foot view of the solution.  Build a SQL table that has 2 varchar(255) columns, one named AppVarName and the other AppVarValue.  Build a class that checks to see if the DataTable exists in the cache, if not retrieve the DataTable from SQL and place it in the cache.  The class then loops through the rows in the DataTable creating and assigning application variables with the AppVarName field from the table and populates it's value from the AppVarValue field.  Place a call to this class in the Application_start, and Application_BeginRequest methods in the Global.asax.  Now in all your pages you have access to this information. 

I personally use this in my Master page, in the Page_Load method I check the value of "ApplicationStatus" which I've created in my table and assigned a value of "1" to.  If the value is "1" I do nothing, if the value is "0" I redirect to a friendly "Down For Maintenance" page.  This allows me to set the value in say a reindexing routine and then change it back when it's finished.  I also use different status values to enable or disable certain features of my application, this allows me a lot of flexibility.

Now on to the code snippets.  For this article I am using the Enterprise Library Data Access Application Block, if you don't use this it's not a big deal and I think you can see how to adapt this to your own data access pattern. I'll attach the code to the post, but here's the "gist" of it...please pardon any skipped steps...refer to the attached solution for the details.

First make a table called ApplicationSettings with 2 varchar(255) columns one named AppVarName and the other named AppVarValue.

Next make a stored procedure named GetApplicationSettings which reads simply like

SELECT AppVarName, AppVarValue From ApplicationSettings

Now create a blank solution.  Add a C# Class Library Project, and a C# ASP.NET web site.

Add the AppManager Class to your Library:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Data;

using System.Web;

using Microsoft.Practices.EnterpriseLibrary.Data;

using System.Data.Common;

namespace Web.AppManager

{

public class ApplicationVariableManager

{

public void UpdateApplicationVars()

{

string cacheKey = "ApplicationVariables";

int cacheDuration = 0;

DataTable dtVariables = HttpRuntime.Cache[cacheKey] as DataTable;if (dtVariables == null)

{

Database myDatabase = DatabaseFactory.CreateDatabase("myConnectionString");

using (DbCommand dbCommand = myDatabase.GetStoredProcCommand("GetApplicationSettings"))

{

using (DataSet myds = myDatabase.ExecuteDataSet(dbCommand))

{

dtVariables = myds.Tables[0].Copy();

}

}

foreach (DataRow myDataRow in dtVariables.Rows)

{

if (myDataRow.Field<string>("AppVarName") == "SettingsCache")

{

cacheDuration = Convert.ToInt32(myDataRow.Field<string>("AppVarValue"));

}

if (cacheDuration == 0)

{

cacheDuration = 300;

}

}

HttpRuntime.Cache.Insert(cacheKey, dtVariables, null, DateTime.Now.AddSeconds(cacheDuration), TimeSpan.Zero);

}

foreach (DataRow myDataRow in dtVariables.Rows)

{

HttpContext.Current.Application[myDataRow.Field<string>("AppVarName")] = myDataRow.Field<string>("AppVarValue");

}

}

}

}

Add a reference in your web app to the Class Library

Create a Global.asax and add update the Application_Start and Application_BeginRequest methods to look like this:

protected void Application_Start(object sender, EventArgs e)

{

Web.AppManager.ApplicationVariableManager AppManager = new Web.AppManager.ApplicationVariableManager();

AppManager.UpdateApplicationVars();

}protected void Application_BeginRequest(object sender, EventArgs e)

{

Web.AppManager.ApplicationVariableManager AppManager = new Web.AppManager.ApplicationVariableManager();

AppManager.UpdateApplicationVars();

}

 

Add a check in your Page_Load of your site master's code behind this check:

if (Convert.ToInt32(Application["ApplicationStatus"])==0)

Response.Redirect("/Down.aspx");

Now make a Down.aspx page and make sure your Default.aspx page is using your site master.  Voila.  The CacheSettings variable allows you to tinker with how long that variables are cached.

All standard disclaimers apply....works on my machine.  Let me know if you have questions or can improve it (I am sure there is room there for improvement). Here's the code.

So I'm headed to my first TechEd.  I'm really excited.  The presentation list is huge!  I think I've got them all mapped out I think like 27 in 4 days...crazy.  It's almost like a developer pilgrimage.  I will also be twittering a lot so check out #TechEd (I think that'll be the group name...I'll update if it's different).

Edit: Updated the twitter tag

So in researching another series I am writing I got a wild desire to do some Linq to SQL performance testing.  So I whipped up a database table and populated it with data and went about writing some code.  What you'll see in the code is 4 tests one with a fairly standard Linq query and a loop through the results (just to make sure the lazy loading has been done), a Linq query using a sproc (again looping through the results), a fairly standard T-SQL query DataReader which loops through the results and creates a List of objects (to simulate the collections that Linq returns) and a stored procedure using the same query (again looping through the results to simulate a result from Linq).  The idea with the 'standard' methods of T-SQL and the Stored Procedure was to return the same slick collections of 'objects' that Linq produces, so to that end I made a small POCO that resembles our result set.

What's interesting is that for larger record sets Linq to Sql slows way down and DataReader speeds up.  For smaller record sets the opposite is true.

However I think something shifty is going on because the Linq methods 'feel' faster...meaning I get to the command prompt faster and with the Data Reader there is a pause after it displays the elapsed time. 

My database table is fairly simple:

CREATE TABLE [dbo].[Contact](

[contactid] [int] IDENTITY(1,1) NOT NULL,

[contactfirstname] [varchar](50) NOT NULL,

[contactlastname] [varchar](50) NOT NULL,

[contactdate] [datetime] NOT NULL

) ON [PRIMARY]

My Poco class looks like:

    public class MyContact
    {
        private int _contactid;
        public int ContactID { get; set; }

        private string _contactfirstname;
        public string ContactFirstName { get; set; }

        private string _contactlastname;
        public string ContactLastName { get; set; }

        private DateTime _contactdate;
        public DateTime ContactDate { get; set; }

        public MyContact(int contactid, string contactfirstname, string contactlastname, DateTime contactdate)
        {
            _contactid = contactid;
            _contactfirstname = contactfirstname;
            _contactlastname = contactlastname;
            _contactdate = contactdate;
        }

    }

And here is my actual code:

   class Program
    {
        public static string connstring = "Data Source=localhost;Initial Catalog=TestDB;
     Persist Security Info=True;User ID=perftest;Password=12345";

        private static void UseLinq(int i)
        {
            using (ContactsDataContext ctx = new ContactsDataContext())
            {
                var cont = from p in ctx.Contacts
                           select p;
                foreach (Contact c in cont)
                {
                    i = i + 1;
                    //Console.WriteLine(i.ToString());
                }
            }
        }
        private static void UseSPLinq(int i)
        {
            using (ContactsDataContext ctx = new ContactsDataContext())
            {
                var cont = from p in ctx.GetContacts()
                           select p;
                foreach (var mycontact in cont)
                {
                    i = i+1;
                   
                    //Console.WriteLine(i.ToString());
                }
            }
        }
        private static void TSQLDataReader(int i)
        {
            using (SqlConnection conn = new SqlConnection(connstring))
            {
                conn.Open();
               
                SqlCommand myCommandObject = new SqlCommand();
                myCommandObject.CommandType = CommandType.Text;
                myCommandObject.CommandText = "select contactid, contactfirstname,
      contactlastname, contactdate from contact";
                myCommandObject.Connection = conn;

               
                using (SqlDataReader MyDataReader = myCommandObject.ExecuteReader())
                {
                    IList<MyContact> myList = new List<MyContact>();
                    while (MyDataReader.Read())
                    {
                        MyContact mytmpContact = new MyContact(
      (int)MyDataReader[0],
      MyDataReader[1].ToString(),
      MyDataReader[2].ToString(),
      (DateTime)MyDataReader[3]
      );
                        myList.Add(mytmpContact);
                    }
                    foreach (MyContact mc in myList)
                    {
                        i = i + 1;
                        //Console.WriteLine(i.ToString());
                    }
                 
                }
                conn.Close();
                myCommandObject.Dispose() ;
            }
        }
        private static void SPDataReader(int i)
        {
            using (SqlConnection conn = new SqlConnection(connstring))
            {
                conn.Open();
               
                SqlCommand myCommandObject = new SqlCommand();
                myCommandObject.CommandType = CommandType.StoredProcedure;
                myCommandObject.CommandText = "GetContacts";
                myCommandObject.Connection = conn;

                using (SqlDataReader MyDataReader = myCommandObject.ExecuteReader())
                {
                    IList<MyContact> myList = new List<MyContact>();
                    while (MyDataReader.Read())
                    {
                        MyContact mytmpContact = new MyContact(
      (int)MyDataReader[0],
      MyDataReader[1].ToString(),
      MyDataReader[2].ToString(),
      (DateTime)MyDataReader[3]
      );
                        myList.Add(mytmpContact);
                    }
                    foreach (MyContact mc in myList)
                    {
                        i = i + 1;
                        //Console.WriteLine(i.ToString());
                    }
                 
                }

                conn.Close();
                myCommandObject.Dispose();
            }
        }
        static void Main(string[] args)
        {
            int i = 0;
            Stopwatch mystopwatch = new Stopwatch();
            mystopwatch.Start();

            
            if (args[0] == "StraightLinq")
            {
                UseLinq(i);
            }

            if (args[0] == "SPLinq")
            {
                UseSPLinq(i);
            }

            if (args[0] == "T-SQL_DataReader")
            {
                TSQLDataReader(i);
               
            }

            if (args[0] == "SP_DataReader")
            {
                SPDataReader(i);

            }

    
            mystopwatch.Stop();
            TimeSpan ts = mystopwatch.Elapsed;
            string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}",
            ts.Hours, ts.Minutes, ts.Seconds,
            ts.Milliseconds);
            Console.WriteLine(elapsedTime, "RunTime");
           
        }
    }

Here's my results for 50,000 records:

Avg (5 Runs) Speed Difference
StraightLinq 0.5554 2.986022
SPLinq 0.4242 2.280645
T-SQL_DataReader 0.1886 1.013978
SP_DataReader 0.186 1

 Here's my results for 1,000 records:

Avg (5 Runs) Speed Difference
StraightLinq 0.4984 1.372247
SPLinq 0.3632 1
T-SQL_DataReader 0.954 2.626652
SP_DataReader 0.948 2.610132

Are these results similar to what you are finding or is there an issue with my testing methodolgy?  Drop a comment if you have suggestions on how to improve or correct this test.

 Edit: Ugh these layouts are bad for wide data...sorry about that.

Edit: The extra for each loop in the T-Sql and SP versions of the test were the culprit to the higher values in the 1000 record test.  With those loops removed the times were about .17

In the previous posts we dropped a table from the Server Explorer onto our design surface and saw how a SqlDataSource was created with T-SQL statements to populate the basic functions of our control.  In the second part we replaced our T-SQL with stored procedures and saw how this can help us maintain our code.  However this is method still tightly couples our presentation layer to our data layer (what there is of one).  While I'm not comfortable saying this is always wrong...it's definately not always right.  For RAD (rapid application development), quick prototyping and short lived specialized applications this may be sufficient for you.  However if your application is going to be maintained and grow over a longer period of time, you will quickly find that the SqlDataSource will become a sticky issue.

Consider this scenario:  You have a dropdown list of some value on your page that you want to populate with data from a SqlDataSource.  So you write a stored procedure to return your list.  You add the SqlDataSource to your page.  Add your dropdown list control and bind it to your datasource and blammo...working page.  Two months later a business rule has sprung up that certain values from that list will be excluded based on other conditions within the application.  Let's assume for a moment that those "conditions" are unique to the user that is currently logged into your application and are not persisted in the database.  Meaning that we cannot use our stored procedure to limit the result set.  Now we are forced with overriding the binding behavior of our dropdown list.  I personally avoid overriding the behavior of standard controls in all but the most necessary scenarios.  In my view it leads to very difficult to maintain code.  A 'better' solution to this is to create a business object that controls the data being returned to the application, and then the application doesn't need to do anything fancy it just binds that data to the control.

It all comes down again to seperation of concerns.  The Data Access Layer (the database and objects that get data from it) does data access, the Presentation Layer (the html/windows form) presents, and the Business Logic Layer (classes that sit between the Data Access and Presentation layers) makes all the decisions.  While this is what is highly desired there are practical reasons that these roles blend from time to time...the key is to limit them whenever possible.  I prefer to have all my presentation layer pages (or Windows Form Elements) be as 'dumb' as possible.  So in the scenario above the SqlDataSource lets us down in that regard.  However, having said that there is a time and place for everything so knowing the basics of how to use them is essential.

Mark Twain said: "To the man with a hammer, everything looks like a nail."  Don't let SqlDataSource be your hammer.  Likewise remember that it is there and can help you with some tasks.

I'm going to leave the SqlDataSource alone for a while and in the next post I'll start discussing the ObjectDataSource and building some basic classes and show you a couple of fun and easy data access patterns.

Ok so I made a mistake when I kicked off this series...I picked Sql Server Express as our database for all the logical reasons...however I failed to remember that Sql Server Express doesn't do Sql Cache Dependency...so instead of retro fitting my series to use Sql Server 2005 I'll punt and just say (for now) that the Sql Data Source can cache the results of your queries to improve performance and can even take advantage of cache dependencies (provided you are using Sql Server 2005).

So I am going to leave caching out of the series for the moment.  I will come back to it at the end of the series and show it in action.

Please be gentle with me...I'm not a professional blogger. :-)

Ryan

In our last post we showed what happened when you drag and drop a table onto the design surface, a fully functional (including sorting, paging, updating and deleting) SqlDataSource with T-SQL populating the command fields. While this scenario makes for a slick demo, it's not ideal.  The reason I say it's not ideal is that mixes data logic right into the aspx markup.  This makes making changes in the underlying data routines risky as the markup and data structures tightly coupled.  One way we can reduce these risks is by replacing the T-SQL statements that were auto generated for us, with stored procedures.  By replacing the T-SQL with stored procedures we can alter our database logic without having to touch the aspx markup.  The first task in this is to create the stored procedures.  Here's the SQL for the stored procedures:

CREATE PROCEDURE dbo.DeleteCustomer

@customerid varchar(10)

AS

delete from customers where customerid = @customerid

RETURN

CREATE PROCEDURE dbo.InsertCustomer

@CustomerID varchar(50),

@CompanyName varchar(50),

@ContactName varchar(50),

@ContactTitle varchar(50),

@Address varchar(50),

@City varchar(50),

@Region varchar(50),

@PostalCode varchar(50),

@Country varchar(50),

@Phone varchar(50),

@Fax varchar(50)

AS

INSERT INTO [Customers] ([CustomerID], [CompanyName], [ContactName], [ContactTitle], [Address], [City], [Region], [PostalCode], [Country], [Phone], [Fax])

VALUES (@CustomerID, @CompanyName, @ContactName, @ContactTitle, @Address, @City, @Region, @PostalCode, @Country, @Phone, @Fax)

RETURN

CREATE PROCEDURE dbo.SelectAllCustomers

AS

Select * from customers

RETURN

CREATE PROCEDURE dbo.UpdateCustomer

@CustomerID varchar(50),

@CompanyName varchar(50),

@ContactName varchar(50),

@ContactTitle varchar(50),

@Address varchar(50),

@City varchar(50),

@Region varchar(50),

@PostalCode varchar(50),

@Country varchar(50),

@Phone varchar(50),

@Fax varchar(50)

AS

UPDATE [Customers] SET

[CompanyName] = @CompanyName, [ContactName] = @ContactName,

[ContactTitle] = @ContactTitle, [Address] = @Address, [City] = @City,

[Region] = @Region, [PostalCode] = @PostalCode, [Country] = @Country,

[Phone] = @Phone, [Fax] = @Fax WHERE [CustomerID] = @CustomerID

RETURN

Now that we have our stored procedures created, we can use the design surface to update our SqlDataSource control to use the procs.  So... go to the design view and click the smart tab thingy on the SqlDataSource control, and select configure data source. We'll keep our connection string intact so hit 'next'.  From the next page which should read Configure the Select Statement, select the radio button that reads "Specify a custom SQL statement or stored procedure and click 'next'. Now click the stored procedure radio button and use the pulldown to select the stored procedure named: SelectAllCustomers. Don't hit 'next' yet, use the tabs at the top to do the same for the Update, Delete and Insert tabs, using the appropriate stored procedure.  Now click 'next' and then finish.  Let's take a look at what the markup of our aspx page looks like now:

<asp:SqlDataSource ID="SqlDataSource1" runat="server"

ConnectionString="<%$ ConnectionStrings:NORTHWNDConnectionString1 %>"

DeleteCommand="DeleteCustomer"

InsertCommand="InsertCustomer"

ProviderName="<%$ ConnectionStrings:NORTHWNDConnectionString1.ProviderName %>"

SelectCommand="SelectAllCustomers"

UpdateCommand="UpdateCustomer" DeleteCommandType="StoredProcedure" InsertCommandType="StoredProcedure" SelectCommandType="StoredProcedure"

UpdateCommandType="StoredProcedure">

<DeleteParameters>

<asp:Parameter Name="CustomerID" Type="String" />

</DeleteParameters>

<InsertParameters>

<asp:Parameter Name="CustomerID" Type="String" />

<asp:Parameter Name="CompanyName" Type="String" />

<asp:Parameter Name="ContactName" Type="String" />

<asp:Parameter Name="ContactTitle" Type="String" />

<asp:Parameter Name="Address" Type="String" />

<asp:Parameter Name="City" Type="String" />

<asp:Parameter Name="Region" Type="String" />

<asp:Parameter Name="PostalCode" Type="String" />

<asp:Parameter Name="Country" Type="String" />

<asp:Parameter Name="Phone" Type="String" />

<asp:Parameter Name="Fax" Type="String" />

</InsertParameters>

<UpdateParameters>

<asp:Parameter Name="CustomerID" Type="String" />

<asp:Parameter Name="CompanyName" Type="String" />

<asp:Parameter Name="ContactName" Type="String" />

<asp:Parameter Name="ContactTitle" Type="String" />

<asp:Parameter Name="Address" Type="String" />

<asp:Parameter Name="City" Type="String" />

<asp:Parameter Name="Region" Type="String" />

<asp:Parameter Name="PostalCode" Type="String" />

<asp:Parameter Name="Country" Type="String" />

<asp:Parameter Name="Phone" Type="String" />

<asp:Parameter Name="Fax" Type="String" />

</UpdateParameters>

</asp:SqlDataSource>

Again it's a little out of order, but notice how our SelectCommand, DeleteCommand, UpdateCommand and InsertCommands have been replaced with their respective stored procedure names.  Additionally a new CommandType parameter is paired with each Command, in our case it reads "StoredProcedure".

And now the SqlDataSource is completely up and running again, having replaced the T-SQL with stored procedures. 

There are a couple of advantages to using stored procedures with the SqlDataSource.  The first being that now you can change the logic for your CRUD statements in the stored procedures, rather than having to edit the aspx markup.  This is a big step forward in promoting the principal of loosly coupling our presentation layer (HTML and the aspx markup) and business and data layers.  However it's still coupled pretty tightly as we have the name of our stored procedure in our markup.  So, if we needed to change the name of the stored procedure in the database, we'll also have to modify it in the aspx markup.  Ideally we'd have another layer seperating the database and the aspx markup...which is where some other data source controls come in.  But for now we've at least made it easier to modify the logic.

In the next post I'll delve into the caching properties of the SqlDataSource and a summary of the pro's and con's of the SqlDataSource.

Further Reading:

Stored Procedures - Wikipedia

Welcome back!

If you are following along from the last post, you'll notice that two controls were automatically created on our page when we dropped the Customer table onto the page. The first control is called a GridView.  The GridView control is used to display data in much the same way that the SQL manager shows you results when you have run a query directly out of it (that is row by row).  The GridView is very powerful and flexible; we'll go into the GridView control a little deeper in later posts but for now let's focus on how we are getting data from the database to the page. 

The SqlDataSource serves as the plumbing that connects the web page to the database.  Let's take a look at how Visual Studio created our control and talk a little bit about the parameters it set up for us. 

Here's the code it created for us:

        <asp:SqlDataSource ID="SqlDataSource1" runat="server"
            ConnectionString="<%$ ConnectionStrings:NORTHWNDConnectionString1 %>"
            DeleteCommand="DELETE FROM [Customers] WHERE [CustomerID] = @CustomerID"
            InsertCommand="INSERT INTO [Customers] ([CustomerID], [CompanyName], [ContactName], [ContactTitle],

[Address], [City], [Region], [PostalCode], [Country], [Phone], [Fax]) VALUES (@CustomerID,

@CompanyName, @ContactName, @ContactTitle, @Address, @City, @Region, @PostalCode, @Country, @Phone, @Fax)"
            ProviderName="<%$ ConnectionStrings:NORTHWNDConnectionString1.ProviderName %>"
            SelectCommand="SELECT [CustomerID], [CompanyName], [ContactName], [ContactTitle], [Address], [City], [Region],

[PostalCode], [Country], [Phone], [Fax] FROM [Customers]"
            UpdateCommand="UPDATE [Customers] SET [CompanyName] = @CompanyName, [ContactName] = @ContactName,

[ContactTitle] = @ContactTitle, [Address] = @Address, [City] = @City, [Region] = @Region,

[PostalCode] = @PostalCode, [Country] = @Country, [Phone] = @Phone, [Fax] = @Fax WHERE [CustomerID] = @CustomerID">
            <DeleteParameters>
                <asp:Parameter Name="CustomerID" Type="String" />
            </DeleteParameters>
            <InsertParameters>
                <asp:Parameter Name="CustomerID" Type="String" />
                <asp:Parameter Name="CompanyName" Type="String" />
                <asp:Parameter Name="ContactName" Type="String" />
                <asp:Parameter Name="ContactTitle" Type="String" />
                <asp:Parameter Name="Address" Type="String" />
                <asp:Parameter Name="City" Type="String" />
                <asp:Parameter Name="Region" Type="String" />
                <asp:Parameter Name="PostalCode" Type="String" />
                <asp:Parameter Name="Country" Type="String" />
                <asp:Parameter Name="Phone" Type="String" />
                <asp:Parameter Name="Fax" Type="String" />
            </InsertParameters>
            <UpdateParameters>
                <asp:Parameter Name="CompanyName" Type="String" />
                <asp:Parameter Name="ContactName" Type="String" />
                <asp:Parameter Name="ContactTitle" Type="String" />
                <asp:Parameter Name="Address" Type="String" />
                <asp:Parameter Name="City" Type="String" />
                <asp:Parameter Name="Region" Type="String" />
                <asp:Parameter Name="PostalCode" Type="String" />
                <asp:Parameter Name="Country" Type="String" />
                <asp:Parameter Name="Phone" Type="String" />
                <asp:Parameter Name="Fax" Type="String" />
                <asp:Parameter Name="CustomerID" Type="String" />
            </UpdateParameters>
        </asp:SqlDataSource>

There's a lot in there right?  Imagine if you had to type all that out!

So what you've got there is a control that will handle all of your CRUD (Create, Retrieve, Update and Delete) operations for the Customer table. Let's go through the properties and parameters a little. 

They're a little out of order but that's okay.

The first one is pretty self-explanatory, that's the ID.  It's just a unique name to identify the control.  We can use this ID in our code behind page if we need to make any programatical changes to our control.

The next one is really important: it's the connection string that resides in our Web.Config and was put there by Visual Studio when we dropped it on the page.  Notice that it's surrounded by the ASP markers, the reason for this is that the ConnectionStrings: object is a little bit of design time magic.  It has a reference to the ConnectionsStrings XML node in the Web.Config file.  There's a related one called ProviderName, this one dictates the type of database to be used, according to our Web.Config file.  Our sample will be using System.Data.SqlClient meaning it will be using Sql Server or Sql Server Express.   However you could conceivably use any database if there was an available provider.

Now we'll move on to the commands.  Visual Studio was nice enough to create T-Sql statements for each of the commands.  The commands created are pretty basic as you can see as they are just CRUD statements.

Take a look at the SelectCommand, it's very a standard Sql select statement, and Visual Studio was even so considerate to surround the fields with []'s in case your field's had spaces etc in them. Just because Visual Studio created T-Sql doesn't mean we're stuck with T-Sql, we can easily use Stored Procedures here, we'll show those off in the next post.

For each of the other Command statements, parameters have be created and integrated into the T-Sql. Let's take a look at them a little deeper.

Check out the DeleteCommand.  When you are deleting a record, you need a way to identify the record you want to delete.  By default Visual Studio uses the table's primary key, in this case it's CustomerID. This is almost enough to get the job done, however since these are Sql Parameters we need to know a little more about the parameter we are passing.  This is where the DeleteParameters section comes in.  In this section we define what type of data the parameter is.  In this case it's a string.

The rest of commands and parameters are more of the same, standard Sql statements and parameters, so I won't bore you by going through each of the others.

When a data control is bound to this SqlDataSource, these commands will be executed when requested by the data control.

If you take a few moments to examine the properties of the SqlDataSource control in Visual Studio, you'll find some interesting stuff.  We'll go into those in a future post and then finish with a look back to highlight the pros and cons of the SqlDataSource.  But now I'm off to get ready for the next post where we'll convert these T-sql statements into stored procedures.

Further Reading:

W3Schools Sql Tutorial

SqlDataSource Web Server Control Overview - Microsoft

Scott Mitchell's Awesome Data Access Tutorial Series

Ok so you have Visual Studio or Web Developer Express installed, have installed SQL Server Express, and have downloaded and run the SQL Samples from the previous post.

Lets create our Web Project, select Create Project and select either a C# or a VB.Net ASP.NET Web Application, you can create this project anywhere you want, for reference I'm just putting mine locally and using the built in web server.

Now we'll add the Northwinds Database to our project.  Right click on the App_Data and select Add->Existing Item and navigate to the C:\SQL Server 2000 Sample Databases folder or wherever you installed them and selecting Northwnd.mdf and select 'Add'.  It should now appear under your App_Data directory.

Now let's test out connection by doing one of the traditional 'Whizz Bang' marketing demos, where we'll drop a table from Northwinds right on to the page and kapow data displayed without writing a single line of code.

First, open up your default.aspx page and switch to design view.

Next make sure you have the Server Explorer window open, if it's not open you can open it by selecting View->Server Explorer.

Next in the Server Explorer window, expand the Northwind Data Connection, then expand the the Tables folder. 

Now for the fun part select the Customer table and drag and drop it on to the blank design surface.  Kapow!  Now let's view it in the browser by right clicking on the design surface and selecting View In Browser.  There you have it a bound data control with zero lines of code. Please see my note below for a little trouble I had with VS2008 and Vista.

So let's see what happened, first a SqlDataSource (In the next post I'll describe the SqlDataSource and some of it's features) was created with parameters that selected all the rows from the Customer table.  Then a GridView was placed on the page and bound to the SqlDataSource.  All this was done automagically for you.

Ok so now our environment is all set.  Feel free to play around with the GridView, try using the themes and fiddle with the properties.

In the next post I'll discuss the SqlDataSource in more detail and discuss the pros and cons of the control.  In some later posts I'll go into detail in how you can take control of the GridView and override some of it's default behaviors including nested data sources and additional GridViews.

Note: I'm running Vista Ultimate 32bit, and when I first tried to view the web page I got an error page.  This happened to any page I created even if it just had text.  Turns out it has something to do with IPv6 on Vista...after looking around I found Rob Bazinet's blog The Accidental Technologist in which he encountered the same problem.  Through discussions on the blog a work around was presented to edit the hosts file and comment out the line that contains ::1 Check the link to the post for details.  What was really weird is that I know I've created sites just fine before...so I don't know if it was a recent update that did it...but it works now...weird.

Today I'm starting a new series of posts regarding Data Access methods and Data Binding with focus on the new developer.  There are a multitude of ways to access data with ASP.NET and I will attempt to go through as many as I can, hopefully digging deep enough for you to get a solid understanding of the pros and cons of each method.  We'll be beginning with some of the traditional methods of straight ADO.NET (don't worry if you're lost already we'll get you caught up) and then look at ways we can leverage some best practices and techniques to enhance how we use them and make our lives easier, including the use of the Enterprise Library.   Then we will move on to some of the new technologies including LINQ and the Entity Framework.  For the binding aspect of the series I will mostly be sticking with the Gridview and the FormView, in the later posts I may branch out to third party controls but I will be keeping it simple to start.

Each post will include Visual Basic and C# code.

Through the tutorial I will be making the assumption that you are using VS2008 or Visual Web Developer 2008 Express Edition. We'll be using SQL Server 2005 Express Edition as our database.  So if you need either of those please visit their respective links and install them now and apply any necessary updates.

For the actual data itself we'll be using the AdventureWorksLT database which is a lighter version of the AdventureWorks Database, it should suffice for what we'll be showing.  Where it comes up short I'll address that (I'm specifically thinking about a section we'll be covering with a LARGE amount of rows).

UPDATE: After some suggestions I've decided to use Northwind instead.  This will make the demos and setup  a little simpler.

A lot of the material I will be presenting is probably available in different forms in other places on the web so I will have a "Further Reading" section in most posts so that you can do your own research where necessary.  What I hope to bring to the table is some 'real world' application of the techniques and a dialog on the good, the bad and the ugly.  I'll show you where the fancy "oohahh" presentations that Microsoft likes to show fall short and where they are appropriate.

In the next post (I'm shooting for Sunday April 13th) I'll walk you through setting up our development environment which will primarily just include the referencing of the AdventureWorksLT Northwind database and making sure we are ready to go. 

Here's the next in the series: Data Access Project Setup