ThreadAbortException

August 2009 Entries

Windows Mobile Device Center – Deleted Contacts

If you are using Windows Vista or above and also Windows Mobile Pocket PC or Smart Phone, you would have definitely stumbled upon Windows Mobile Device Center.  It is a one stop place for synchronizing your phone data with your PC i.e. Contacts, Tasks, Emails, Calendar etc.,

Today, while I was setting up my new phone, I accidentally deleted the SIM contacts and worst, synchronized it with my PC using the WMDC and gosh, all the contacts from my Outlook got removed as well.  It was frustrating and kept me nervous over the next few minutes.  Then, I did a bi tof research and finally looked into the “Deleted Items” folder in the Outlook.  There all my contacts were there.  I quickly moved them back to the Contacts folder and also saved my years of contacts.

Well, this post is to help you find it like me, if you happen to end up in a situation like me, so if it helps, it solved the purpose.

Cheers !!!

What’s new in ASP.NET 4.0 – Part II – Routing in Webforms

When I wrote the first post in this series, there was tremendous amount of interest generated and also a lot of feedback requesting to post some of the advanced features.  Like I said earlier, ASP.NET 4.0 has lots of new features some of them as simple as Page.Title whereas so as big as caching improvements.  This post covers one such feature which is Routing in Webforms.  Although Routing was available even in .NET 3.5 SP1, (check this excellent post by Phil Haack on implementing Routing in ASP.NET 3.5 with .NET 3.5 SP1), it was kind of less known.  Also the plumbing work was too much for getting it implemented.

However, this has been much simplified in ASP.NET 4.0.  To give a background, System.Web.Routing is the namespace that provides the all important RouteTable & PageRouteHandler class.  Initially System.Web.Routing was an integral part of ASP.NET MVC.  However, the team must have anticipated that Routing is more important even for Webforms and hence they moved this DLL outside the scope of just MVC and made it available to Webforms as well. 

Importance of Routing:  Getting friendlier URLs which help in better search engine optimization and indexing.  Cleaner URLs that can be bookmarked than the unfriendly querystring based approach.  As more and more URLs are available, the chances of improvement in search engine ranking becomes higher.  These are some of the general advantages of Routing and friendly URLs. 

Ok, now that the context is established, lets start with our sample.  To begin with, I am using Visual Studio 2010 Beta 1 (download link) and Northwind Sample Database (download link)

I created a “File – New Project – ASP.NET Web Application” leaving the default .NET 4.0 as the framework option. Then, I created a bunch of pages i.e Products.aspx, Categories.aspx and also the Global.asax (Add – New Item – Global Application Class)

On the Default.aspx page, I added a GridView and configured it to use the Northwind Database Connection String and the Categories Table therein.  I modified the auto-generated bound columns with a Template Column to accomodate our link to Categories Page.  The modified GridView code looks as below:-

<asp:GridView ID="GridView1" runat="server" AllowPaging="True"
            AllowSorting="True" AutoGenerateColumns="False" CellPadding="4"
            DataKeyNames="CategoryID" DataSourceID="SqlDataSource1" ForeColor="#333333"
            GridLines="None">
            <AlternatingRowStyle BackColor="White" />
            <Columns>
               <asp:TemplateField HeaderText="CategoryName" SortExpression="CategoryName">
                    <ItemTemplate>
                   
    <a href="Categories/<%# Eval("CategoryName") %>"><asp:Label ID="Label1" runat="server" Text='<%# Bind("CategoryName") %>'></asp:Label></a>
                    </ItemTemplate>
                </asp:TemplateField>
                <asp:BoundField DataField="Description" HeaderText="Description"
                    SortExpression="Description" />
            </Columns>
            <FooterStyle BackColor="#990000" Font-Bold="True" ForeColor="White" />
            <HeaderStyle BackColor="#990000" Font-Bold="True" ForeColor="White" />
            <PagerStyle BackColor="#FFCC66" ForeColor="#333333" HorizontalAlign="Center" />
            <RowStyle BackColor="#FFFBD6" ForeColor="#333333" />
            <SelectedRowStyle BackColor="#FFCC66" Font-Bold="True" ForeColor="Navy" />
        </asp:GridView>

As you can see, the Item Template for Category Name is modified to sport a hyperlink to “Categories” page followed by the CategoryName itself.  This would mean that the URL for a category, say Beverages would point to Categories/Beverages

Similarly, on the Categories page, I added a GridView and configured it to use the “Allphabetical List of Products” Table.  I also modified the Bound field for ProductName to a template column to have a link to another Products Page.  The modified GridView code looks as below:-

<asp:GridView ID="GridView1" runat="server" AllowPaging="True"
           AllowSorting="True" AutoGenerateColumns="False" CellPadding="4"
           DataSourceID="SqlDataSource1" ForeColor="#333333" GridLines="None">
           <AlternatingRowStyle BackColor="White" />
           <Columns>
               <asp:TemplateField HeaderText="CategoryName" SortExpression="CategoryName">
                   <ItemTemplate>
                     
<a href="Products/<%# Eval("ProductName") %>"><asp:Label ID="Label1" runat="server" Text='<%# Bind("ProductName") %>'></asp:Label></a>
                   </ItemTemplate>
               </asp:TemplateField>
               <asp:BoundField DataField="QuantityPerUnit" HeaderText="QuantityPerUnit"
                   SortExpression="QuantityPerUnit" />
               <asp:BoundField DataField="UnitPrice" HeaderText="UnitPrice"
                   SortExpression="UnitPrice" />
               <asp:BoundField DataField="UnitsInStock" HeaderText="UnitsInStock"
                   SortExpression="UnitsInStock" />
               <asp:BoundField DataField="UnitsOnOrder" HeaderText="UnitsOnOrder"
                   SortExpression="UnitsOnOrder" />
               <asp:BoundField DataField="ReorderLevel" HeaderText="ReorderLevel"
                   SortExpression="ReorderLevel" />
               <asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued"
                   SortExpression="Discontinued" />
           </Columns>
           <EditRowStyle BackColor="#2461BF" />
           <FooterStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
           <HeaderStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
           <PagerStyle BackColor="#2461BF" ForeColor="White" HorizontalAlign="Center" />
           <RowStyle BackColor="#EFF3FB" />
           <SelectedRowStyle BackColor="#D1DDF1" Font-Bold="True" ForeColor="#333333" />
       </asp:GridView>

Note, while configuring above GridView, in the screen where we configure DataSource, I also added a where condition to accomodate the Route Request.  The screen looks as below:-

confscreen1

confscreen2

Note that I had selected the Where condition from the first screen and specified “CategoryName” under Column,“=” under Operator and “Route” under Source.  Also specified are the RouteKey “catname” and DefaultValue “Beverages”.  Post this, I just had to click on “Add” and then “Ok” to get a conditional select statement in the SQL DataSource (note: for the purpose of this demo, I have used SQL DataSource.  But this would work even if you used any other datasource type / written ADO.NET Code).  The “Route” type is new feature added under Source in Visual Studio 2010.

Once the above configuration is done, the SQL DataSource code looks as below:-

<asp:SqlDataSource ID="SqlDataSource1" runat="server"
           ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>"
           SelectCommand="SELECT [ProductName], [QuantityPerUnit], [UnitPrice], [UnitsInStock], [UnitsOnOrder], [ReorderLevel], [Discontinued] FROM [Alphabetical list of products] WHERE ([CategoryName] LIKE '%' + @CategoryName + '%')">
          
<SelectParameters>
               <asp:RouteParameter DefaultValue="Beverages" Name="CategoryName"
                   RouteKey="catname" Type="String" />
           </SelectParameters>
       </asp:SqlDataSource>

I have also added a label to the page just to show the term used to filter and the value for that can be picked up from the Page.RouteData values in the codebehind as follows:-

protected void Page_Load(object sender, EventArgs e)
       {
           if (Page.RouteData.Values["catname"] != null)
           {
               lblDisplay.Text += "<b>" + Page.RouteData.Values["catname"].ToString() + "</b>";
           }
           else
           {
               lblDisplay.Visible = false;
           }
       }

Before getting into Route Configuration, I also added a DetailsView control in the Products.aspx page to show the complete details of a product.   And when configuring the DataSource for the DetailsView, I again specified the WHERE condition to the picked up from the RouteData that would come from the above GridView in Catagories Page.

Once this is done, all that is pending is to configure the Route Values.  In .NET 3.5 SP1 if you want to establish routing, you would have to manually create the WebFormRouteHandler Class and make sure all the pages inherit from this class.  However, in .NET 4.0, it has been much simplied.  All I had to do was open the Global.asax and add the following

protected void Application_Start(object sender, EventArgs e)
        {
             RouteTable.Routes.Add("ProductRoute", new Route("Categories/Products/{productname}",
       new PageRouteHandler("~/Products.aspx")));

            RouteTable.Routes.Add("CategoryRoute", new Route("Categories/{catname}",
       new PageRouteHandler("~/Categories.aspx")));
        }

(note you would need to add System.Web.Routing namespace to be able to use PageRouteHandler, RouteTable classes etc.,)

So, in the Default.aspx page, all the Catagories would have a link that points to /Categories/<CategoryName> and in the Categories.aspx page, all the ProductNames would have a link that points to /Categories/Products/<ProductName>

A typical URL is http://localhost/Categories/Condiments and http://localhost/Categories/Products/Aniseed%20Syrup

Note that similarly, we have close to 10 URLs for Beverages and around 80 URLs (a URL for each product as above) for Products in this particular application.

You can download the sample from  (Add your connectionstring to Northwind Database in the web.config file)

Cheers !!!

What’s new in ASP.NET 4.0 - Part I - View State & SEO Improvements

UPDATE dated August 17, 2009

Once I posted this entry, there is a lot of interest that has been shown and few queries as well.  So I thought I need to update this post.

This is only the first post in the series on ASP.NET 4.0 fetaures.  There are lot of ground breaking things and other enhancements that are pretty exciting.

To the folks who asked for "why MS changing dev platform once in 18 months"

There is no change.  there are a few enhancements.  Post .NET 2.0 there have been a lot of additional APIs such as LINQ, Entity Framework etc., which are different ways of data handling.  While core ADO.NET is still valid, these are additional enhancements that can be used at the Developer's / Architect's discretion. 

For the folks you asked about URL Rewriting

There is URL Routing which is much better than URL Rewriting.  We have talked about it, enough in the past .  You can read more about it at http://www.mostlylucid.net/archive/2009/01/25/asp.net-4.0-webform-routing-quick-rsquon-dirty-version.aspx

I will be covering more features in the following post.

Thanks for the inerest

With Visual Studio 2010 Beta 1 and .NET Framework Beta 1 out for some time, this post is due from me for a long time.   ASP.NET 4.0 has many improvements for different set of scenarios such as Webforms, Dynamic Data & AJAX based web development.  There are also a lot of enhancements to the core runtime that powers ASP.NET such as Caching, Session & Request/Response objects.

For this post, we will examine some of the web form enhancements.  There are sure a lot of them and we will examine some of them in the future posts.

Controlling View State using the ViewStateMode Property – Performance Enhancement

One of the most complained thing in ASP.NET Webform is the growing viewstate which becomes a concern for performance.  While earlier you can set the EnableViewState property to true or false, post that, all the controls, by default inherit and even if you set it to enabled at control level, the behaviour was inconsistent.

With ASP.NET 4.0, the ViewStateMode property helps to determine for every control, whether the ViewState should be enabled, disabled or inherited accordingly.  Ex.-

<asp:Panel ID="pnlViewState" runat="server" ViewStateMode="Disabled">
      Disabled: <asp:Label ID="label1" runat="server"  Text="Value set in markup" ViewStateMode="Inherit"  /><br />
           Enabled: <asp:Label ID="label2"  runat="server" Text="Value set in markup" ViewStateMode="Enabled" />
  <hr />
  <asp:button ID="Button1" runat="server"  Text="Postback" />
    </asp:Panel>

In the code-behind

protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            label1.Text = "Value set in code behind";
            label2.Text = "Value set in code behind";
        }
    }

When you run the above page, you can find that the intial value for both the labels is set to “Value set in code behind” whereas after clicking on the button (postback), the value of label1 changes to “Value set in markup” whereas the value of label2 remains unchanged.  As you can see, the Panel which holds both these lables has ViewStateMode set to Disabled and label1 is inherting the mode (this is the default if not specified) and label2 has it enabled.  That is the reason label2 maintains viewstate while label1 loses it.

While it is arguably possible using the simple EnableViewState property earlier, it was never consistent.  Considering the fact that in most of our performance sessions, we talk about disabling viewstate and then enabling it at control level while it doesnt work, this ViewStateMode is a welcome architectural change to improve performance.

Page Meta Keyword & Description – Search Engine Optimization feature

Upto Visual Studio 2008, one can set the Title of the page declaratively or through program using Page.Title.  However, as more and more web traffic is happening through search engines, Page’s Title, Keyword and description become more important.  Although the Keyword feature was exploited and hence many search engines today ignore it, Page Description is something still major search engines such as Google, Bing use for identifying and indexing pages based on content.

The new feature in ASP.NET 4.0 allows users to programmatically set the Page Description and Keywords as follows:-

protected void Page_Load(object sender, EventArgs e)
    {
        this.Page.Title = "My ASP.NET Blog";
        this.Page.MetaKeywords = "ASP.NET, Web Development, Blog, ASP.NET Blog";
        this.Page.MetaDescription = "This Blog contains posts related to ASP.NET and Web Development";
    }

The above code appends the following markup

<meta name="keywords" content="ASP.NET, Web Development, Blog, ASP.NET Blog" />

<meta name="description" content="This Blog contains posts related to ASP.NET and Web Development" />

And the way it works is that, if the meta tags are already present in the HTML markup, whatever is set in the code behind  will fill up the “content” part alone if the “name” tag is matching.

Although this looks simple, it is very useful in cases where you want to set these dynamically based on a condition / criteria.  So far, these were set statically in the HTML.  Now with Page Class level access, these can be set dynamically.

There are many more enhancements to Webforms such as Routing improvements, setting ClientID etc., which we will examine in the future posts.

Cheers !!!

ASP.NET 4.0 QueryExtender, AutoCompleteExtender and UpdatePanel – mashing it up all

(this is a repost since the initial post had a few issues that couldn’t be corrected)

I am playing with the ASP.NET 4.0 QueryExtender released as a part of the Visual Studio 2010 Beta 1.   It provides endless opportunities for working with data without writing much code and when you combine it with a few Ajax features, gives a truly great user experience with very less effort. 

To begin with you need the Visual Studio 2010 Beta 1 and .NET Framework 4.0 Beta 1.  You can install both from http://msdn.microsoft.com/hi-in/netframework/dd819232(en-us).aspx

Also, I am using the Northwind sample database and this can be downloaded from http://www.microsoft.com/downloads/details.aspx?FamilyID=06616212-0356-46A0-8DA2-EEBC53A68034&displaylang=en

To make things more fun, I am also using AJAX Control Toolkit.  You can download the same from http://ajaxcontroltoolkit.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=27326 You can download just the DLL from the AJAXControlToolkit-Framework3.5SP1-DllOnly.zip link in this page since in this sample, we are just going to use the control.  However, if you already have the AjaxControlToolkit installed, you can simply reference the AjaxControlToolkit.dll file in the Website.

Note that, due to a security implementation in VS 2010, the AjaxControlToolkit DLL cannot be used as is in the projects in VS 2010.  Check this post on using AjaxControlToolkit with VS 2010 Beta 1 http://blogs.msdn.com/webdevtools/archive/2009/05/26/using-microsoft-ajax-control-toolkit-with-visual-studio-10-beta-1.aspx

Once you are done with the installation and other steps, create a new ASP.NET Website.  Note that, for the QueryExtender to work well, you need to make a small web.config update.  You can find the details about this, in my previous post  here  This is just a Beta behaviour.

In the ASP.NET Website, first we will create a LINQ to SQL Class (Right Click Website – Add new item – LINQ TO SQL Class.  Provide this the name “Northwind”.  It would create a Northwind.dbml file, a class file as well as layout file.   Using the server explorer link, connect to the Northwind Database.  For this sample, I am using the Products table.   So, select the table from Northwind database folder under “Tables” and drag and drop it into the LINQ to SQL designer view.  Build the solution.

Once this is done, open the Default.aspx page and add the following code after the form tag.

<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="true">
        </asp:ScriptManager>

We are adding the above since we are going to use Ajax Futures.  Normally this is not required to use the QueryExtender

Then, add a LINQ Data Source and configure it to use the Products table in the Northwind DataContext created.  The code should look something like below:-

<asp:LinqDataSource ID="LinqDataSource1" runat="server" ContextTypeName="NorthwindDataContext"
            TableName="Products">
        </asp:LinqDataSource>

Post this, I am adding a TextBox, Button and QueryExtender as follows:-

<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>

<asp:Button ID="Button1" Text="Submit" runat="server" />

<asp:QueryExtender ID="QueryExtender1" runat="server" TargetControlID="LinqDataSource1">
          <asp:SearchExpression DataFields="ProductName" SearchType="StartsWith">
            <asp:ControlParameter ControlID="TextBox1" />
          </asp:SearchExpression>
        </asp:QueryExtender>

Note that the ControlID for QueryExtender’s ControlParameter is TextBox1 and the TargetControlID is LinqDataSource1.  Update these if you gave different ID Names.

Post this, add a GridView to the page and configure it to use the LINQ DataSource created above.  I am not posting the code below since it would make this post too big. 

If everything works fine, you should be able to see a GridView populated with the Products Data and a TextBox and Button when you run the page.

When you type a particular Product Name, say “Anideed Syrup” and click on “Submit” Button, it would filter and show only that particular record in the GridView.  Note that, you haven’t written any code for this filtering so far.  All happens by virtue of the QueryExtender control.  We have used SearchExpression in this case and it has stuff like StartsWith, EndsWith, contains for SearchType that can be used exhaustively for different requirements.  Also, there is RangeExpression, PropertyExpression etc., 

The input for the QueryExtender can also come from various sources.  In this case, it is TextBox and hence ControlParameter.  You can also use QueryString, Cookie, Form, Profile, Session etc.,

For a more exhaustive sample on QueryExtender, check http://msdn.microsoft.com/en-us/library/dd537669(VS.100).aspx

Now that we have a search behaviour, we would like to go little further and make this whole stuff asynchronous so that the operation is smooth.

Add an UpdatePanel and place all the above inside the ContentTemplate of the UpdatePanel.

When you run the page now and type a search item and click on the “Submit” button, the results bind asynchronously without a postback, making it look better.

One final touch is to provide search suggestions.  For this, I would be using the AutoCompleteExtender feature.  If you already have the AjaxControlToolkit installed, you may want to remove it from ToolBox and do the step in paragraph 5 (security implementation in VS 2010)  above and then add it to the Toolbox again. 

Drag and drop the AutoCompleteExtender into the page and set its TargetControlID to TextBox1. 

Now, the AutoCompleteExtender requires a Web Method to retrieve the values asynchronously.   We will use PageMethods for this so that we avoid creating a separate webservice.   For this purpose, we will use the same DataContext created by the LINQ DataSource Control above.

Swtich to the code behind of the Default.aspx page i.e. Default.aspx.cs file and add the following using statements

using System.Web.Services;
using System.Configuration;

Post that, the method for retrieving product name is as below:-

[WebMethod]
    public static IQueryable GetProductNames(string prefixText)
    {
        NorthwindDataContext nwdc = new NorthwindDataContext();

        var productList = from Product in nwdc.Products
                          where Product.ProductName.StartsWith(prefixText)
                          select Product.ProductName;

        return productList;
     }

With this, we are getting a list of Products whose names start with the prefixText.  The prefixText is the one used in the AutoCompleteExtender and length can be set using MinimumPrefixLength from 1 to 2,3 etc.,  This is basically the number of characters you need to type before the suggestions need to show up.  Then configure the ServiceMethod for the AutoCompleteExtender to GetProductNames (the method above).  The updated AutoCompleteExtender code should look like

<cc1:AutoCompleteExtender ID="AutoCompleteExtender1" runat="server"
                    MinimumPrefixLength="1" TargetControlID="TextBox1" ServiceMethod="GetProductNames">
                </cc1:AutoCompleteExtender>

Once you have all of these, run the page and when you start typing the ProductName in the TextBox, it should start showing suggestions.  Select one of them and click on the “Submit” Button and you should get the result bound to GridView, all happening without a postback.

You can improve the layout with better CSS for the AutoCompleteExtender, better layout for GridView, Button etc., to further improve the experience.  I have posted a few sample screen shots below and also attached the source code for the above sample with this.  Note that there are far too many steps involved in setting up the website rather than the actual code work, so if something doesn’t work, make sure you have checked all the initial steps mentioned by me in the beginning.  Hope you find this sample useful.

Download the sample from http://cid-069f94a102eff49a.skydrive.live.com/browse.aspx/QueryExtenderSample

screenshot1

screenshot

Cheers !!!