Wednesday, 27 May 2009

IT Galla 2009

For those of you lucky to be around Stavanger on the June 4th ErgoGroup is holding its annual IT event, IT Galla

I will be up in the TechnoLoft which is the hands on area of IT Galla where you can ask the experts, play with the products and generally take a break from the sessions if you are so inclined.

Come and visit if you are in the area. Registration here

Upgrades upgrades upgrades

One of the unfortunate parts of certification is the whole need to upgrade to the newest version of the certification path that you are on. Its a necessary evil.

Thankfully there are upgrade exams which shorten this process and also make it less expensive allowing you to shortcut the amount of time for exams.

I have begun my upgrades now beginning with the MCPD Web 2.0 to MCPD ASP.NET 3.5 which is exam 70-567.

This is a two part exam which comprises of the ASP.NET MCTS and the MCPD ASP.NET Pro exam.

Hopefully I will get to document the progress for this and what I did.

Further updates coming

Thursday, 21 May 2009

Silverlight Virtual Earth Map Control

This is a newly released control which is available through the Microsoft Connect site.

This new control allows you to use the rich display capabilities of Silverlight to display your geographical data.

There is a great tutorial on it available here 

I will be doing an update of some code from earlier to use this new control.

Test Windows 7 XP Mode

I went looking for this and you can download the beta from here

There is some concern that it may be issues with this feature as it requires AMD-V or Intel VT enabled on your machine which not all machines may have. But try it out and see what happens

Wednesday, 13 May 2009

Fixing the 403 error with Live Writer and publishing posts with images

On Blogger you can receive a 403 error when you create a post that contains embedded images. You can publish without images ok

The issue relates that WLW publishes the images to Picassa and your account may not be set up. So login to to fix this and you should be able to post images to your blog

More shopping cart (cowbell!)

Right our little shopping cart demo is starting to take shape and look ok.

But there is some issues with it.

  • You cannot update the amount in the cart
  • Adding existing items changes the order of the cart
  • Some repeated code

So lets do some changes.

To add the an update to the cart items, we need a text box that we can update.

In the repeater _Cart we need to change the amount row to

<asp:TextBox ID="_Amount" runat="server" Text="<%# ((CartItem)Container.DataItem).Amount.ToString(CultureInfo.InvariantCulture) %>"></asp:TextBox>

This wraps the Amount value in a textbox and sets the text to the current amount.

So if we build and view it in the browser we now have a nice textbox in our shopping cart


But we now also have to add some code to handle this in the code behind. In the repeater item event handler for _Cart we will need to add some changes.

private void CartItemCommand(object source, RepeaterCommandEventArgs e)
           if (Session["Cart"] == null) return; //No session just return

           //Check what button was pressed and do stuff

           switch (e.CommandName)
               case "Remove":
                       var cartItems = (List<CartItem>)Session["Cart"];
                       //since the session is a controlled list, just remove at the index
                       //Assign back to session
               case "Update":
                       var amount = (TextBox)e.Item.FindControl("_Amount");

                       int intAmount;
                       Int32.TryParse(amount.Text, out intAmount);//will set the value to 0 if cannot parse
                       var cartItems = (List<CartItem>)Session["Cart"];
                       cartItems[e.Item.ItemIndex].Amount = intAmount;


First we check to see if we have the cart in session and then do a switch statement on CommandName so that we can determine what we need to do. So in the Update method, we look for the control _Amount which is our TextBox and cast it as such.

We need to set the value of the Amount and first you should try and make sure its an integer.

Also there is a function UpdateSessionAndLabels which manages putting the cart back into session and rebinding the Cart

private void UpdateSessionAndLabels(List<CartItem> cartItems)
            Session["Cart"] = cartItems;
            _totalPrice = 0;
            _numItems = 0;
            _CartSize.Text = _numItems.ToString();
            _FullPrice.Text = _totalPrice.ToString();

This just makes life easier because we dont need to repeat as much code.

Right now we can update the amount in our shopping cart. Lets take a look at the changing order when we add existing products.

var hasitem = cartItems.Find((ci => ci.Id == cartItem.Id));
            //If the item exists, get its current amount and add 1.
            if (hasitem != null)
                cartItem.Amount = 1;
            //put the cart back into the session state

We need to see if the item exists still. But now we just access it by the index and update the amount. Otherwise set the value to 1 and add it to the list.

A little bit of a tidy up and some extra functionality to this demo

You can download the code from here

Saturday, 9 May 2009

Continuing with the shopping cart

In the previous post we updated the system to use the 3.5 version of the framework. Now we are going to continue with that and plug some of the elements together such as the dropdown and the catalog page.

The DropdownDemo.aspx file demonstrated how to make a simple cascading dropdown in .NET. This next part will show how to add that particular functionality to our catalog page so that we can show products from a particular specialisation.

Now I use the naming Category, Sub Category and Specialisation to show the three layers of the cascade. The way that the tables are set up shows how this works


As you can see, each Sub Category must have a parent Category and each Specialisation must have a Sub Category. The foreign key constraints ensure that this relationship is maintained correctly.

What we need now is a way to connect the products to the Specialisation table and we do this using a link table. The means that every product can be part of one or more specialisations allowing you to have common products appearing in more than one spec.

This table just contains an identity column which serves as the primary key and 2 other integer columns representing the ProductID and the SpecID which are both foreign keys. You could also create a unique constraint on the two columns meaning that a product couldn’t be added to the same specialisation twice

So the database diagram looks something like this


So lets look at adding the cascading dropdowns to the catalog page and see where it leads.

So if we open the source of the DropdownDemo.aspx file we can copy the three dropdowns into our existing catalog.aspx page

<table class="style1">
            <asp:DropDownList ID="Category" runat="server" Height="20px" Width="300px"
            Sub Category</td>
            <asp:DropDownList ID="SubCategory" runat="server" Width="300px"
            <asp:DropDownList ID="Specialisation" runat="server" Width="300px">

Paste it in just below the label for the shopping cart.

Now we need to wire it up.

So copy the three bind functions from the code behind file and add them to the code behind file of catalog.aspx

private void BindCategory()
          Category.DataSource = CategoryBl.GetAllCategories();
          Category.DataValueField = "Id";
          Category.DataTextField = "Name";

      private void BindSubCat()
          SubCategory.DataSource = CategoryBl.GetAllSubCats(Int32.Parse(Category.SelectedValue));
          SubCategory.DataValueField = "Id";
          SubCategory.DataTextField = "Name";

      private void BindSpec()
          Specialisation.DataSource = CategoryBl.GetAllSpecs(Int32.Parse(SubCategory.SelectedValue));
          Specialisation.DataValueField = "Id";
          Specialisation.DataTextField = "Name";

Next we can copy the event handler methods from the dropdowndemo code behind file to the catalog code behind file.

private void SubCategory_SelectedIndexChanged(object sender, EventArgs e)

        private void Category_SelectedIndexChanged(object sender, EventArgs e)

And the next bit of copying is adding the event handlers in the OnInit method of the page. So just copy the 2 lines and add them to the same part in the Catalog.aspx.cs file

Category.SelectedIndexChanged += Category_SelectedIndexChanged;
SubCategory.SelectedIndexChanged += SubCategory_SelectedIndexChanged;

Finally lets wire this up for the page load so copy the three lines from the page load event and paste them after the _Catalog.DataBind();


Right then, compile and check its all working that you are getting your dropdowns to work.

At this stage the dropdowns are fairly useless as they don’t actually filter the data for us.

So we need to add some methods to our DataAccess and Business classes to allow this.

Firstly we need to create a new stored procedure that will return all the product data for a given specialisation id.

CREATEPROCEDURE dbo.usp_select_AllProductsBySpec
SELECT     dbo.Product.Id, dbo.Product.Name, dbo.Product.Description, dbo.Product.Cost
FROM         dbo.Product INNER JOIN
                      dbo.SpecProduct ON dbo.Product.Id = dbo.SpecProduct.ProductID
WHERE     (dbo.SpecProduct.SpecID = @SpecID)

This procedure does a simple join to the products table and returns all the appropriate products.

Now we need to make sure we can retrieve that data from the database with our data layer. So we need to create a function that does that in our ProductDal.cs file

public List<Product> GetAllProducts(int catId)
          SqlParameter[] sqlParameters = {
                                             DbHelper.MakeInParam("SpecID", SqlDbType.Int, 4, catId)
          var dataSet = new DbHelper().RunProcDs("usp_select_AllProductsBySpec", sqlParameters);
          return Helpers.Common.CreateList<Product>(dataSet);

'** NOTE: This may already have been in the solution previously so I have tweaked the names. Just make sure before blindly copying and pasting in! **

Now we need a function in our business layer to access this function. That is created in the ProductBl.cs file.

public static List<Product> GetAllProducts(int specID)
           return new ProductDal().GetAllProducts(specID);

Now that we have that, we need to do some more in the catalog.aspx.cs file to make this work.

Firstly we need to change how the existing _Catalog.aspx repeater is bound.

So in the page load event in Catalog.aspx.cs remove the 2 lines that currently bind the repeater

Create a new function that binds the repeater to the SelectedValue of the Specialisation dropdown

private void BindProducts()
           _Catalog.DataSource = ProductBl.GetAllProducts(Int32.Parse(_Specialisation.SelectedValue));

And we add the function call after the BindSpec() in the BindSpec function

private void BindSpec()
         _Specialisation.DataSource = CategoryBl.GetAllSpecs(Int32.Parse(_SubCategory.SelectedValue));
         _Specialisation.DataValueField = "Id";
         _Specialisation.DataTextField = "Name";

After this we need to add an event handler for the Specialisation so that it will rebind the _Catalog repeater each time the selection changes

Specialisation.SelectedIndexChanged += _Specialisation_SelectedIndexChanged; // in OnInit

void _Specialisation_SelectedIndexChanged(object sender, EventArgs e)

Lastly we need to set the Specialisation dropdown to auto postback so in the page source, add AutoPostBack=”true” so that it looks like this

<asp:DropDownList ID="Specialisation" runat="server" Width="300px" AutoPostBack="true">

You should be able to compile the page and view it. You will probably not see any data because you will not have populated the link table.

So if you open the table SpecProduct and enter 1 in the ProductID field and 1 in the SpecID field, you should see some information. This assumes that you have a Product with an Id of 1 and also a spec with an Id of 1.

I have created an updated version of the project which also includes sample data and sample links for the first 3 specs.




You can download the solution from here

In case you are wondering I created the sample product data from the AdventureWorks database using the following query

INSERT INTO [ShoppingCartDemo].[dbo].[Product] ([Name],Description,Cost)
SELECT [Name], 'Some Description', ROW_NUMBER() OVER(ORDER BY [Name] ASC)*2 AS Cost FROM AdventureWorks.Production.Product

The ROW_NUMBER() function allowed me to generate sample costs hence why they are all multiples of 2

Thursday, 7 May 2009

Even more with Virtual Earth

Onwards and upwards we go

Lets add a pushpin to our map. So if you open the existing solution we can dive right in!

Create a new ASPX file and drag a map control onto it and a script manager if there is not one there already

Now we will add 2 text boxes and a button

<asp:Label ID="Label2" runat="server" Text="Latitude"></asp:Label>&nbsp;
<asp:TextBox ID="txtLat" runat="server"></asp:TextBox>
<br />
<asp:Label ID="Label1" runat="server" Text="Longitude"></asp:Label>&nbsp;
<asp:TextBox ID="txtLong" runat="server"></asp:TextBox>
<br />
<asp:Button ID="btn" runat="server" Text="Add Point" onclick="AddPoint" />

Right now into the code behind

We will add some code to add a point to the map

double dblLat = ConvertDbl(txtLat.Text);
double dblLong = ConvertDbl(txtLong.Text);

LatLongWithAltitude point = new LatLongWithAltitude(dblLat, dblLong);
Shape s = new Shape(ShapeType.Pushpin, point);

s.Title = "Hello from the demo";


First thing you will notice is the function ConvertDbl. This is a small helper function because of the way that Norwegian systems interpret how a double works. Norwegian uses a comma (,) as decimal separator rather than a point.

So we create a new LatLongWithAltitude object and pop our co-ordinates in. Then we create a shape to using a PushPin shape and our point. We can add other attributes to our shape at this point.

Then we add the shape to the Map. If you are adding a lot of shapes, its best to use a ShapeLayer so that you can hold them otherwise they are added to the base ShapeLayer

Right so putting in 58.97 as latitude and 5.75 as longitude we get a point that is in downtown Stavanger in Norway.


Solution can be downloaded from here

And yes I am experimenting with different plug-ins to highlight code in Windows Live Writer :)

Hiring Manager Forum: Thursday, May 14 at Tech∙Ed

Registration for our Wednesday forum filled so quickly that we've added a second session! The Product Marketing team will share valuable tools and insights to help you leverage Microsoft Certifications in your organization. Attendees will also have the chance to win an HP Mini!

Getting the Most from Your Microsoft Technology Investment: How to Improve Organizational Efficiency with Microsoft Certifications

Thursday May 14 @ 11:30 AM - 1:00 PM (lunch will be provided)

Room 518

LA Convention Center

Register Now!

Learn more about Tech∙Ed

Wednesday, 6 May 2009

More with Virtual Earth

Continuing on from the previous post, lets do something very simple. A find query.

You can use the previous solution for this sample as well.

Create a new ASPX file and drag a map control into it. Also drag in a script manager if there is not one present on the page.


Now we are going to add some labels and text boxes and a button

<asp:Label ID="Label1" runat="server" Text="Find:"></asp:Label>&nbsp;
        <asp:TextBox ID="txtFind" runat="server"></asp:TextBox>&nbsp;
        <asp:Label ID="lblWhere" runat="server" Text="Where"></asp:Label>&nbsp;
        <asp:TextBox ID="txtWhere" runat="server"></asp:TextBox>&nbsp;
        <asp:Button ID="btnFind" runat="server" Text="Find This" onclick="FindStuff" />

Now in the code behind we have

protected void FindStuff(object sender, EventArgs e)
           string what = txtFind.Text;
           string where = txtWhere.Text;

           Map1.Find(what, where);

Now build the code and run it in the browser.

I used searched for Sushi in Manhattan, New York and it brought a couple of hits


You can find the updated solution file here

Tuesday, 5 May 2009

Using Virtual Earth in your ASP.NET applications

One of the really neat things is when you discover something nifty purely by accident and then it becomes of those things you keep using. Just like the Virtual Earth Map control.

You can download the Windows Live Tools for Visual Studio 2008 from here and this will install some new Visual Studio templates and allow you to use among other things, the Map control in your web sites.

Now we cant leave it at just that, we need to do something fun with it.

For this demo we are going to add a Virtual Earth map control to a page and show how

First off download and install the tools from the link above.

Now create a new web site. You can use the new Windows Live Web Application template.


Just give it a name and off we go. You will see you will have some new controls in the controls toolbox (once you double click on default.aspx).


You should see that there is a script manager referenced on the page by default.

Drag in the map control to the page to have it on your page.


I am now going to change the centre position to my home town Stavanger which has very rough co-ordinates of 58.97,5.7. So click on the map and look at the properties window and you will see Center and then you can change the Longitude and Latitude co-ordinates to whatever you want. Unfortunately the control in design view will not reflect this change.

Now I am going to add 2 text boxes and a button

          <asp:Label ID="Start" Text="From" runat="server"></asp:Label>&nbsp;
          <asp:TextBox ID="txtStart" runat="server"></asp:TextBox>&nbsp;
          <asp:Label ID="Finish" Text="To" runat="server"></asp:Label>&nbsp;
          <asp:TextBox ID="txtFinish" runat="server"></asp:TextBox>
&nbsp;<asp:Button ID="Button1" runat="server" Text="Get me directions"
            onclick="GetDirections" />

We are going to look for directions.

So in the code behind file we need to add the following using statement

using Microsoft.Live.ServerControls.VE;

Next we create the function GetDirections

protected void GetDirections(object sender, EventArgs e)
                List<string> Positions = new List<string>();


                RouteOptions ro = new RouteOptions();

                ro.DistanceUnit = RouteDistanceUnit.Kilometer;
                ro.DrawRoute = true;
                ro.RouteOptimize = RouteOptimize.MinimizeTime;
                ro.SetBestMapView = true;

                Map1.GetDirections(Positions, ro);


Using the RouteOptions object we can change how we render the route. To get the directions its just a call to the Map1.GetDirectiions and pass in our list of strings and the RouteOptions object we created.

So build the project and view it in the browser. I typed in Stavanger, Norway to Sandnes, Norway and it generated a nice little directions map for me


You can do more with Virtual Earth and in a later post, I will show another little trick on showing where your visitors are coming from on the map

You can download the VS 2008 solution file from here

How to search every field in the database

Recently I had an issue where one of my solution files got corrupted and I couldn’t remove it from SharePoint.

I used stsadm –o enumsolutions to find out what was happening and noticed that the solutions were there but only the ID of the file was there and no other information so I couldn’t retract or delete the solution file.

I was left with the cardinal sin of SharePoint going into the database and poking around. I didn’t know where about this information was held so I used a little trick of searching all the GUID fields in the database to see where it lived.

First bit I needed to do was find all the fields in the database that are GUID types and also what tables they were in

So I used this quick query to do that

SELECT AS ColumnName, AS TableName
FROM syscolumns INNER JOIN sysobjects ON =
WHERE sysobjects.type='U' AND syscolumns.xtype=36

The syscolumns.xtype is type of column and you can get a list of all column data types by running

SELECT xtype, name FROM systypes

Which on SQL Server 2008 returns the following

34    image
35    text
36    uniqueidentifier
40    date
41    time
42    datetime2
43    datetimeoffset
48    tinyint
52    smallint
56    int
58    smalldatetime
59    real
60    money
61    datetime
62    float
98    sql_variant
99    ntext
104    bit
106    decimal
108    numeric
122    smallmoney
127    bigint
240    hierarchyid
240    geometry
240    geography
165    varbinary
167    varchar
173    binary
175    char
189    timestamp
231    nvarchar
239    nchar
241    xml
231    sysname

Now that I had my query to find all the specific fields in every table in the database I still needed to search for my GUID

I used part of my previous post’s to use a cursor and do my search


SELECT AS ColumnName, AS TableName
FROM syscolumns INNER JOIN sysobjects ON =
WHERE sysobjects.type='U' AND syscolumns.xtype=36

OPEN RowCounter

INTO @Column,@Table


SELECT @strSQL = 'SELECT [' + @Column +  '] FROM [' + @Table + '] WHERE [' + @Column + '] = ''61c93a97-3989-4823-888d-1d68b5674350'''

PRINT 'Checking ' + @Column + ' in ' + @table


INTO @Column,@Table

CLOSE RowCounter

This allowed me to find the offending GUID which was in the Objects table in the Config database.

Sunday, 3 May 2009

Threading in ASP.NET pages: How to

A very interesting technique that is sometimes not used is the idea of threading and using it in your ASP.NET application. What is really good for is when you have a long running call (such as a function that does a lookup against DNS) and you don’t want to delay the execution of your page.

So how do you do threading in ASP.NET

In this example I will just do a quick run through of creating a threaded operation that could be used for example to save some information to the database, such as a roll your own analytics object.

Firstly you will need to a reference the assembly System.Threading ie
using System.Threading;
I have a small sample class that will contain the IP address, the host name for that IP and the date it was created.
public class TrackingObject 
public string HostIP { get; set; }
public string HostName { get; set; }
public string DateEntered { get; set; }
Next thing is that we need to create a new object on the page load and set some information
TrackingObject trackingObject = new TrackingObject(); 
trackingObject.DateEntered = DateTime.Now.ToString();
trackingObject.HostIP = Request.UserHostAddress;
So we get the IP of the requesting computer and current date and time and set those properties.

Now we are going to create a thread so that we can do a DNS lookup on the IP and then theoretically save this information to the database.

Because we are passing our object into our thread delegate so we need to use the ParameterizedThreadStart Delegate
Thread trackingThread = new Thread(new ParameterizedThreadStart(SaveTrackingInfo));
SaveTrackingInfo is a new function which takes a single object and we will go through that in a minute.

Next thing is to set the priority of the thread we are creating. Thread priority defines which thread gets the resources, the higher the priority of the thread the quicker it will get the resources. On the flip side, if you raise your thread’s priority to a higher level you can cause locks and other problems.

Finally in this section we need to actually start the thread and pass it the object.
Now we will take a look at the SaveTrackingInfo function. This is the function that will be run in the thread.
private void SaveTrackingInfo(object obj)     
TrackingObject trackingObject = (TrackingObject) obj;
trackingObject.HostName = Dns.GetHostEntrytrackingObject.HostIP).HostName;
trackingObject.HostName = trackingObject.HostIP;
} //Save tracking object to the database

We need to cast the object to correct type, because we are passing in a base object.

For this example, we are going to try and get the host name for the IP, which could take some time and then save this object to the database.

This very quick sample shows how to use threading in an ASP.NET application.

You can download the solution from here

In a later post, I will demonstrate what we can do with some of this information.

Encrypting the web.config online

One of the things that possibly gets overlooked when you are first developing web applications is the concept of security and defending against possible security breaches.

Even though the web.config file is not served by IIS, it may happen that someone accidently changes that! It has being known to happen. Or someone may get access to the machine through some other nefarious means.

So how do you secure the web.config. Well here is how

I have a simple Config.aspx file. This ASPX file only shows the current encryption status of the connectionString section in the web.config and allows you to reverse it.

So here is how to do it

<html xmlns="" >
<head id="Head1" runat="server">
    <title>Config Tools</title>
    <form id="form1" runat="server">
        <asp:Label ID="_ConnStatus" runat="server"></asp:Label>
        <br />
        <asp:Label ID="_Conn" runat="server"></asp:Label>
        <br />
        <br />
        <asp:Button ID="Encrypt" runat="server" onclick="EncryptConfig"
            Text="Encrypt" />
            <asp:Button ID="Decrypt" runat="server"
            Text="Decrypt" onclick="DecryptConfig" />
        <br />
        <br />
        <br />
        <br />

That is the front end and in the code behind we have this

protected void Page_Load(object sender, EventArgs e)
            string strConnDesc;

            if (Business.Config.IsEncrypted(Request.ApplicationPath))
                Decrypt.Enabled = true;
                Encrypt.Enabled = false;
                strConnDesc = "is encrypted";
                Encrypt.Enabled = true;
                Decrypt.Enabled = false;
                strConnDesc = "is not encrypted!";
            _Conn.Text = "The connection string is currently " + strConnDesc;

        protected void EncryptConfig(object sender, EventArgs e)
            Decrypt.Enabled = true;
            Encrypt.Enabled = false;
            _Conn.Text = "The connection string is currently is encrypted ";

        protected void DecryptConfig(object sender, EventArgs e)
            Encrypt.Enabled = true;
            Decrypt.Enabled = false;
            _Conn.Text = "The connection string is currently is not encrypted!";

Now the main two things are the EncryptConfig and DecryptConfig

I have the logic for these functions in another assembly.

To encrypt the web.config file you can use the following function

public static void EncryptConnString(string strPath)
           var config = WebConfigurationManager.OpenWebConfiguration(strPath);

           var section = config.GetSection("connectionStrings");
           if (section.SectionInformation.IsProtected) return;

First thing you do is you open the web.config file and get the connectionStrings section.

Check and see if its currently protected, and if its not, encrypt it with the RSA provider. And then save the file

To decrypt its much the same

public static void DecryptConnString(string strPath)
            var config = WebConfigurationManager.OpenWebConfiguration(strPath);
            var section = config.GetSection("connectionStrings");
            if (!section.SectionInformation.IsProtected) return;

Again open the web.config and look for the connectionStrings section and check if its encrypt and then decrypt and save.

Now I also have a help function that tells me if the section is encrypted or not

public static bool IsEncrypted(string strPath)
           var config = WebConfigurationManager.OpenWebConfiguration(strPath);
           var section = config.GetSection("connectionStrings");
           return section.SectionInformation.IsProtected;

A couple of words of warning though.

You should only encrypt the web.config on the machine that it will be residing on. The reason is that the config is encrypted using the machine key of the encypting machine. Or put simply, encrypting it on your dev machine will mean you cant use it on another machine.

You will also need to make sure that the config file is writable otherwise you wont be able to encrypt or decrypt. So you should check if the file is writable first.

You can download the Config.aspx sample and also the helper class as part of the shopping cart demo here

An update on the shopping cart demo

Well the first version of this demo (here) was targeted at the 2.0 version of the .NET framework, this version is for the 3.5 version.

A few changes and additions were made.

Firstly I changed the layout of the projects now to use a common namespace and put the different classes in folders to represent their functional types. Also they have being strongly named and signed with a keyfile. (Password is password). Using this means you can install the assemblies to the GAC if required.

I added a new property to the CartItem class that returns the total cost of the item in the shopping cart

        public int TotalPrice
            get { return Amount*Cost; }

I changed how the generic object filler works. Instead of using the names of the properties of the object, it now uses what you returned in the datarow making it a bit more flexible.

I updated the Catalog.aspx file to now show the shopping cart which will also show the cost of the items in the cart as well as the actual amount of items in the cart rather than the list count.

I have also used a Lambda expression where before I used an anonymous method

var hasitem = cartItems.Find((ci => ci.Id == cartItem.Id));

Included now is a very simple cascading dropdown demo which shows how to do it and also how you can link products to the values in the database.

Later I will show how to do this using Ajax.

Also there is a copy of the config.aspx tool that I use to protect the connection string in the web.config. This tool and how it works is explained here

You can download the updated solution from here

Installation notes.

You will need to create a database first and then use the SQL script in the root of the solution folder to create the tables and stored procedures.

You may also have to adjust the connection string in the web.config file to suit your own installation.

If you are prompted for a password for the keyfiles, type in password

How to get all stored procedures in a database

Every so often you will need to get a list of objects from the database and then do something to them

In this case this script allows you to get a list of all the stored procedures in your database and grant execute to a specific user. This is handy when you want to change user and modify a lot of stored procedures

DECLARE @spPrefix VARCHAR(200)

SELECT @User = 'username'
SELECT @spPrefix ='usp_get%'

SELECT [name]
FROM sys.objects
WHERE name LIKE @spPrefix AND type in (N'P', N'PC')

OPEN RowCounter
INTO @spName


SELECT @strSQL = 'GRANT EXECUTE ON ' + @spName +  ' TO [' +@User+ ']'

PRINT 'Granting execute on: ' + @spName


INTO @spName

CLOSE RowCounter

Full script here