PROGRAMMING
Last edited July 3, 2008
More by Desmond54 »
.NET

Visual Studio 2005 Web Application Project Option
webproject.scottgu.com/

Visual Studio 2005 Web Application Project Option

The Visual Studio 2005 Web Application Project Model is a new web project option for Visual Studio 2005 that provides the same conceptual web project approach as VS 2003 (a project file based structure where all code in the project is compiled into a single assembly) but with all the new features of VS 2005 (refactoring, class diagrams, test development, generics, etc) and ASP.NET 2.0 (master pages, data controls, membership/login, role management, Web Parts, personalization, site navigation, themes, etc).

Kieran Lynam's Blog : ConfigurationManager Gotcha
www.irishdev.com/blogs/kieranlynam/archive/2005/05...

ConfigurationManager Gotcha

Quick Gotcha... Up to now, you use the ConfigurationSettings.AppSettings property to read an entry from the AppSettings section of the config file.

In ASP.NET 2.0, this property is marked as obsolete; you must now use the ConfigurationManager class. However, this does not seem to exist in the System.Configuration namespace. Annoying.

Actually, it does exist in this namepsace, but you must add a referene to the System.Configuration assembly first (this assembly is not one of the default references).

Modal Dialog - enhanced - The Code Project - ASP.NET
www.codeproject.com/useritems/ModalDialogV2.asp?pr...
 
All Topics, .NET, C#, ASP.NET >> ASP.NET >> Unedited Reader Contributions
http://www.codeproject.com/useritems/ModalDialogV2.asp

Modal Dialog - enhanced
By volkan.ozcelik

In this article, we will try to generate a draggable DHTML layer that loads data from an external URL via XMLHTTP connection. This is an enhanced version of my previous Draggable Layer article, hence it addresses additional issues that are not present in the former article. 
 Advanced
 Generic
Windows, .NET
ASP.NET, Win32, VS
Dev
 Posted 2 Jun 2006 8:37
1,930 views
Note: This is an unedited reader contribution
6 votes for this article.
Popularity: 3.5. Rating: 4.5 out of 5.

Introduction

This article can be considered as a follow up to my former Modeling a Draggable Layer and Loading Dynamic Content to it via XML HTTP article. Have a look at it, if you have not done already so that you can catch up faster.

In this article, we will try to

  • Create a cross-browser, draggable DHTML Modal Dialog
  • Send an AJAX request using HTTP-POST.
  • Get the response and display it inside the modal dialog

And best of all, we will do all these in less than 20 lines of code,  with the help of sardalya API.

Please note that the API enclosed in this article's source archive is a rather simplified version of sardalya. You can visit sardalya's web site for the latest full version of it.

Before starting, you may want to see the final result it in action first.

Creating the View

The HTML of our ModalDialog is fairly simpe:

<div id="ModalBG"></div>
 
<div id="DialogWindow">
  <div id="DialogHeader">
    <span id="DialogTitle">Title comes here</span>
    <img id="DialogActionBtn"  src="icn_close.png" alt="close icon" title="" />
  </div>
  <div id="DialogIcon"><img id="DialogIcon" src="icn_alert.png" alt="alert icon" title="" /></div>
  <div id="DialogContent">...</div>
</div>

"ModalBG" is a layer that is placed between the page content and the ModalDialog window so that we prevent accidential clicks on other page elements and in the same time put some visual emphasis on the ModalDialog by fading the page in the background.

The CSS

To make our "DialogWindow" layer resemble an actual modal dialog, we need some CSS tweaks:

Master.css
 
#DialogWindow
{
 border: 1px #FFFFFF outset;
 width: 550px;
 display: none;
 background: #FFFFFF;
 z-index: 1000;
 position: absolute;
 top: 0;
 left: 0;
}
 
#DialogContent
{
 float: right;
 margin-right: 10px;
 margin-bottom: 10px;
 margin-top: 24px;
 width: 450px;
 display: block;
 font-size: 90%;
}

 
#DialogIcon
{
 padding: 10px;
 float: left;
}
 
#DialogHeader
{
 border-bottom: 1px #00449E outset;
 background: #00449E;
 text-align: right;
}
 
#DialogTitle
{
 float: left;
 padding: 8px;
 color: #FFFFFF;
}
 
#DialogActionBtn
{
 cursor:pointer;
}

And we need an "Opacity.css" for transparency support:

Opacity.css
 
#ModalBG
{
 width:100%;
 display:none;
 background-color:#333333;
 position:absolute;
 top:0;
 left:0;
 height:100%;
 z-index:999;
 opacity:.40;
 filter:alpha(opacity=40)
}

With the help of this CSS, our boxes will pretty much look like a Modal Dialog, except for certain browsers:

Be Kind to Opera

I hear you say "Why am I to be kind to Opera all the time? why is never Opera kind to me?!" and I truly understand you :) But let us be kind to Opera once again:

To make our transparent background work on Opera we need several more CSS tweaks:

Master.css
 
/* transparency support for Opera */
.modalOpera
{
 background-image: url("maskBg.png") !important;
}

That's it! Opera does not understand CSS transparency, but it fully supports .png transparency, therefore a transparent mask as a background will make our ModalDialog work equally good in Opera.

There is one remaining issue here, we need to selectively apply this class only if and only if the user agent is Opera. That is other browsers, such as Mozilla, does not require this transparency hack and we should not use "modalOpera" class if our user agent is one of them. We will address this issue in a second.

The Script

First of all we need to prepare the Modal Dialog at page load:

 window.onload=function()
 {
  /* Sweep unnecessary empty text nodes. */
  DOMManager.sweep();

  /*
   * Attach supporting css bind required
   * classes for transparency support in Opera.
   */ 
  addExtensionsForOpera();
 
  /* Attach opacity css. */
  attachOpacityCSS();
 
  /* Adjust height. */
  adjustHeight();

  /* Create the modal dialog */
  g_Modal=new ModalDialog("ModalBG","DialogWindow",
   "DialogContent","DialogActionBtn");
 

  /* Bind an event listener to double-click event. */
  EventHandler.addEventListener(document,"dblclick",document_dblclick);
 };

sweep is a utility method of sardalya's DOMManager object. It removes empty text nodes from the <CODE>DOM structure.

Now let us look at other methods one by one

 function addExtensionsForOpera()
 {
  /* classes for opera */
  var ModalBG=new CBObject("ModalBG").getObject();
  if(typeof(window.opera)!="undefined")
  {
   ModalBG.className="modalOpera";
  }
 }

As seen, we only append "modalOpera" className to the "ModalBG" if the user agent is Opera.

Note that we do not sniff the user agent (navigator.userAgent) but do an "object detection" instead (window.opera). 

Browsers love to fool scripts by sending false user agent strings and therefore object detection is the way to go. Although details of it is the subject of an entire article, I can say that browser sniffing is soo '90s. As a rule of thumb always use object detection.

Then comes attach opacity css piece:

 function attachOpacityCSS()
 {
  /*
   * CSS for opacity support
   * Note that this can be directly added to the body.
   * If you do not care about blindly adhering to standards
   * you can directly include the rules into Master.css
   *
   * Do I care? Yes and No.(visit http://www.sarmal.com/Exceptions.aspx
   * to learn how I feel about it).
   */
    var opacityCSS = document.createElement("link");
    opacityCSS.type="text/css";
    opacityCSS.rel="stylesheet";
    opacityCSS.href="Opacity.css";
    document.getElementsByTagName("head")[0].appendChild(opacityCSS);
 }

And height adjustment:

 function adjustHeight()
 {
  /* get the available height of the viewport */
  var intWindowHeight=WindowObject.getInnerDimension().getY();
  var dynModalBG=new DynamicLayer("ModalBG");
  var intModalHeight=dynModalBG.getHeight();
 
  /*
   * if modal background's height is less than the viewport's 
   * available height, increase its height.
   */
  if(intModalHeight<intWindowHeight)
  {
   dynModalBG.setHeight(intWindowHeight);
  }
 }
Then we create the modal dialog in just a single line:
  g_Modal=new ModalDialog("ModalBG","DialogWindow",
   "DialogContent","DialogActionBtn");

"ModalBG" is the ID of transparent background, "DialogWindow" is the ID of modal dialog container, "DialogContent" is where messages is displayed when calling the show method of ModalDialog, and "DialogActionBtn" is the ID of the close button.

And finally we attach a double-click event to the document which will trigger an AJAX action:

  /* Bind an event listener to double-click event. */
  EventHandler.addEventListener(document,"dblclick",document_dblclick);

Now let us have a look at document_dblclick method:

 function document_dblclick(evt)
 {
  /* create an AJAX request */
  var ajax = _.ajax();

  /*
   * Note that _.ajax(); is a shorthand notation 
   * for new XHRequest();
   * Visit http://sardalya.pbwiki.com/Shortcuts for details.
   */

  /* 
   * You can add as many fields as you like to the post data. 
   * Normally the server will use this data to create an
   * output that makes sense which may be an XML, a JSON String
   * or an HTML String.
   */
  ajax.removeAllFields();
  ajax.addField("name","John");
  ajax.addField("surname","Doe");

  /* These events will be fired when server posts back a response. */
  ajax.oncomplete=ajax_complete;
  ajax.onerror=ajax_error;

  /* Set a default waiting message. */
  g_Modal.show("Fetching data... Please wait...");

  /*
   * Disable close action if you want to force the user 
   * to wait for the outcome of the AJAX request.
   * Although it is generally not recommended 
   * this may be necessary at certain times.
   */
  g_Modal.disableClose();

  /* Post data to the server. */
  ajax.get("externalScript.html");

  /* Stop event propagation. */
  new EventObject(evt).cancelDefaultAction();
 }

The comments should be self-explanatory.

And finally the two methods that are triggered after the server's post back.
 /* Triggered when a successful AJAX response comes from the server.*/
 function ajax_complete(strResponseText,objResponseXML)
 {
  g_Modal.show(strResponseText);
  
  /* Re-activate close button. */
  g_Modal.enableClose();
 }

 /* Triggered when server generates an error. */
 function ajax_error(intStatus,strStatusText)
 {
  g_Modal.show("Error code: ["+ intStatus+ "] error message: [" + 
   strStatusText + "].");

  /* Re-activate close button. */
  g_Modal.enableClose();
 }

That's it!

What about those nasty SELECTs ?

ModalDialog object internally handles it, by replacing them with SPAN elements with class "modalWrap" whenever ModalDialog opens. This sorts out the well known "SELECTs bleed through my top layer" issue.

Here is the CSS of it for the sake of completeness:
.modalWrap
{
 border: 2px #ffffcc inset;
 background:#ffffcc;
 margin:5px;
}

You can add as many rules as you like to it. The more the SPAN resembles a SELECT element, the better (you can apply width and line-height, set display to inline-table... etc, I did not change it too much to keep it simple)

For those who wonder how the replacement of those SPANs and SELECTs are done, the corresponding private method is given below. You can observe the source code of the article's zip file for more details.

In the former version, we were simply hiding the SELECTs by setting their CSS visibility to hidden.  Having tested it in real-life scenarios, I saw that the "all of a sudden" dissappearance of SELECTs was annoying to some of the users.

imho, transforming the SELECTs is much better than hiding them completely.

... And no, I do not want to use IFRAMEs :)

Here follows the code:

_this._replaceCombos=function(blnReplaceBack)
{
 var arSelect = document.getElementsByTagName("select");
 var len=arSelect.length;
 var objSel=null;
 var strNodeValue="";
 var objSpan=null;
 var o=null;

 if(!blnReplaceBack)
 {
  blnReplaceBack=false;
 }
 
 for(var i=0;i<len;i++)
 {
  objSel=arSelect[i];
  strNodeValue=objSel.childNodes[objSel.selectedIndex
  ].childNodes[0].nodeValue;

  objSpan=new CBObject(objSel.id+"_ModalWrap");
  if(objSpan.exists())
  {
   o=objSpan.getObject();
   o.parentNode.removeChild(o);
  }

  objSpan=document.createElement("span");
  objSpan.id=objSel.id+"_ModalWrap";
  objSpan.appendChild(document.createTextNode(strNodeValue));
  objSpan.className="modalWrap";
  objSel.parentNode.insertBefore(objSpan,objSel);

  if(blnReplaceBack)
  {
   new DynamicLayer(objSpan).collapse();
   new DynamicLayer(objSel).expandInline();
  }
  else
  {
   new DynamicLayer(objSpan).expandInline();
   new DynamicLayer(objSel).collapse();
  }
 }
};

Conclusion

In conclusion, we modeled and created a draggable DHTML Modal dialog, established an AJAX connection to an external script; we did some cross-browser tweaks to make our application work on as many browsers as possible, and we did some OO coding.

And as always,
Happy coding!

History

  • 2006-06-02 : Article created.

About volkan.ozcelik


Volkan is a java enterprise architect who left his full-time senior developer position to venture his ideas and dreams. He codes C# as a hobby, trying to combine the .Net concept with his Java and J2EE know-how. He also works as a freelance web application developer/designer.

Volkan is especially interested in database oriented content management systems, web design and development, web standards, usability and accessibility.

He was born on May '79. He has graduated from one of the most reputable universities of his country (i.e. Bogazici University) in 2003 as a Communication Engineer, and is currently about to finish his MBA in a second university.

Click here to view volkan.ozcelik's online profile.


Discussions and Feedback

5 comments have been posted for this article. Visit http://www.codeproject.com/useritems/ModalDialogV2.asp to post and view comments on this article.
All Topics, .NET, C#, ASP.NET >> ASP.NET >> Unedited Reader Contributions
Updated: 2 Jun 2006 8:37
Article content copyright volkan.ozcelik, 2006
everything else Copyright © CodeProject, 1999-2006.

Flicker Fix

Summary

This page tells you how to use ASP.NET to eliminate the flicker sometimes seen in menus implemented with cascading style sheets (CSS).

Background

For as long as authors have been experimenting with pure CSS menus there have been reports of flicker problems.

The emerging view is that the problem is ultimately caused by the client browser failing to cache images used in hover styles (CSS).

One way to eliminate this flicker is to specially configure your web server (e.g., IIS). Then, when these sorts of rollover images are requested, your web server will add specific caching requirements to the HTTP headers. These tell the browser to cache the images regardless of the default caching policy it normally uses.

Unfortunately, this sort of solution is only practical when you have access to your web server's configuration. Most web hosting services do not give you access to the IIS management console. Even when IIS can be directly accessed, configuring caching policies is scary and unfamiliar to many authors.

Happily, there is a much simpler solution if you are developing a web site with ASP.NET. It's is easy to implement and doesn't involve configuring your web server.

Solution

To implement this solution you will:

  • Add an HTTP handler to the root of your web site.
  • Tweak your CSS styles to refer to this HTTP handler instead of the image files that are flickering.
  • Add a few elements to the appSettings section of your web site's web.config file.

After adding the HTTP handler PersistantImage.ashx to the root of your web app, locate styles that refer to background images. Imagine your web site has a subfolder just below the root where you put all your style sheets. It might have a class that defines the background image used in a menu:

CSS
1
2
3
4
5
        .someClassName
        {
            background:#AABBCC url(someFlickeringImage.gif) repeat-x;
        }

If you find that the menu flickers, you could modify this style to be:

CSS
1
2
3
4
5
        .someClassName
        {
            background:#AABBCC url(../PersistantImage.ashx?key=SomeFlickeringImage) repeat-x;
        }

Then you would add an entry defining the query string key, SomeFlickeringImage, in the appSettings section of the web.config in the root of your web app:

CSS
1
2
3
4
5
6
7
8
9
10
        <?xml version="1.0"?>
        <configuration>
          <appSettings>
            <add key="SomeFlickeringImage" value="~/myStyles/someFlickeringImage.gif"/>
          </appSettings>
          <system.web>
          </system.web>
        </configuration>
        

This forces the browser to request the background image via the HTTP handler, PersistantImage.ashx, rather than requesting that image file directly. You provide PersistantImage.ashx with a nickname for the image via the key in the query string. PersistantImage.ashx looks up that nickname in the appSettings to find the true path to the image to send down to the browser with special caching instructions. Because the browser now caches the image it doesn't re-request it from the server again and again, which is what causes the flicker effect.

Using nicknames for images enhances your web site's security. Your web.config contains a white list of images that you explicitly allow to be exposed. No one can invoke PersistantImage.ashx directly to gain access to other files on your web site.

As an added security measure, the HTTP hander PersistantImage.ashx will only respond to requests for files with a web image extension: jpg, jpeg, gif or png.

If you are using the CSS Friendly ASP.NET Control Adapters you will find it helpful to modify the style sheet that defines the background images used for menus.

One of the styles to change is:

CSS
1
2
3
4
5
        .PrettyMenu ul.AspNet-Menu li
        {
            background:#4682B4 url(bg_nav.gif) repeat-x;
        }

It should become:

CSS
1
2
3
4
5
        .PrettyMenu ul.AspNet-Menu li
        {
            background:#4682B4 url(../../PersistantImage.ashx?key=BasicBgNav) repeat-x;
        }

Then, in the web.config you would modify the <appSettings> to be:

CSS
1
2
3
4
5
6
7
8
9
10
        <?xml version="1.0"?>
        <configuration>
          <appSettings>
            <add key="BasicBgNav" value="~/App_Themes/Basic/bg_nav.gif"/>
          </appSettings>
          <system.web>
          </system.web>
        </configuration>
        

You may find that you need to use PersistantImage.ashx in several different styles in order to fully eliminate the menu flicker. In practise it is probably easiest to adopt a simple policy of using PersistantImage.ashx for all of the menu-related styles that refer to background images.


Dissecting the Validation Controls in ASP.NET 2.0
By Scott Mitchell


Introduction
One annoying task that most every developer has had to face in the past is form validation. Since forms are an integral part of dynamic, data-driven Web sites, it is essential that a user's query into a form fit the specified guidelines. For example, in a website like eBay where users are entering shipping and billing information, it is vital that the user enter credit card numbers, zip codes, email addresses, and other information in an acceptible format.

Prior to ASP.NET, form validation was a frustrating and tedious process. The page developer was responsible for creating the client-side and server-side logic for each and every validation check. ASP.NET version 1.0 helped end this tedium with a host of validation Web controls, making validation was as simple as adding the appropriate validation control to the web page and setting a few properties.

While validation controls in ASP.NET version 1.x greatly improved the developer's experience with adding page validation, they still lacked some important functionality. For one, the client-side script emitted by the validation controls used the JavaScript object document.all, which is an object that is not in the JavaScript standard and only supported by Microsoft's Internet Explorer browser. This meant that browsers other than Internet Explorer could not enjoy the client-side benefits of the validation controls. Additionally, the validation controls could not be logically grouped, which proved irksome with pages divided up into different sections.

ASP.NET 2.0 doesn't add any new validation controls, but it does fix the validation control shortcomings in version 1.x, along with adding additional features. In this article we'll dissect the validation controls in version 2.0. Read on to learn more!

(This article does not cover the basics of ASP.NET's validation controls; for more information on that refer to Form Validation with ASP.NET - It Doesn't Get Any Easier! and User Input Validation in ASP.NET.)

Validation Groups
One of the annoyances with ASP.NET version 1.x validation controls was that they all were checked against when a postback occurred. Granted, you could suppress all validators from firing when a particular Button control was clicked using the CausesValidation property, but oftentimes a finer degree of control is needed. Oftentimes Web Forms have multiple "sections," where each section has a set of input controls and a Button that, when clicked, performs some action based on that section's controls. Ideally, each section would have section-dependent validation controls, but with ASP.NET version 1.x's limitations, developers were either forced to forgo the validation controls altogether (or forgo them in all but one section) or turn to a third-party validation solution, such as Peter Blum's Professional Validation And More (VAM). (Read a short review of mine on VAM and Peter's other product, Peter's Date Package.)

Thankfully with ASP.NET version 2.0 validation controls can be grouped. All validation controls now contain the ValidationGroup property. The default value of this property is the empty string, which indicates that the validation control is not part of a group. But those controls that have the same ValidationGroup values are considered grouped. The Button, LinkButton, DropDownList, and other controls that are commonly used to submit a Web Form also contain the ValidationGroup property. When a Button with a ValidationGroup property value is clicked all of the validation controls on the page with the same ValidationGroup property value are fired.

On postback, the Page.IsValid property reflects the validity of all of the validation controls that have been validated. By default, this is the set of validation controls whose ValidationGroup property value equals the ValidationGroup property value of the control that instigated the postback. However, additional validation controls can be programmatically checked by calling the validation control's Validate() method.

In ASP.NET version 1.x the Page class had a Validate() method that could be called programmatically which would check all validator controls in the page; furthermore, there was a Validators property that provided a collection of all validation controls. With version 2.0, the Page class's Validate() method has been enhanced and a new GetValidators() method has been added. (The Validation property remains for backwards compatibility, and still returns all validation controls in the page.)

With 2.0 the Validate() method can be called without passing in any parameters, just like in version 1.x. Doing so validates against all of the validation controls in the page. The Validate() method has an overload that takes in a string parameter and validates against those controls whose ValidationGroup property equals the passed-in string to Validate(). When a Button control causes a postback and its CausesValidation property is True, internally the Page.Validate(ValidationGroupPropertyValue) method gets called. As in version 1.x, the Page.Validators property returns a collection of all validation controls. The GetValidators(ValidationGroup) method returns a collection of validation controls that belong to the specified validation group.

The behavior of these properties and methods is illustrated in the ValidationMethod.aspx demo available for download at the end of this article...

Focusing On Invalid Form Fields
One of the nifty features of ASP.NET version 2.0 is that all server controls now have a Focus() method. Calling this method injects a bit of client-side JavaScript to set focus to a particular control on page load. In addition to this nifty little method, the validation controls offer similar functionality through their SetFocusOnError property. This property, if set to True, causes the Web control the validation control is operating on to receive focus if the validation control is invalid. This is a nice little usability touch that your users will likely appreciate.

Client-Side Validation Support in Non-Microsoft Browsers
One of the unfortunately aspects of ASP.NET version 1.x's validation controls was that they used a bit of proprietary JavaScript to provide client-side validation functionality, making the validation controls inoperable in non-Microsoft browsers. This topic was discussed in detail in a previous article of mine, Client-Side Validation in Downlevel Browsers. The workaround for version 1.x was to either:

  • Build up your own validation control library from scratch,
  • Use a free or third-party validation package that has done the dirty work for you. (The two options I mentioned in my article were Paul Glavich's free DomValidators controls or Peter Blum's Professional Validation And More (mentioned earlier in this article).
The client-side script used by the validation controls in 2.0 still includes calls to document.all, but also includes equivalent checks using the standard-compliant document.getElementById(id). The end result is that the client-side validation features of the validation controls now work with any browser that supports JavaScript 1.2 or higher. This includes Internet Explorer's two largest competitors: FireFox and Opera.

Conclusion
The validation controls in ASP.NET version 2.0 have been improved from version 1.x. While there are no new validation controls, the existing controls have been enhanced to include support for validation groups and client-side support for browsers other than just Internet Explorer. While these enhancements do make ASP.NET's built-in validation controls much more useful in real-world settings, they still lack many of the more advanced features found in third-party validation packages. For most developers, however, these new additions to the validation controls will make the built-in controls sufficient for real-world use.

Happy Programming!

  • By Scott Mitchell


    Attachments

  • Download the code examples (ZIP format)


  • JupiterWeb networks:

    Search JupiterWeb:

    Jupitermedia Corporation has two divisions:
    Jupiterimages and JupiterWeb

    Copyright 2006 Jupitermedia Corporation All Rights Reserved. Copyright 2005-2006 Jupitermedia Corporation All Rights Reserved.
    Legal Notices, Licensing, Reprints, & Permissions, Privacy Policy.

    Jupitermedia Corporate Info | Newsletters | Tech Jobs | Shopping | E-mail Offers

    Working with Data and ASP.NET 2.0: ASP.NET Web: The Official Microsoft ASP.NET 2.0 Site
    www.asp.net/Learn/DataAccess/Default.aspx?tabid=1

    Building and using a 3-tiered data architecture with ASP.NET 2.0

    Welcome to a series of tutorials that will explore techniques for implementing these common data access patterns in ASP.NET 2.0. These tutorials are geared to be concise and provide step-by-step instructions with plenty of screen shots to walk you through the process visually. Each tutorial is available in C# and Visual Basic versions and includes a download of the complete code used.

    Raj Kaimal : What's the deal with Databinder.Eval and Container.DataItem?
    weblogs.asp.net/rajbk/archive/2004/07/20/188868.as...

    What's the deal with Databinder.Eval and Container.DataItem?


    The databinding expression <%# some expression %> is evaluated in the language of the page (VB, C#, etc.)  This can have a big impact on the current syntax, so be very careful when you are looking at docs for the language you are using.

     

    Container.DataItem is a runtime alias for the DataItem for this specific item in the bound list.  For a grid which displays 10 rows of data, this is one row from the datasource.  The actual type of DataItem is determined by the type of the datasource.  For example, if the datasource is a Dataview, the type of DataItem is DataRowView.  If the type of the datasource is an array of strings, the type of DataItem is String.  If the datasource is a collection of strongly-typed objects (for example "Employees" objects), the type of DataItem is Employees.

     

    Each of these cases requires a slightly different databinding expression, with further differences between VB and C#.  In every case, you want the databinding expression to produce a string that can be displayed in the page.

     

    Here are some examples:

     

    Array of Strings:

    VB/C# <%# Container.DataItem %>

     

    Field from DataView:

    VB      <%# Container.DataItem("EmployeeName") %>

    C#      <%# ((DataRowView)Container.DataItem)["EmployeeName"] %>

     

    Property from a collection of objects:

    VB      <%# Container.DataItem.CustomerName %>

    C#      <%# ((Customer)Container.DataItem).CustomerName %>

     

    Non-String property from a collection of objects:

    VB      <%# CStr(Container.DataItem.CustomerID) %>

    C#      <%# ((Customer)Container.DataItem).CustomerID.ToString() %>

     

     

    As you can see the syntax is tricky, especially for C#, which requires explicit casting. So we've provided a DataBinder.Eval() helper method that figures out the syntax for you and formats the result as a string. It's really convenient, with a couple of caveats: it's late bound (uses reflection at runtime to figure out the data types), and it only supports basic data types in the fields: string, int, datetime, etc.

      

    DataBinder.Eval takes 2 or 3 arguments.  The first arg is the data object to bind to.  In the case of DataGrid, DataList and Repeater, Container.DataItem is the single row.  The second arg the string name of the field from the data object you which to display.  DataBinder.Eval uses these two pieces of information to work out the rest of the expression syntax.

     

    An optional third argument is a .NET formatting string.  DataBinder.Eval will handle a single replacement for {0} only.  So the example below:

     

    <a href='<%# "default.aspx?CategoryId=" + Cstr(Databinder.Eval(Container.DataItem, "ID"))%>'>


    could be simplified to:


    <a href='<%#  Databinder.Eval(Container.DataItem,"ID","default.aspx?CategoryId={0}" ) %>'>

     

    Wrapping DataBinder.Eval in CStr() is unnecessary as the compiler will wrap the statement with a Convert.ToString like this:

    control1.SetDataBoundString(0, Convert.ToString(DataBinder.Eval(item1.DataItem, "ID")));

     

    Best of all, the Databinder.Eval syntax is the same for VB and C#.

    Scott Galloway's Personal Blog : Small point...what Databinding syntax does everyone use?
    www.mostlylucid.co.uk/archive/2003/12/09/664.aspx

    Small point...what Databinding syntax does everyone use?

    posted on Tuesday, December 09, 2003 4:25 PM

    UPDATE: OK, before anyone points out the obvious (actually after many people have!) - I am aware that DataBinder.Eval promotes code reuse to some degree; you still have to know what your fields are called of course! My point in this post was that I rarely saw the alternative syntax used and that in my experience I have found almost no drawbacks in using the 'Strong' syntax. This was also a trawl for comments to get the opinions of other developers...with that in mind, let the comments recommence...

    Incidentally, in the post below, if anything looks screwy, sorry, having problems with the stupid Rich Text editor again!

    Reason I ask is that most of the examples I see all over the web use the:
    <%# databinder.eval(container.dataitem,="" "price","{0:c}"))%="">format...
    now the reason for this seems to be that this is  allegedly 'easier' to use and read.
    To be honest, I have never used that format, I tend to use this:
    <%# string.format("{0:c}",((dbdatarecord)container.dataitem)["price"]%="">  format
    now, I understand that this looks more complex  but as it points out here,
    "It is important to note that DataBinder.Eval can carry a noticeable performance penalty over the standard data binding syntax because it uses late-bound reflection. Use DataBinder.Eval judiciously, especially when string formatting is not required. ".
    Now, in my tests (I benchmark everything...obsessive? Quite possibly :-)), the Eval syntax is a LOT slower, like up to 20% slower - so, what is the actual advantage of using it?
    OK, my opinion on why you wouldn't use the 'explicit casting' syntax (i.e., the one I use):

    1. You have to import the correct namespace depending on whether you use a DataSet or a SqlDataReader (or any IDataReader) (System.Data and System.Data.Common respectively).
    2. You have to cast to the 'correct' object so for DataSet it's DataRowView and for SqlDataReader (or any IDataReader) it's DbDataRecord...for any other objects, it's the correct object obviously :-)
    3. It's more 'wordy'
    4. Very few examples show this syntax...

    Now, these are pretty good reasons to use the DataBinder.Eval syntax, but to be honest, I just don't find them compelling...so here's some reasons to use the 'explicit casting' one:

    1. You have to import the correct namespace depending on whether you use a DataSet or a SqlDataReader (or any IDataReader) (System.Data and System.Data.Common respectively) - same argument...but I have to say, I like this! It's more explicit about what namespace you're actually using and forces you to know this stuff!
    2. You have to cast to the 'correct' object so for DataSet it's DataRowView and for SqlDataReader (or any IDataReader) it's DbDataRecord...for any other objects, it's the correct object obviously - again, the same as above...knowing what type of object you're using is, in my opinion, a good thing, it gives a whole lot of control in how you represent the object; for example you get access to all the nice DateTime formatters in the .ToString() method - same as you would in code!
    3. It's faster - now this is not a 'be all and end all' argument, but it is important, especially in high-hit sites or when you have to bind really long lists of data - faster = less time for which a thread is used!
    4. Makes you look like a smart ass - OK, just me then :-)

    Anyway, I really am interested in hearing people's opinions on this one...which do you prefer, and why?

    ScottGu's Blog : ASP.NET 2.0 Localization (Video, Whitepaper, and Database Provider Support)
    weblogs.asp.net/scottgu/archive/2006/05/30/ASP.NET...

    ASP.NET 2.0 Localization (Video, Whitepaper, and Database Provider Support)

    ASP.NET 2.0 and VS 2005 add a bunch of features that make localizing ASP.NET applications much easier.

    To learn more about how the localization features work, I'd recommend first checking out this excellent 13 minute video in the ASP.NET 2.0 "How Do I?" video series.  In it Scott Stansfield walks-through how to build and localize an ASP.NET application from scratch, and how to support both dynamically picking the language used based on the incoming user-agent string of the client, as well as allowing a user to explictly choose their language preference from a dropdownlist.  I think you will walk away being surprised at how easy it is.  

    Wei-Meng Lee has also written a great walkthrough article on ASP.NET 2.0 localization that you can read for free on the O'Reilly network hereFor more detailed information on ASP.NET 2.0 localization, you can also then read Michele Bustamente's excellent ASP.NET 2.0 Localization article on MSDN.  Bilal Haidar also has a great artlce on ASPAlliance here.

    The articles and videos above use XML .resx files to store localized resource strings.  These can either by compiled into binaries or deployed as XML source with an ASP.NET 2.0 application.  Jeff Modzel recently published an article that shows how to build a custom Resource Provider that stores the resource strings in a database instead.  You can read about how he built this as well download his sample code here.

    Hope this helps,

    Scott

    How to: Use Resources to Set Property Values in Web Server Controls
    msdn2.microsoft.com/en-us/library/ms228093.aspx
    How to: Use Resources to Set Property Values in Web Server Controls 

    In an ASP.NET page, you can use the following methods to retrieve values from resource files that are compiled by ASP.NET and managed by the .NET Framework resource manager:

    • Implicit localization, wherein ASP.NET fills property values from the resource manager based on matching the resource key to the properties of the control.

    • Explicit localization, by creating an expression that retrieves a specific resource from the .NET Framework resource manager.

    • Programmatically, by retrieving resource values in code.

      For detailed information, see How to: Retrieve Resource Values Programmatically.

    To use implicit localization

    1. Make sure that you have local resource files (.resx files) that meet the following criteria:

      • They are in an App_LocalResources folder.

      • The base name matches the page name.

        For example, if you are working with the page named Default.aspx, the resource files are named Default.aspx.resx (for the default resources), Default.aspx.es.resx, Default.aspx.es-mx.resx, and so on.

      • The resources in the file use the naming convention resourcekey."property".

        For example, key name Button1."Text".

    2. In the control markup, add an implicit localization attribute.

      For example:

      <asp:Button ID="Button1" runat="server" Text="DefaultText" 
          meta:resourcekey="Button1" />

      All resource files are compiled and the .NET Framework resource manager is used by ASP.NET at run time to retrieve the culture-appropriate resource for each resource in the default resource file. For each resource, ASP.NET looks for a corresponding resourcekey."property" combination (in the preceding example, resourcekey="Button1") in the page, and then substitutes the resource for the retrieved value.

    To use explicit localization

    • In the markup for a control, use a resource expression to set the value for each property that you want to replace with a resource. The syntax is as follows:

      <%$ Resources:Class, ResourceKey %>

      Use these values:

      • Class is the resource file class, which is based on the .resx file name.

        A resource file named WebResources.resx uses the class name WebResources. All culture variant resource files use the same class name as the culture neutral resource file. If you want to obtain a resource from the local resource file that is associated with a page, Class is optional.

      • ResourceKey is the name of a resource in the specified class.

      For example, a Button control that is configured to set the Text property from a global resource file might look similar to the following code example:

      <asp:Button ID="Button1" runat="server" 
          Text="<%$ Resources:WebResources, Button1Caption %>" />

    Example

    The following code examples show implicit and explicit localization. The first code example shows how to use implicit localization, in which each control is marked with the meta attribute. At run time, ASP.NET matches resources to control properties. The second code example shows a page that uses resource expressions (explicit localization) to set the values of the Text property of various controls and of the ImageUrl property of an Image control.

    In the first example, change <default> in the first line to a valid culture name. For a list of culture names, see the "Culture Names and Identifiers" table in CultureInfo.

    Security Note

    This example has a text box that accepts user input, which is a potential security threat. By default, ASP.NET Web pages validate that user input does not include script or HTML elements. For more information, see Script Exploits Overview.

    Visual Basic
    <%@ Page Language="VB" Culture="auto:<default>" 
        meta:resourcekey="PageResource1" UICulture="auto" %>
    
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML
      1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
    
    <html  >
    <head runat="server">
      <title>Implicit Localization Sample</title>
    </head>
    <body>
        <form id="form1" runat="server">
        <div>
        <h1>
           <asp:Localize runat=server 
              ID="WelcomeMessage" 
              Text="Welcome!" meta:resourcekey="Literal1" />
        </h1>
        <p>
        <asp:Image runat="server" ID="Logo" ImageUrl="" 
          meta:resourcekey="Logo" />
        </p>
        <br />
        <br />
        <asp:Localize runat="server"
           ID="NameCaption"
           Text="Name: " meta:resourcekey="Literal2" />
        <asp:TextBox runat="server" ID="TextBox1" 
          meta:resourcekey="TextBox1" />
        <br />
        <br />
        <asp:Button ID="Button1" runat="server" Text="Submit" 
          meta:resourcekey="Button1"/><br />
        </div>
        </form>
    </body>
    </html>
    
    C#
    <%@ Page Language="C#" Culture"auto:<default>" 
        meta:resourcekey="PageResource1" UICulture="auto" %>
    
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 
      1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
    
    <html  >
    <head runat="server">
      <title>Implicit Localization Sample</title>
    </head>
    <body>
        <form id="form1" runat="server">
        <div>
        <h1>
           <asp:Localize runat=server 
              ID="WelcomeMessage" 
              Text="Welcome!" meta:resourcekey="Literal1" />
        </h1>
        <p>
        <asp:Image runat="server" ID="Logo" ImageUrl="" 
          meta:resourcekey="Logo" />
        </p>
        <br />
        <br />
        <asp:Localize runat="server"
           ID="NameCaption"
           Text="Name: " meta:resourcekey="Literal2" />
        <asp:TextBox runat="server" ID="TextBox1" 
          meta:resourcekey="TextBox1" />
        <br />
        <br />
        <asp:Button ID="Button1" runat="server" Text="Submit" 
          meta:resourcekey="Button1"/><br />
        </div>
        </form>
    </body>
    </html>
    
    Visual Basic
    <%@Page Language="VB"
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 
      1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
    
    <html  >
    <head runat="server">
      <title>Explicit Localization Sample</title>
    </head>
    <body>
      <form id="form1" runat="server">
      <div>
        <h1>
        <asp:localize runat="server" 
          Text="<%$ Resources:WebResources, WelcomeMessage %>" />
        </h1>
        <asp:Image runat="server" id="Logo" 
          ImageUrl="<%$ Resources:WebResources, LogoUrl %>" />
        <p>
          <asp:Localize runat="server" 
             Text="<%$ Resources:WebResources, EnterNameCaption %>" />
           <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
           <br />
           <br />
           <asp:Button ID="Button1" runat="server" 
              Text="<%$ Resources:WebResources, SubmitButtonCaption %>" />
        </p>
      </div>
      </form>
    </body>
    </html>
    
    C#
    <%@ Page Language="C#" UICulture="auto" %>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 
      1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
    
    <html  >
    <head runat="server">
      <title>Explicit Localization Sample</title>
    </head>
    <body>
      <form id="form1" runat="server">
      <div>
        <h1>
        <asp:localize runat="server" 
          Text="<%$ Resources:WebResources, WelcomeMessage %>" />
        </h1>
        <asp:Image runat="server" id="Logo" 
          ImageUrl="<%$ Resources:WebResources, LogoUrl %>" />
        <p>
          <asp:Localize runat="server" 
             Text="<%$ Resources:WebResources, EnterNameCaption %>" />
           <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
           <br />
           <br />
           <asp:Button ID="Button1" runat="server" 
              Text="<%$ Resources:WebResources, SubmitButtonCaption %>" />
        </p>
      </div>
      </form>
    </body>
    </html>
    

    In the @ Page directive, the UICulture attribute is set to auto to specify that the page should set the current UI culture to the information that is passed by the browser.

    In Web pages, resources, such as graphics, are included by using a URL to reference an external file. Typically, you localize graphics and other resources by creating different versions of the graphics. You can then set the URL of a control using a resource expression that specifies a string resource to the appropriate path.

    See Also

    WebResource ASP.NET 2.0 explained - The Code Project - ASP.NET
    www.codeproject.com/aspnet/MyWebResourceProj.asp
    WebResource ASP.NET 2.0 explained

    Introduction

    This code drop is part of Redux series. This article started out to be about a dropdown date/time ASP.NET 2.0 server control. So I built the control and all its ancillary JavaScript, along with the images, and then everything was put into a zip file for uploading to the CodeProject. The installation instructions for the control went something like this:

    • Unzip the contents to a folder.
    • Copy the DLL to the Bin folder of your new project.
    • Copy the JavaScript file.
    • Copy the CSS file.
    • Copy the images.

    Hey, wait a minute, this is really lame, why all the extra files, why not embed them into the DLL as resources. That was the beginning of three days of hell trying to understand and work with WebResources. I have boiled it down to about twenty lines of working code.

    Nothing else on the net gives you an actual working sample and a lot of the information is simply wrong (possibly because it was based on beta versions). Well, this code compiles and runs on the release version of .NET 2.0.

    Code snippets

    Create a project which looks like this (you can get the control from the download and then just add an ASP.NET project to the solution):

    Highlight the three files in MyResources, and in the Properties window, set Build Action to Embedded Resource:

    The source for the control is as follows:

    namespace MyWebResourceProj
    {
      [ToolboxData("<{0}:MyWebResource runat=server></{0}:MyWebResource>")]
      public class MyWebResource : System.Web.UI.WebControls.TextBox
      {
          protected override void RenderContents(HtmlTextWriter output)
          {
             output.Write(Text);
          }
    
          protected override void OnInit(EventArgs e)
          {
            base.OnInit(e);
            this.Page.ClientScript.RegisterClientScriptInclude(
               this.GetType(), "Test", 
               Page.ClientScript.GetWebResourceUrl(this.GetType(), 
               "MyWebResourceProj.MyResources.Test.js"));
    
            // create the style sheet control
            // and put it in the document header
            string csslink = "<link href='" + 
               Page.ClientScript.GetWebResourceUrl(this.GetType(), 
                "MyWebResourceProj.MyResources.Test.css")
               + "' rel='stylesheet' type='text/css' />";
            LiteralControl include = new LiteralControl(csslink);
            this.Page.Header.Controls.Add(include);
          }
      }

    This is not a discussion on server controls, so let's focus on the parameters of GetWebResourceUrl:

    • this.GetType() - mandatory, just do it.
    • "MyWebResourceProj.MyResources.Test.js" - this is the most misunderstood parameter and the source of just about all errors. It is composed of [Assembly of this project].[Folder containing resource].[Filename of resource].

    No matter what you read anywhere else, including Microsoft's official documentation (which is wrong), if you don't do it this way, you will fail (I know this from personal experience).

    Now on to the AssemblyInfo.cs:

    [assembly: System.Web.UI.WebResource(
          "MyWebResourceProj.MyResources.Test.css", "text/css")]
    [assembly: System.Web.UI.WebResource(
          "MyWebResourceProj.MyResources.Test.js", 
          "text/javascript", PerformSubstitution = true)]
    [assembly: System.Web.UI.WebResource(
          "MyWebResourceProj.MyResources.Test.gif", "image/gif")]

    You need to specify the resource name exactly as you did in your program code. You need to specify the mime-type. If the file contains JavaScript, you may want to have ASP.NET perform text substitution. For example, you may have a line in JavaScript like:

    document.write("<img src='Test.gif'>");

    When you embed your images in a resource, you no longer know the name of it as it appears in the resource. To fix this, code as follows:

    document.write("<img src='<%=WebResource("MyWebResourceProj" + 
                                     ".MyResources.Test.gif")%>'>");

    All the "official documentation" and all the articles on the web say that you can leave [AssemblyName].[FolderName] out. But they are dead wrong!

    Note: if you are using this control as the base class of another, see yc4king's note below.

    Update

    KonstantinG has pointed out an error which only becomes apparent when your namespace is not the same as your assembly name. Previously, I had indicated that the name of the resource is: [Namespace of this project].[Folder containing resource].[Filename of resource], but really it is [Assembly of this project].[Folder containing resource].[Filename of resource]. As always, the best way to find information on this is by using Lutz Roeder's .NET Reflector available at aisto.com.

    SmashGrab / Redux series

    I have recently started two series of articles here at CodeProject.

    Smash and Grab is intended as a series of short articles on one specific code technique. Redux is intended as a series of longer articles which attempts to reduce a complicated topic (like WebResource) into its basic constituent parts and show that once you have all the information, it isn't really that hard. To find the Smash and Grab articles, search for the keyword SmashGrab. To find the Redux articles, search for the keyword Redux. I welcome any contributions to either series, but please follow the guidelines when submitting articles to either.

    Conclusion

    So there you have it, everything you need to know to use WebResources. This article documented how to place CSS files in the <head> section, extract JavaScript files, dynamically change the contents of a JavaScript file.

    Gary Dryden


    Click here to view Gary Dryden's online profile.

    A Resource Server Handler Class For Custom Controls - The Code Project - ASP.NET
    www.codeproject.com/aspnet/ressrvpage.asp
    A Resource Server Handler Class For Custom Controls

    Introduction

    When developing custom controls for ASP.NET, it may be necessary to create some client-side script that is used to interact with the custom control. There may also be image files that are used for certain elements of the control, such as buttons or style sheets that set the look of the control. A decision has to be made about how to deploy these resources with the custom control assembly.

    The script can be built up using a StringBuilder or a static string, and inserted directly into the page using RegisterClientScriptBlock. This is okay for small scripts, but is unwieldy for much larger scripts, especially if they are complex enough to need debugging to work out problems during development. Scripts can also be embedded as resources in the assembly, retrieved at runtime, and again inserted into the page using RegisterClientScriptBlock. This is better than the StringBuilder approach for large scripts, but it is still inserted into the page and is rendered in its entirety, every time the page is requested. The more script code you have or the more controls on the page that render supporting script code, the larger the page gets. The script also cannot be cached on the client to save time on subsequent requests.

    The scripts can be distributed as separate files along with the assembly. This solves the problem of the code being rendered in the page on each request and it can be cached on the client. However, it may complicate distribution of the custom control. It is no longer a simple XCOPY deployment as now scripts have to be installed along with the assembly. A number of factors such as whether it is a production or development server, whether or not the application is using SSL, and how the end-user's applications are set up, can affect where the scripts go, and you may end up with multiple copies in several locations. Versioning issues may also come into play if the scripts are modified in future releases of the control.

    To solve these issues, I developed a class that implements the System.Web.IHttpHandler interface and acts as a resource server of sorts. The idea was inspired by examples I saw that showed how to render dynamic images using an ASPX page as the source of the image tag. The concept is basically the same for the resource server handler. You embed the resources in the control assembly and then add a simple class to your custom control assembly that implements the IHttpHandler interface. A section is added to the application's Web.Config file to direct resource requests to the handler class. The handler class uses parameters in the query string to determine the content type and sends back the appropriate resource such as a script, an image, a style sheet, or any other type of data that you need.

    By having the resources embedded in the assembly and serving them as needed, there is as little code rendered in the page as possible by the controls. The resource handler responses can also be cached on the client, so performance can be improved as less information is sent to the client in subsequent page requests that utilize the same resources. This is most beneficial for users with slow dial-up connections, especially on forms that utilize controls with auto-postback enabled. The resources do not have to be deployed separately along with the assembly either, thus solving the problem of where to install the resources, as well as any issues involving versioning. We are back to a simple XCOPY deployment again.

    The use of a resource server handler is not restricted to custom controls. It also adds the ability to do such things as request dynamic content, such as XML, using client-side script. For example, a request could be made to retrieve the results of a database query as XML using client-side script. The results could be used to populate a control or a popup window with information when it is needed rather than sending everything along with the page when first loaded. The following sections describe how to setup and utilize the resource server handler class.

    A word about ASP.NET 2.0

    The following will allow you to embed and serve resources in ASP.NET 1.1 applications as well as ASP.NET 2.0 applications. However, with ASP.NET 2.0 the ability to serve embedded web resources is a built-in feature and is simpler to implement. It makes use of embedded resources as described in here but utilizes attributes to define them along with a built-in Page.ClientScript method to render a link to them to the client. It does not require you to write a handler and does not require any entries in the Web.config file to define the handler or to allow anonymous access to them. Gary Dryden has already written a good article on how this works so I will just refer you to it rather than repeating what it has to say (WebResource ASP.NET 2.0 Explained[^]).

    Add resources to your project

    To keep things organized, store the resources in separate folders grouped by type (Scripts for script files, Images for image files, etc.). To create a new folder in the project, right click on the project name, select Add..., select New Folder, and enter the folder name. Add a new resource to the folder by right clicking on it, and selecting Add... and then Add New Item... to create a new item, or Add Existing Item... if you copied an existing file to the new folder. Once added to the project folder, right click on the file and select Properties. Change the Build Action property from Content to Embedded Resource. This step is most important as it indicates that you want the file to be embedded as a resource in the compiled assembly.

    Add the ResSrvHandler class to your project

    Add the ResSrvHandler.cs source file to your control's project and modify it as follows. TODO: comments have been added to help you find the sections that need modification.

    Modify the namespace so that it matches the one for your custom control:

    // TODO: Change the namespace to match your control's namespace.
    namespace ResServerTest.Web.Controls
    {

    Modify the cResSrvHandlerPageName constant so that it matches the name you will use in the application's Web.Config file to direct resource requests to the class. I have chosen to use the custom control namespace with an .aspx extension. This keeps it unique and guarantees that it won't conflict with something in the end-user's application:

    // TODO: Modify this constant to name the ASPX page that will be
    // referenced in the application Web.Config file to invoke this
    // handler class.
    
    /// <summary>
    /// The ASPX page name that will cause requests to get routed
    /// to this handler.
    /// </summary>
    public const string cResSrvHandlerPageName =
                  "ResServerTest.Web.Controls.aspx";

    Modify the cImageResPath and cScriptResPath constants to point to your script and image paths. Add additional constants for other resource type paths as needed. The names of the embedded resources in the assembly are created by using the default namespace of the project plus the folder path to the resource. The default namespace is usually the same as the assembly name but you can modify it by right clicking on the project name, selecting Properties, and changing the Default Namespace option in the General section of the Common Properties entry. For the demo, the default namespace has been changed to match the namespace of the control, ResServerTest.Web.Controls, and the resource paths are Images and Scripts. As such, the constants are defined as shown below. The trailing "." should also be included. The resource name will be appended to the appropriate constant when loading it from the assembly.

    Note that if you are using VB.NET, the default behavior of the compiler differs from the C# compiler. It will not append the default namespace to the front of the resource filename unless you explicitly include the command line option to tell it to do that. As such, for VB.NET projects, you can omit the path constants or set them to empty strings:

    // TODO: Modify these two constants to match your control's
    // namespace and the folder names of your resources. Add any
    // additional constants as needed for other resource types.
    
    /// <SUMMARY>
    /// The path to the image resources
    /// </SUMMARY>
    private const string cImageResPath =
        "ResServerTest.Web.Controls.Images.";
    
    /// <SUMMARY>
    /// The path to the script resources
    /// </SUMMARY>
    private const string cScriptResPath =
        "ResServerTest.Web.Controls.Scripts.";

    The ResourceUrl method can be called to format the URL used to retrieve an embedded resource from an assembly. The first version will retrieve the named resource from the assembly that contains the class. Simply pass it the name of the resource and it returns a URL that points to the resource.

    The second version of the method can be used to extract embedded resources from assemblies other than the one containing the resource server class. Pass it the name of the assembly that contains the resource (without a path or extension, System.Web for example), the name of the resource handler that can retrieve it (i.e., the class defined in the cResSrvHandlerPageName constant), and the name of the resource to retrieve. When using this version, the name of the resource will be matched to the first resource that ends with the specified name. This allows you to skip the path name if you do not know it or extract resources from VB.NET assemblies which do not store the path:

    /// <summary>
    /// This can be called to format a URL to a resource name that is
    /// embedded within the assembly.
    /// </summary>
    /// <param name="strResourceName">The name of the resource</param>
    /// <param name="bCacheResource">Specify true to have the
    /// resource cached on the client, false to never cache it.</param>
    /// <returns>A string containing the URL to the resource</returns>
    public static string ResourceUrl(string strResourceName,
                                             bool bCacheResource)
    {
        return String.Format("{0}?Res={1}{2}", cResSrvHandlerPageName,
                strResourceName, (bCacheResource) ? "" : "&NoCache=1");
    }
    
    /// <summary>
    /// This can be called to format a URL to a resource name that is
    /// embedded within a different assembly.
    /// </summary>
    /// <param name="strAssemblyName">The name of the assembly that
    /// contains the resource</param>
    /// <param name="strResourceHandlerName">The name of the resource
    /// handler that can retrieve it (i.e. the ASPX page name)</param>
    /// <param name="strResourceName">The name of the resource</param>
    /// <param name="bCacheResource">Specify true to have the
    /// resource cached on the client, false to never cache it.</param>
    /// <returns>A string containing the URL to the resource</returns>
    public static string ResourceUrl(string strAssemblyName,
           string strResourceHandlerName, string strResourceName,
           bool bCacheResource)
    {
        return String.Format("{0}?Assembly={1}&Res={2}{3}",
            strResourceHandlerName,
            HttpContext.Current.Server.UrlEncode(strAssemblyName),
            strResourceName, (bCacheResource) ? "" : "&NoCache=1");
    }

    The IHttpHandler.IsReusable property is implemented to indicate that the object instance can be reused for other requests. The IHttpHandler.ProcessRequest method is implemented to do all of the work. The first step is to determine the requested resource's name and its type. I use the filename's extension to determine the type. The code assumes that the query string parameter is called Res. Adjust this if you choose a different parameter name. Likewise, you can modify the code to determine the resource name and type in any number of ways depending on your needs:

    /// <summary>
    /// Load the resource specified in the query string and return
    /// it as the HTTP response.
    /// </summary>
    /// <param name="context">The context object for the
    /// request</param>
    public void ProcessRequest(HttpContext context)
    {
        Assembly asm;
        StreamReader sr = null;
        Stream s = null;
    
        string strResName, strType;
        byte[] byImage;
        int nLen;
        bool bUseInternalPath = true;
    
        // TODO: Be sure to adjust the QueryString names if you are
        // using something other than Res and NoCache.
    
        // Get the resource name and base the type on the extension
        strResName = context.Request.QueryString["Res"];
        strType = strResName.Substring(strResName.LastIndexOf(
                                                '.') + 1).ToLower();

    The next step is to clear any current response and set up the caching options. If the NoCache query string parameter has not been specified, the class sets the necessary page caching options in the context.Response.Cache object. If it has been specified, the options are set such that the response will never be cached. The class defaults to having the response cached for one day. Adjust this as necessary for your controls. The response is set to vary caching by parameter name. The default class only has one parameter called Res. If you have additional parameters, be sure to add them as additional VaryByParams entries:

    context.Response.Clear();
    
    // If caching is not disabled, set the cache parameters so that
    // the response is cached on the client for up to one day.
    if(context.Request.QueryString["NoCache"] == null)
    {
        // TODO: Adjust caching length as needed.
    
        context.Response.Cache.SetExpires(DateTime.Now.AddDays(1));
        context.Response.Cache.SetCacheability(HttpCacheability.Public);
        context.Response.Cache.SetValidUntilExpires(false);
    
        // Vary by parameter name. Note that if you have more
        // than one, add additional lines to specify them.
        context.Response.Cache.VaryByParams["Res"] = true;
    }
    else
    {
        // The response is not cached
        context.Response.Cache.SetExpires(DateTime.Now.AddDays(-1));
        context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
    }

    The next section checks to see if the resource resides in another assembly. If the Assembly query string option has been omitted, it assumes the resource is in the same assembly as the class. If specified, it looks for the named assembly and, if found, searches for the resource within its manifest. When loading from a different assembly, the internal class path names are ignored and the name matched during the search is used instead:

    // Get the resource from this assembly or another?
    if(context.Request.QueryString["Assembly"] == null)
        asm = Assembly.GetExecutingAssembly();
    else
    {
        Assembly[] asmList =
            AppDomain.CurrentDomain.GetAssemblies();
        string strSearchName =
            context.Request.QueryString["Assembly"];
    
        foreach(Assembly a in asmList)
            if(a.GetName().Name == strSearchName)
            {
                asm = a;
                break;
            }
    
        if(asm == null)
            throw new ArgumentOutOfRangeException("Assembly",
                           strSearchName, "Assembly not found");
    
        // Now get the resources listed in the assembly manifest
        // and look for the filename. Note the fact that it is
        // matched on the filename and not necessarily the path
        // within the assembly. This may restricts you to using
        // a filename only once, but it also prevents the problem
        // that the VB.NET compiler has where it doesn't seem to
        // output folder names on resources.
        foreach(string strResource in asm.GetManifestResourceNames())
            if(strResource.EndsWith(strResName))
            {
                strResName = strResource;
                bUseInternalPath = false;
                break;
            }
    }

    As given, the class can serve up various image and script types, some styles for the demo, plus an additional XML file to demonstrate the NoCache option. A simple switch statement is used to determine what type to send back. The context.Response.ContentType property is set accordingly, the resource is retrieved, and it is then written to the response stream. You can expand or reduce the code to suit your needs:

    switch(strType)
    {
        case "gif":     // Image types
        case "jpg":
        case "jpeg":
        case "bmp":
        case "png":
        case "tif":
        case "tiff":
            if(strType == "jpg")
                strType = "jpeg";
            else
                if(strType == "png")
                    strType = "x-png";
                else
                    if(strType == "tif")
                        strType = "tiff";
    
            context.Response.ContentType = 
                                     "image/" + strType;
    
            if(bUseInternalPath == true)
                strResName = cImageResPath + strResName;
    
            s = asm.GetManifestResourceStream(strResName);
    
            nLen = Convert.ToInt32(s.Length);
            byImage = new Byte[nLen];
            s.Read(byImage, 0, nLen);
    
            context.Response.OutputStream.Write(
                                        byImage, 0, nLen);
            break;
    
        case "js":      // Script types
        case "vb":
        case "vbs":
            if(strType == "js")
                context.Response.ContentType = 
                                      "text/javascript";
            else
                context.Response.ContentType = 
                                        "text/vbscript";
    
            if(bUseInternalPath == true)
                strResName = cScriptResPath + strResName;
    
            sr = new StreamReader(
                asm.GetManifestResourceStream(strResName));
            context.Response.Write(sr.ReadToEnd());
            break;
    
        case "css":     // Some style sheet info
            // Not enough to embed so we'll write 
            // it out from here
            context.Response.ContentType = "text/css";
    
            if(bUseInternalPath == true)
                context.Response.Write(".Style1 { font-weight: bold; " +
                    "color: #dc143c; font-style: italic; " +
                    "text-decoration: underline; }\n" +
                    ".Style2 { font-weight: bold; color: navy; " +
                    "text-decoration: underline; }\n");
            else
            {
                // CSS from some other source
                sr = new StreamReader(
                    asm.GetManifestResourceStream(strResName));
                context.Response.Write(sr.ReadToEnd());
            }
            break;
    
        case "htm":     // Maybe some html
        case "html":
            context.Response.ContentType = "text/html";
    
            sr = new StreamReader(
                asm.GetManifestResourceStream(strResName));
            context.Response.Write(sr.ReadToEnd());
            break;
    
        case "xml":     // Even some XML
            context.Response.ContentType = "text/xml";
    
            sr = new StreamReader(
                asm.GetManifestResourceStream(
                "ResServerTest.Web.Controls." + strResName));
    
            // This is used to demonstrate the NoCache option.
            // We'll modify the XML to show the current server
            // date and time.
            string strXML = sr.ReadToEnd();
    
            context.Response.Write(strXML.Replace("DATETIME",
                DateTime.Now.ToString()));
            break;
    
        default:    // Unknown resource type
            throw new Exception("Unknown resource type");
    }

    For simple text-based resources such as scripts, the StreamReader.ReadToEnd method can be used to retrieve the resource. For binary resources such as images, you must allocate an array and use StreamReader.Read to load the image into the array. Once loaded, you can write the array out to the client as shown above.

    If an unknown resource type is requested or if it cannot be loaded from the assembly, an exception is thrown. For script resource types, the exception handler will convert the response to the appropriate type and send back a message box or alert so that the exception is displayed when the page loads. This will give you a chance to see what failed during development. For an XML resource, the exception handler will send back an XML response containing nodes with the resource name and the error description. For all other resource types, nothing is returned. Images will display a broken image placeholder, which serves as an indication that you may have done something wrong:

    catch(Exception excp)
    {
        XmlDocument xml;
        XmlNode node, element;
    
        string strMsg = excp.Message.Replace("\r\n", " ");
    
        context.Response.Clear();
        context.Response.Cache.SetExpires(
                        DateTime.Now.AddDays(-1));
        context.Response.Cache.SetCacheability(
                        HttpCacheability.NoCache);
    
        // For script, write out an alert describing the problem.
        // For XML, send an XML response containing the exception.
        // For all other resources, just let it display a broken
        // link or whatever.
        switch(strType)
        {
            case "js":
                context.Response.ContentType = "text/javascript";
                context.Response.Write(
                    "alert(\"Could not load resource '" +
                    strResName + "': " + strMsg + "\");");
                break;
    
            case "vb":
            case "vbs":
                context.Response.ContentType = "text/vbscript";
                context.Response.Write(
                    "MsgBox \"Could not load resource '" +
                    strResName + "': " + strMsg + "\"");
                break;
    
            case "xml":
                xml = new XmlDocument();
                node = xml.CreateElement("ResourceError");
    
                element = xml.CreateElement("Resource");
                element.InnerText = "Could not load resource: " +
                                                        strResName;
                node.AppendChild(element);
    
                element = xml.CreateElement("Exception");
                element.InnerText = strMsg;
                node.AppendChild(element);
    
                xml.AppendChild(node);
                context.Response.Write(xml.InnerXml);
                break;
        }
    }
    finally
    {
        if(sr != null)
            sr.Close();
    
        if(s != null)
            s.Close();
    }

    Using the resource server handler in your control

    Using the resource server handler in the custom control is very simple. Just add code to your class to render the attributes, script tags, or other resource types such as images that utilize the resource server page name. This is done by calling the ResSrvHandler.ResourceUrl method with the name of the resource and a Boolean flag indicating whether or not to cache it on the client. The demo control contains several examples.

    // An image
    img = new HtmlImage();

    // Renders as:
    // src="ResServerTest.Web.Controls.aspx?Res=FitHeight.bmp"
    img.Src = ResSrvHandler.ResourceUrl("FitHeight.bmp", true);

    // Call a function in the client-side script code registered below
    img.Attributes["onclick"] = "javascript: FitToHeight()";

    this.Controls.Add(img);

    // Register the client-side script module
    // Renders as: <script type='text/javascript'
    // src='ResServerTest.Web.Controls.aspx?Res=DemoCustomControl.js'>
    // </script>
    this.Page.RegisterStartupScript("Demo_Startup",
    "<script type='text/javascript' src='" +
    ResSrvHandler.ResourceUrl("DemoCustomControl.js", true) +
    "'></script>");

    // Register the style sheet
    // Renders as: <link rel='stylesheet' type='text/css'
    // href='ResServerTest.Web.Controls.aspx?Res=Styles.css'>
    this.Page.RegisterScriptBlock("Demo_Styles",
    "<link rel='stylesheet' type='text/css' href='" +
    ResSrvHandler.ResourceUrl("Styles.css") + "'>\n");

    As noted earlier, the lack of the NoCache query string option will cause the resources to be cached on the client. To turn off caching for a resource, simply specify false for the cache parameter of the ResourceUrl method, or add the NoCache parameter to the query string if hand-coding the URL. The demo ASPX page contains an example that retrieves an XML document from the control assembly. It uses the no caching option so that it displays the current time on the server every time the XML resource is requested. It also contains a couple of examples that retrieve resources from assemblies other than the custom control's assembly.

    <script type='text/javascript'>
    // Demonstrate the loading of uncached,
    // dynamic resources outside the
    // control class. This gets some XML
    // from the resource server page.
    function funShowXML()
    {
    window.open(
    'ResServerTest.Web.Controls.aspx?Res=Demo.xml&NoCache=1',
    null,
    'menubar=no,personalbar=no,resizable=yes,' +
    'scrollbars=yes,status=no,' +
    'toolbar=no,screenX=50,screenY=50,' +
    'height=400,width=800').focus()
    }
    </script>

    Using the control and the resource server handler in an application

    In the application's project, add a reference to your custom control's assembly and add your custom control to the application's forms in the normal fashion. To use the resource server handler, add an entry in the <system.web> section of your application's Web.Config file like the following:

    <!-- Demo Control Resource Server Handler
    Add this section to map the resource requests to the resource
    handler class in the custom control assembly. -->
    <httpHandlers>
    <add verb="*" path="ResServerTest.Web.Controls.aspx"
    type="ResServerTest.Web.Controls.ResSrvHandler,
    ResServerTest.Web.Controls"/>
    </httpHandlers>

    Modify the path attribute so that it matches the ResSrvHandler.cResSrvHandlerPageName constant. Modify the type attribute to reference your resource server handler's class name (including its namespace) followed by a comma and then the name of the assembly. This entry causes any requests containing the page name specified in the path attribute, regardless of the folder, to get mapped to your resource handler class.

    Allowing anonymous access to resources when using forms-based authentication

    When using forms-based authentication to secure an entire applic

    Themes In ASP.NET 2.0

    Posted by scott on Sunday, August 07, 2005

    This article will provide an in-depth look at the Themes feature in ASP.NET.

    In a previous article, we looked at Master Pages in ASP.NET 2.0. Master pages allow you to dictate the layout and common content for the pages in your application using template files with a .master extension. The Themes feature in ASP.NET 2.0 allows you to dictate the appearance of controls in your application using template files with a .skin extension, and with style sheets. In this article, we will examine the Themes feature in-depth.

    There is some overlap in what you can do with themes and master pages, as we will see later in the article. What you ultimately can achieve with the combination of these two features is the following:

    • Easily build a web application with consistent layout and appearance across all pages
    • Easily change the layout and appearance of all pages just by modifying a few template files
    • Easily personalize an application at run time for a specific user by letting the user chose their favorite look from a number of appearance and layout options
    ASP.Net 2.0 - Master Pages: Tips, Tricks, and Traps
    www.odetocode.com/Articles/450.aspx
    ASP.Net 2.0 - Master Pages: Tips, Tricks, and Traps

    Posted by scott on Tuesday, April 11, 2006

    MasterPages are a great addition to the ASP.NET 2.0 feature set, but are not without their quirks. This article will highlight the common problems developers face with master pages, and provide tips and tricks to use master pages to their fullest potential.

    Master pages are a great addition to the ASP.NET 2.0  feature set. Master pages help us build consistent and maintainable user interfaces. Master pages, however, are not without their quirks. Sometimes master page behavior is surprising, and indeed the very name master page can be a bit misleading. In this article, we are going to examine some of the common problems developers run into when using master pages, and demonstrate some practical advice for making effective use of master pages.

    Event Bubbling From Web User Controls in ASP.NET (C#)
    www.odetocode.com/Articles/94.aspx
    Event Bubbling From Web User Controls in ASP.NET (C#)

    Posted by scott on Saturday, February 14, 2004

    This C# code example demonstrates event bubbling from a web user control (ASCX) to a parent page (ASPX) and the shows the flow of events through execution.

    Some user controls are entirely self contained, for example, a user control displaying current stock quotes does not need to interact with any other content on the page. Other user controls will contain buttons to post back. Although it is possible to subscribe to the button click event from the containing page, doing so would break some of the object oriented rules of encapsulation. A better idea is to publish an event in the user control to allow any interested parties to handle the event.

    This technique is commonly referred to as “event bubbling” since the event can continue to pass through layers, starting at the bottom (the user control) and perhaps reaching the top level (the page) like a bubble moving up a champagne glass.

    Creating a Simple Configuration Section Handler Using ASP.NET 2.0 - 123aspx.com ASP.NET Resource Dir
    www.123aspx.com/redir.aspx?res=34641
    Introduction [ Back To Top ] In ASP.NET 1.x it was fairly difficult to create a custom configuration section in ASP.NET 2.0. Many enterprise applications then relied upon the AppS
    How to: Apply ASP.NET Themes Programmatically
    msdn2.microsoft.com/en-us/library/tx35bd89.aspx
    How to: Apply ASP.NET Themes Programmatically 

    In addition to specifying theme and skin preferences in page declarations and configuration files, you can apply themes programmatically. You can set both page themes and style sheet themes programmatically; however, the procedure for applying each type of theme is different.

    Note

    The themes referenced below are not included in ASP.NET. To create a custom theme, see How to: Define ASP.NET Themes.

    To apply a page theme programmatically

    • In a handler for the page's PreInit method, set the page's Theme property.

      The following code example shows how to set a page's theme conditionally based on a value passed in the query string.

      Visual Basic
      Protected Sub Page_PreInit(ByVal sender As Object, _
              ByVal e As System.EventArgs) _
              Handles Me.PreInit
          Select Case Request.QueryString("theme")
              Case "Blue"
                  Page.Theme = "BlueTheme"
              Case "Theme2"
                  Page.Theme = "PinkTheme"
          End Select
      End Sub
      

      C#
      Protected void Page_PreInit(object sender, EventArgs e)
      {
          switch (Request.QueryString["theme"])
          {
              case "Blue":
                  Page.Theme = "BlueTheme";
                  break;
              case "Pink":
                  Page.Theme = "PinkTheme";
                  break;
          }
      }
      

    To apply a style sheet theme programmatically

    • In the page's code, override the StyleSheetTheme property and in the get accessor, return the name of the style sheet theme.

      The following code example shows how to set a theme named BlueTheme as the style sheet theme for a page:

      Visual Basic
      Public Overrides Property StyleSheetTheme() As String
         Get
             Return "BlueTheme "
         End Get
         Set(ByVal value As String)
         End Set
      End Property
      

      C#
      public override String StyleSheetTheme
      {
        get { return "BlueTheme "; }
      }
      

    To apply control skins programmatically

    • In a handler for the page's PreInit method, set the control's SkinID property.

      The following code example shows how to set the SkinID property of a Calendar control.

      Visual Basic
      Sub Page_PreInit(ByVal sender As Object, _
              ByVal e As System.EventArgs) _
              Handles Me.PreInit
          Calendar1.SkinID = "BlueTheme"
      End Sub
      

      C#
      void Page_PreInit(object sender, EventArgs e)
      {
          Calendar1.SkinID = "MySkin";
      }
    Dynamic Themes assignment - Rick Strahl's WebLog
    west-wind.com/weblog/posts/6168.aspx
    Dynamic Themes assignment

     

    If you’ve been following this Blog you probably know I’ve been mucking around with a bunch of infrastructure stuff recently and one of the things I’ve played with a lot is trying to build a generic solution for skinnable UIs. To that effect I ended up using a mixture of Themes and Master Pages in a separate set of template directories that match the Themes templates.

     

    The idea if you change the Theme the Master Page template changes along with the theme. I rigged this up a while ago but it was a bitch to get this to work right in ASP.NET 2.0 stock projects because of various inheritance issues. In the end I got it to work though although I had to make some compromises in what goes onto the master pages (it works fine in WAP though).

     

    Today I finally put that stuff live on my site, and just for kicks I decided to try and switch themes on the pre-compiled site. To my dismay – nothing happened. I changed the theme in the web.config file, but it no effect at all on the site... What? Isn’t that the whole point of Themes?

     

    Back here in my dev environment it works fine, so it must have something to do with precompilation. I double checked by compiling my app into a local deployment directory and then setting up a virtual for it and sure enough I see the same behavior – the app is stuck with whatever Theme was used in web.config when the project was compiled and changing the value in Web.config has zero effect. Chalk that one up to another unexpected ASP.NET 2.0 project behavior where design time and runtime vary.

     

    This project is a stock project compiled with fixed file options, non-updateable and then set up using ASPNET_MERGE into a single assembly. What’s really odd is that the web.config setting that is during compile time seems to determine which Theme the app is stuck on.  I haven’t had the inclination to check this out further, but this seems pretty silly if theming adjustments through web.config doesn’t work with a precompiled site somehow. I must be missing something here...

     

    I didn’t bother digging deeper because I wanted to add dynamic application driven theming to the site anyway, and so the web.config setting was just a temporary solution. In the end I wanted it so that Themes can be interactively changed through the Configuration API of the application. Thankfully setting the Theme through Page.OnPreInit works correctly and that's what I'm doing now.

     

    In order to select set the theme (which sets both the Theme as well as the Master Page template) at runtime I use code like this:

     

    protected override void OnPreInit(EventArgs e)

    {

        this.Theme = App.Configuration.Theme;

     

        // *** Use a templated MasterPage

        if (this.TemplateMasterPage != "" && !string.IsNullOrEmpty(this.Theme))

            this.MasterPageFile = this.GetThemeTemplatePath(this.TemplateMasterPage);

     

        base.OnPreInit(e);

    }

     

    /// <summary>

    /// Figures out the appropriate theme specific Template path

    /// that is used to hold Master Pages and other templates.

    ///

    /// This version returns a fully qualified file Url.

    /// </summary>

    /// <param name="FileName"></param>

    /// <returns></returns>

    public string GetThemeTemplatePath(string Filename)

    {

        if (Filename == null)

            return "~/App_Templates/";

     

        return "~/App_Templates/" + this.Theme + "/" + Filename;

    }

     

    This code is defined in my page base class for the app. It first gets the Active Theme for the application from my Configuration store and then assigns that explicitly which effective changes the theme. The Theme is then used to look up the appropriate Master Page template by filename. Nice and simple and it works effectively.

     

    In the configuration form the Theme is then displayed and selected and stored in the global configuration object. To get a list of themes I found this code from K. Scott Allen.

     

    /// <summary>

    /// Loads the list of available themes into txtTheme dropdown

    /// </summary>

    private void LoadThemes()

    {

        VirtualDirectory ThemeDir = HostingEnvironment.VirtualPathProvider.GetDirectory("~/App_Themes");

     

        foreach (VirtualDirectory Dir in ThemeDir.Directories)

        {

            this.txtTheme.Items.Add( Dir.Name );

        }

      

    }    

     

    I had been parsing the directories out of the App_Themes directory and like Scott I was worried about the permissions issues that might not allow access there.

     

    The form displays among other things the list of themes and lets the admin pick the theme that is used on the site.

     

    I’ve got what I needed working but I’m still thinking about this funky behavior where the web.config setting isn’t changing the them... Any body have any thoughts?

     

    System.Data.SqlClient.SqlException: Login failed - ASP Free
    forums.aspfree.com/sql-development-6/system-data-s...
    <configuration>

    <!-- application specific settings -->
    <appSettings>
    <add key="connString" value="user id=sa;pwd=;server=<serverName>\VSdotNET;integrated security=SSPI;trusted_connection=true;initial catalog=<Database>;" />
    </appSettings>
    ....


    In C# access looks like this....

    SqlConnection conn = new SqlConnection(ConfigurationSettings.AppSettings["connString"]);
    try {
    conn.Open();
    ...


    But, that still doesn't solve any login failures I had....
    Even though I added my specified login because I wasn't using sa; actually I still am not using sa I setup another login. I thought I could just have it but you need to allow ASPNET to login too. Anyhow, here's how....

    Microsoft sql server -> enterprise manager.

    Microsoft SQL Server
    SQL Server Group
    serverName\VSDOTNET (Windows NT)
    Security
    Logins <right click>

    New Login
    Name <click ....>
    scrolll down to the ASPNET (aspnet wp account)
    You can set default database if you wish here too.

    After that I went to the specific database I was interested in
    and set permissions to allow the various database commands.
    ASP.NET.4GuysFromRolla.com: Sending Email in ASP.NET 2.0
    aspnet.4guysfromrolla.com/articles/072606-1.aspx
    Sending Email in ASP.NET 2.0
    By Scott Mitchell


    Introduction
    Email serves as a ubiquitous, asynchronous notification and information distribution system. Not surprisingly, there are many web development scenarios where server-side code needs to generate an email and scuttle it off to the intended recipient. The email may be destined for a user of the website, informing them of their newly created user account, reminding them of their forgotten password, or emailing them an invoice. Or it may be destined for a web developer or site administrator, providing information of an unhandled exception that just transpired or user feedback.

    Fortunately, ASP.NET makes sending email a breeze. The .NET Framework version 1.x included a number of classes in the System.Web.Mail class that allowed programmatically sending an email with a few scant lines of code. While this namespace and these classes still exist in the .NET Framework version 2.0, they have been deprecated in favor of new mail-related classes found in the System.Net.Mail namespace. (For an article on sending email in ASP.NET version 1.x, see Sending Email from an ASP.NET 1.x Web Page or consult www.SystemWebMail.com.)

    In this article we'll look at the classes in the System.Net.Mail namespace and see how to send an email from an ASP.NET 2.0 page's code-behind class. We'll also look at specifying relay server information in Web.config and how this information can be used in some of the built-in ASP.NET server controls for sending emails (such as when a user creates an account or needs a password reminder/reset). Read on to learn more!

    After reading this article, be sure to check out Sending Email in ASP.NET 2.0: HTML-Formatted Emails, Attachments, and Gracefully Handling SMTP Exceptions, where we'll look at sending HTML-formatted emails, emails with attachments, and gracefully handling SMTP exceptions!

    Exploring the Classes in the System.Net.Mail Namespace
    There are 16 different classes in the System.Net.Mail namespace, all related to send email to a specified Simple Mail Transfer Protocol (SMTP) server for delivery. The two core classes in this namespace are:

    • MailMessage - represents an email message; has properties like From, To, Subject, Body, and so on.
    • SmtpClient - sends a specified MailMessage instance to a specified SMTP server.
    When sending an email from an ASP.NET 2.0 page you will, typically:
    1. Create a MailMessage object
    2. Assign its properties
    3. Create an instance of the SmtpClient class
    4. Specify details about the SMTP server to use (if they're not already specified within Web.config)
    5. Send the MailMessage via the SmtpClient object's Send method
    Steps 1 and 2 may be bypassed as the SmtpClient class's Send method can accept either a MailMessage object or four strings, representing the from, to, subject, and body contents of the email message.

    The System.Net.Mail namespace's other classes allow for more advanced email functionality. There are classes that can be used to add attachments to an email message, to embed objects within an email, to specify SMTP server authentication information, and Exception-derived classes for handling SMTP-specific exceptions. We'll examine using some of these other classes for more advanced scenarios in future articles.

    Providing the SMTP Server's Details
    When sending an email to a friend from Outlook or GMail, the email program establishes a connection with a relay server and sends the contents of the email message, along with information such as the date the email was composed, the email body's format (text or HTML, for example), the recipient(s), and so on. The relay server accepts the message and then connects to the recipient's SMTP server and sends the message. Once the message has been delivered, the recipient can, at some later point in time, pull down the message using a different protocol (such as IMAP or POP3).

    Therefore, to send an email from an ASP.NET page we need to provide the SmtpClient class with information about the relay server. Along with the hostname of the relay server, you can specify the port (typically port 25 is used), whether or not to use SSL when communicating your email message contents to the relay server, and authentication credentials (if necessary). Alternatively, if you have a local SMTP service installed on your web server, it may periodically monitor a particular "drop-off" directory, sending any messages that appear in that directory. You can configure whether the SmtpClient class relays its email messages to a separate relay server or if it drops it off in a specified pickup directory through the DeliveryMethod property.

    The relay server information used by the SmtpClient class can be specified programmatically, through the SmtpClient class's properties, or can be centralized in Web.config. To use the Web.config approach, add a <system.net> element within the <configuration> element. Then, add a <mailSettings> element that contains an <smtp> element whose settings are specified within its <network> child element, like so:

    <configuration>
      <!-- Add the email settings to the <system.net> element -->
      <system.net>
        <mailSettings>
          <smtp>
            <network 
                 host="relayServerHostname" 
                 port="portNumber"
                 userName="username"
                 password="password" />
          </smtp>
        </mailSettings>
      </system.net>
    
      <system.web>
        ...
      </system.web>
    </configuration>
    

    The host attribute contains the relayServerHostname. If you are using an external relay server, the relayServerHostname might be something like smtp.yourisp.com. If the relay server's port number is something other than the typical port 25, specify it through the port attribute. Most external relay servers require authentication of some sort (in order to prevent anonymous spammers from sending their garbage through the relay). The userName and password attributes can be provided in the case where username/password authentication is needed.

    Only a subset of the SmtpClient properties can be specified through settings in Web.config. To customize the other SmtpClient properties - EnableSsl, Timeout, and so on - set them programmatically when sending the email (step 4 from the list of five steps examined earlier in this article).

    Sending an Administrator Email Through a Feedback Web Page
    To illustrate sending an email using the MailMessage and SmtpClient classes, I've created a simple feedback page example. In this page the user is prompted for their email address, the subject of their feedback, and their feedback.

    <table border="0">
        <tr>
            <td><b>Your Email:</b></td>
            <td><asp:TextBox runat="server" ID="UsersEmail" Columns="30"></asp:TextBox></td>
        </tr>
        <tr>
            <td><b>Subject:</b></td>
            <td><asp:TextBox runat="server" ID="Subject" Columns="30"></asp:TextBox></td>
        </tr>
        <tr>
            <td colspan="2">
                <b>Body:</b><br />
                <asp:TextBox runat="server" ID="Body" TextMode="MultiLine" Columns="55" Rows="10"></asp:TextBox>
            </td>
        </tr>
        <tr>
            <td colspan="2" align="center">
                <asp:Button runat="server" ID="SendEmail" Text="Send Feedback" />
            </td>
        </tr>
    </table>

    Once the user has supplied the feedback information and clicked the "Send Feedback" button, a postback ensues and the Button's Click event fires. Inside the event handler, a MailMessage object is created and its To, From, Subject, and Body properties are set according to the information provided by the user. With the MailMessage object created and its properties populated, the email is then sent through the SmtpClient class's Send method.

    Protected Sub SendEmail_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles SendEmail.Click
        '!!! UPDATE THIS VALUE TO YOUR EMAIL ADDRESS
        Const ToAddress As String = "you@youremail.com"

        '(1) Create the MailMessage instance
        Dim mm As New MailMessage(UsersEmail.Text, ToAddress)

        '(2) Assign the MailMessage's properties
        mm.Subject = Subject.Text
        mm.Body = Body.Text
        mm.IsBodyHtml = False

        '(3) Create the SmtpClient object
        Dim smtp As New SmtpClient

        '(4) Send the MailMessage (will use the Web.config settings)
        smtp.Send(mm)
    End Sub

    We didn't need to set any of the SmtpClient class's properties here in code because they have been specified in Web.config (download the complete code at the end of this article to run this application on your computer).

    Conclusion
    Along with a plethora of other improvements from ASP.NET 1.x, the email sending capabilities in ASP.NET 2.0 have been updated and moved to a new namespace, System.Net.Mail. In 2.0 the relay server settings can easily be decoupled from the ASP.NET code and moved into the Web.config file, as we saw in this example. Moreover, there's better support for relay server authentication. Future articles will explore more advanced email scenarios, such as: crafting HTML-formatted emails, sending attachments, embedding objects within the email body, handling SMTP/relay server-related exceptions, and so on.

    See Sending Email in ASP.NET 2.0: HTML-Formatted Emails, Attachments, and Gracefully Handling SMTP Exceptions for a look at sending HTML-formatted emails, emails with attachments, and gracefully handling SMTP exceptions...

    Happy Programming!

  • By Scott Mitchell


    Attachments

  • Download the complete code samples examined in this article (in ZIP format)
  • Suggested Readings

  • Sending Email with System.Net.Mail (includes a C# example)
  • www.SystemNetMail.com (a great set of FAQs and samples for sending email using the System.Net.Mail namespace classes)
  • Sending Email in ASP.NET 2.0: HTML-Formatted Emails, Attachments, and Gracefully Handling SMTP Exceptions
  • ASP.NET.4GuysFromRolla.com: Sending Email in ASP.NET 2.0: HTML-Formatted Emails, Attachments, and Gr
    aspnet.4guysfromrolla.com/articles/080206-1.aspx
    Sending Email in ASP.NET 2.0: HTML-Formatted Emails, Attachments, and Gracefully Handling SMTP Exceptions
    By Scott Mitchell


    Introduction
    As detailed in last week's article, Sending Email in ASP.NET 2.0, the .NET Framework version 2.0 includes a new namespace (System.Net.Mail) and new classes for sending email. (The namespace (System.Web.Mail) and classes used in the .NET Framework version 1.x still exist for backwards compatibility.) Last week we examined how to use the MailMessage and SmtpClient classes in the System.Net.Mail namespace for sending simple, plain-text email messages.

    This article looks at the more advanced email-related options. We'll see how to send HTML-formatted emails, how to include attachments, and how to gracefully handle SMTP exceptions when sending an email (such as invalid relay server credentials or if the relay server is offline). Read on to learn more!

    This article assumes that you are already familiar with sending simple, plain-text emails from an ASP.NET 2.0 web page; if not, please first read Sending Email in ASP.NET 2.0 before tackling this article...

    In Sending Email in ASP.NET 2.0 we saw how to send plain-text emails by assigning the contents of the email to the MailMessage class's Body property. To send HTML-formatted emails, simply set the Body property to the HTML content to send, and then mark the MailMessage class's IsBodyHtml property to True.

    To demonstrate sending an HTML-formatted message, I created a sample named HtmlEmail.aspx available for download at the end of this article. The germane code follows:

    '(1) Create the MailMessage instance
    Dim mm As New MailMessage(FromEmailAddress, ToEmailAddress)

    '(2) Assign the MailMessage's properties
    mm.Subject = "HTML-Formatted Email Demo Using the IsBodyHtml Property"
    mm.Body = "<h2>This is an HTML-Formatted Email Send Using the <code>IsBodyHtml</code> Property</h2><p>Isn't HTML <em>neat</em>?</p><p>You can make all sorts of <span style=""color:red;font-weight:bold;"">pretty colors!!</span>.</p>"
    mm.IsBodyHtml = True

    '(3) Create the SmtpClient object
    Dim smtp As New SmtpClient

    '(4) Send the MailMessage (will use the Web.config settings)
    smtp.Send(mm)

    As you can see, simply set the Body property to the HTML content to send and the IsBodyHtml property to True, and you're done! The actual email content that gets sent to the relay server (and eventually down to the recipient's email client), looks something like the following:

    x-sender: ToEmailAddress
    x-receiver: FromEmailAddress
    mime-version: 1.0
    from: FromEmailAddress
    to: ToEmailAddress
    date: 25 Jul 2006 15:06:44 -0700
    subject: HTML-Formatted Email Demo Using the IsBodyHtml Property
    content-type: text/html; charset=us-ascii
    content-transfer-encoding: quoted-printable

    <h2>This is an HTML-Formatted Email Send Using the <code>IsBodyHtml</code>=
    Property</h2><p>Isn't HTML <em>neat</em>?</p><p>You can make all sorts=
    of <span style=3D"color:red;font-weight:bold;">pretty colors!!</span>.</p>

    Viewing the Email Content Sent to the Relay Server
    Interested in viewing the actual content sent to the relay server by the SmtpClient class (like the content shown above)? In Sending Email in ASP.NET 2.0 we discussed how the SmtpClient class can be configured to send the email to a relay server or have it dropped off in a specified directory. Using the latter option, we can explore the actual email content that would otherwise have been sent to the relay server. Check out the Web.config file in the code available for download at the end of this article - there's a commented out <smtp> element that shows how to configure the SmtpClient class to dump the email's contents to a specified directory.

    The email client - assuming it supports HTML-formatted emails - will display the HTML content within the email.

    Caveats on Sending HTML-Formatted Emails
    When sending HTML-formatted emails, understand that the HTML you see on your screen may differ quite a bit from what your recipients see. Most all email clients strip out potentially dangerous HTML content (ActiveX controls and the like), many prevent JavaScript from running, and most handle external styles poorly. For a more thorough discussion on potential problems with sending HTML-formatted emails, check out Top 10 HTML Email Mistakes and CSS and Email, Kissing in a Tree.

    Including Attachments
    The MailMessage class has an Attachments property that is a collection of Attachment class instances. You can attach an existing file on the web server to the email message or base the content's attachment on a Stream. To illustrate sending emails will attachments, I created a demo where the visitor can fill out a feedback-like form to have an email sent to administrator. However, this feedback form allows the visitor to pick a file from their computer to be attached to the email sent from the web page (much like how the web-based email web applications - Hotmail, GMail, etc. - allow you to attach a file from your computer when sending an email).

    To allow the visitor to attach a file from their computer, we need to allow the user to upload a file from their machine to the web server. This can be easily accomplished using the FileUpload control (which is new to ASP.NET 2.0). Let's look at the declarative syntax used for creating the user interface for this demo:

    <table border="0">
        <tr>
            <td><b>Your Email:</b></td>
            <td><asp:TextBox runat="server" ID="UsersEmail" Columns="30"></asp:TextBox></td>
        </tr>
        <tr>
            <td><b>File to Send:</b></td>
            <td>
                <asp:FileUpload ID="AttachmentFile" runat="server" />
            </td>
        </tr>
        <tr>
            <td colspan="2">
                <b>Body:</b><br />
                <asp:TextBox runat="server" ID="Body" TextMode="MultiLine" Columns="55" Rows="10"></asp:TextBox>
            </td>
        </tr>
        <tr>
            <td colspan="2" align="center">
                <asp:Button runat="server" ID="SendEmail" Text="Send Feedback" />
            </td>
        </tr>
    </table>

    The FileUpload control renders as a <input type="file" ... /> HTML element, which, in the browser, is displayed as a textbox with a Browse button. When clicked, a dialog box is opened from which the user can pick a file from their computer.

    After filling in the feedback form, selecting a file to upload, and clicking the "Send Feedback" button, a postback occurs, sending the contents of the selected file up to the web server. In the "Send Feedback" Button's Click event handler, a MailMessage object is created and an attachment is added. Since the FileUpload provides a Stream to the uploaded data, we can simply point the new Attachment object at this Stream. There's no need to save the uploaded file to the web server's file system.

    'Make sure a file has been uploaded
    If String.IsNullOrEmpty(AttachmentFile.FileName) OrElse AttachmentFile.PostedFile Is Nothing Then
        Throw New ApplicationException("Egad, a file wasn't uploaded... you should probably use more graceful error handling than this, though...")
    End If

    '(1) Create the MailMessage instance
    Dim mm As New MailMessage(FromEmailAddress, ToEmailAddress)

    '(2) Assign the MailMessage's properties
    mm.Subject = "Emailing an Uploaded File as an Attachment Demo"
    mm.Body = Body.Text
    mm.IsBodyHtml = False

    'Attach the file
    mm.Attachments.Add(New Attachment(AttachmentFile.PostedFile.InputStream, AttachmentFile.FileName))

    '(3) Create the SmtpClient object
    Dim smtp As New SmtpClient

    '(4) Send the MailMessage (will use the Web.config settings)
    smtp.Send(mm)

    The Attachment constructor overload used in the code sample above expects two inputs: a reference to the Stream that contains the data to attach, and the name to use for the attachment. The FileUpload's InputStream and FileName properties are used for these two values.

    Handling SMTP Exceptions
    When sending an email from an ASP.NET page, what happens if the relay server is down, or if the authentication information used is invalid? In the face of an SMTP error, the SmtpClient class will throw an SmtpException exception. To gracefully handle such problems, we can add exception handling code around the code that sends the email. If there's an SmtpException we can then display a more friendly and informative error message (or, perhaps, write the email's contents to a file to be sent later).

    In the download at the end of this article I've included a demo that allows the visitor to specify the relay server to use, along with authentication information. If there's an error in attempting to send an email, a client-side alert box is displayed, explaining the problem. To test this out, enter an invalid relay server hostname or invalid credentials for a relay server that requires authentication.

    Try
        '(1) Create the MailMessage instance
        Dim mm As New MailMessage(FromEmailAddress, ToEmailAddress)

        '(2) Assign the MailMessage's properties
        mm.Subject = "Test Email... DO NOT PANIC!!!1!!!111!!"
        mm.Body = "This is a test message..."
        mm.IsBodyHtml = False

        '(3) Create the SmtpClient object
        Dim smtp As New SmtpClient

        'Set the SMTP settings...
        smtp.Host = Hostname.Text
        If Not String.IsNullOrEmpty(Port.Text) Then
            smtp.Port = Convert.ToInt32(Port.Text)
        End If

        If Not String.IsNullOrEmpty(Username.Text) Then
            smtp.Credentials = New NetworkCredential(Username.Text, Password.Text)
        End If

        '(4) Send the MailMessage (will use the Web.config settings)
        smtp.Send(mm)

        'Display a client-side popup, explaining that the email has been sent
        ClientScript.RegisterStartupScript(Me.GetType(), "HiMom!", String.Format("alert('An test email has successfully been sent to {0}');", ToAddress.Replace("'", "\'")), True)

    Catch smtpEx As SmtpException
        'A problem occurred when sending the email message
        ClientScript.RegisterStartupScript(Me.GetType(), "OhCrap", String.Format("alert('There was a problem in sending the email: {0}');", smtpEx.Message.Replace("'", "\'")), True)

    Catch generalEx As Exception
        'Some other problem occurred
        ClientScript.RegisterStartupScript(Me.GetType(), "OhCrap", String.Format("alert('There was a general problem: {0}');", generalEx.Message.Replace("'", "\'")), True)
    End Try

    This code catches both SMTP-specific error messages and general ones (such as assigning invalid email addresses to the MailMessage object's To or From properties). In either case, a client-side alert box is displayed informing the user of the details of the error.

    Conclusion
    In this article we saw how to send HTML-formatted emails, send emails with attachments, and gracefully handle exceptions arising from sending an email message. Sending an HTML-formatted email is as simple as specifying the HTML content in the Body property and setting the IsBodyHtml property to True. The real challenge comes in making sure the HTML content used is rendered as expected by the popular email clients. To add an attachment to an email, simply add an Attachment object to the MailMessage's Attachments collection. The data for the attachment can come from a file on the web server or from a Stream. Finally, to handle SMTP-level exceptions, add exception handling code that catches the SmtpException thrown by the SmtpClient class when unable to transport the message to the relay server.

    Happy Programming!

  • By Scott Mitchell


    Attachments

  • Download the complete code samples examined in this article (in ZIP format)
  • Suggested Readings

  • www.SystemNetMail.com (a great set of FAQs and samples for sending email using the System.Net.Mail namespace classes)
  • 15 Seconds : Simplified and Extended Data Binding Syntax in ASP.NET 2.0
    www.15seconds.com/issue/040630.htm

    The New Two-Way Data Binding Syntax

    When building pages that use list or grid controls in ASP.NET 2.0, the new data source controls make it much easier than the v1.x approach of using code in the page to generate a DataReader or DataSet instance, and then binding this to the control. In version 2.0, you simply place a data source control on the page, and link it to the list or grid control using the DataSourceID attribute of property of the control. You saw an example of this in the earlier section "Using Multiple Data Bound Values in a Hyperlink Control".

    This approach can be used with any of the list controls that are provided with ASP.NET 2.0, including those that were originally introduced in version 1.0 (for example, the DataGrid, DataList, Repeater, ListBox, and HtmlSelect controls). However, data source controls really come into their own when used with the new GridView, DetailsView and FormView controls.

    Automatic Updates with the New Data Source Controls

    The original version's (1.0) list and grid controls are effectively "read-only", in that you have to write code that is executed in response to events raised by controls within the list or grid (for example, when the user clicks an Update or Delete link). This code must collect the values in the relevant row and then execute some code you've written to push these values back into the data store - possibly using stored procedures or explicit SQL statements. In other words, data binding is only being used to populate the list or grid control.

    In the GridView, DetailsView and FormView controls, this is no longer the case. As long as the data source is updateable, and you have provided the appropriate update, insert and delete commands, the control will automatically push changes to the values in the list or grid control to which it's attached back into the data source. You can specify the update, insert and delete commands as explicit SQL statements or use stored procedures if you need to interact with a more complex database table structure.

    What's important with respect to the topic of this article, however, is how these new controls affect the syntax used for data binding. When the columns or rows for a GridView or DetailsView control are auto-generated, or specified using a the pre-defined field types such as BoundField, HyperLinkField, or CheckBoxField, the data source control can figure out what to do when it's time to push changes back into the data store by looking at which columns each of the field types is bound to.

    But, if you use a TemplateField in a GridView or a DetailsView, you must declare all the content for that column or row yourself, just as you would when using a TemplateColumn in a DataGrid control. And, if you use a FormView control, you always have to declare all the content yourself. Like the v1.0 Repeater control, the FormView control does not support pre-defined column types - it uses only templates for content generation.

    Inside the templates of a TemplateField, or in the templates of a FormView control, you use data binding statements to connect the controls you declare there with the columns in the data source control. The interesting point is that the data binding technology must, in this case, support both "read" and "write" operations, rather than being "read-only" as has always been the case in the past. This is termed two-way data binding, and is supported through a new method called Bind.

    The Bind Method

    As with the Eval method, the Bind method has two overloads, which means that it can be used with or without the "format" parameter:

    
    <%# Bind("expression"[, "format"]) %>
    
    
    In fact, other than specifying the new method name, the Bind method is used just like the Eval method. To bind an attribute of a control to the CompanyName column, you use:
    
    <%# Bind("CompanyName") %>
    
    
    If you want to bind an attribute of a control to a value that requires formatting, you add the format parameter:
    
    <%# Bind("OrderDate", "{0:dddd d MMMM}") %>
    
    
    To demonstrate the Bind method, the following code shows a SqlDataSource control with parameterized SQL statements specified for the UpdateCommand and InsertCommand property attributes. The data source control exposes a two-column rowset containing the ID and name of products from the SQL Server Northwind sample database:
    
    <asp:SqlDataSource id="datasource1" runat="server"
         SelectCommand="SELECT ProductID, ProductName FROM Products"
         UpdateCommand="UPDATE Products SET ProductName=@ProductName
                        WHERE ProductID=@ProductID"
         InsertCommand="INSERT INTO Products (ProductName)
                        VALUES (@ProductName)"
    />
    
    
    The next listing shows how the templates of a FormView control can be declared to allow the product name to be edited or a new product to be added. For this to work, the FormView control must know which is the primary key column in the source data, and this is declared using the DataKeyNames attribute (multiple column primary keys are specified using a comma-delimited list of column names in the control declaration, or a String array of column names at runtime).
    
    <asp:FormView id="formview1" DataSourceID="datasource1" runat="server"
                  DataKeyNames="ProductID" AllowPaging="True">
    
      <ItemTemplate>
        <b><%#Eval("ProductName")%></b><p />
        <asp:LinkButton id="btnEdit" runat="server"
             CommandName="Edit" Text="Edit Details" /><br />
        <asp:LinkButton id="btnInsert" runat="server"
             CommandName="New" Text="Add New Product" />
      </ItemTemplate>
    
      <EditItemTemplate>
        <asp:Label id="lblEditID" runat="server"
             Text='<%# Bind("ProductID")%>' />
        <asp:TextBox id="txtEditName" runat="server"
             Text='<%# Bind("ProductName") %>' />
        <asp:LinkButton id="btnUpdate" CommandName="Update"
             Text="Update" runat="server" />
        <asp:LinkButton id="btnCancel" CommandName="Cancel"
             Text="Cancel" runat="server" />
      </EditItemTemplate>
    
      <InsertItemTemplate>
        <asp:TextBox id="txtInsertName" runat="server"
             Text='<%# Bind("ProductName") %>' />
        <asp:LinkButton id="btnAdd" CommandName="Insert"
             Text="Add" runat="server" />
        <asp:LinkButton id="btnAbandon" CommandName="Cancel"
             Text="Cancel" runat="server" />
      </InsertItemTemplate>
    
    </asp:FormView>
    
    
    Then each template section declares a control to either display or allow editing of the product name. In the ItemTemplate, where the value is just being displayed, the code can use the Eval method to bind to the ProductName column. In the EditItemTemplate and the InsertItemTemplate, the product name is displayed in a TextBox control, allowing it to be edited or entered. Because we want to push these values back into the database, the Bind method must be used here.

    One important point to note is that the ProductID column is the primary key in the table, and so it is "read-only" and cannot be changed in the EditItemTemplate. However, it is required to populate the @ProductID parameter of the SQL UPDATE statement - which means it must be placed in an ASP.NET server control (in this case a Label) and bound to the source rowset using the Bind method. If the Eval method is used here, the control will not be able to populate the @ProductID parameter in the SQL statement.

    The ProductID is not required in the InsertItemTemplate, because this is an IDENTITY column in the database - and so it will be populated automatically when a new row is added. For this reason, the SQL INSERT statement has no parameter for the ProductID; hence, there is no requirement to display the value in a server control within this template, or use the Bind method. The only value that is required is the new product name, and the Bind method used with the TextBox where this is entered will push the new value into the database table.

    The screenshot below shows the results. This example, bind-method.aspx, is included in the samples you can download for this article from http://www.daveandal.net/articles/databinding-syntax/.

    Figure 3

    To cause an update, delete or insert operation to take place in a FormView control, you must provide the relevant links or buttons to initiate the process yourself. However, as long as you use the appropriate values for the CommandName property attributes ("Edit", "New", "Update", "Insert", "Delete" or "Cancel") the control will automatically wire them up to the events so that you don't have to write any code. For more details of the FormView control, see the System.Web.UI.WebControls namespace in the Reference section of the SDK installed with the .NET Framework, or available online from http://msdn.microsoft.com.

    When To Use the Bind Method

    When you use templates in a GridView, DetailsView or FormView control, you should be aware of a few points:

    • The Bind method is responsible for providing the values for the parameters declared in the SQL statements that push changes to the data back into the database. Therefore, all the controls that provide values for the parameters in the SQL statement for the current operation must use the Bind method. In the earlier example, the control for the ProductID in the EditItemTemplate is a Label that doesn't allow the value to be edited (it is the primary key for the row). However, the value is required to populate the @ProductID parameter in the SQL UPDATE statement, and so the Bind method is used, rather than the Eval method. However, because ProductID is an IDENTITY column within the database, a value isn't required in the SQL INSERT statement - and a bound control is not required in the InsertItemTemplate section.

    • In general, the Bind method should only be used in an EditItemTemplate and an InsertItemTemplate. It should not be used (or, in fact, required) in an ItemTemplate, AlternatingItemTemplate, or SelectedItemTemplate.

    • A list control in which the Bind method is used must be populated by a data source control that is connected to the list control through the DataSourceID property or attribute. It cannot be used for controls that are populated (as in ASP.NET 1.x) by assigning a rowset to their DataSource property.

    • Every control that uses the Bind method to populate one or more of its attributes must have a unique user-declared value for its ID property (as shown for the controls in the listing above).

    • The Bind method cannot be used in a DataList or a Repeater control, or in any other type of control, and it cannot be used at Page level outside all of the controls. In effect, the only controls to where you can use the Bind method are the GridView, DetailsView and FormView controls.

    ScottGu's Blog : Gotcha: Fixing Error with VS 2005 SP1 Beta and older Web Application Project Templa
    weblogs.asp.net/scottgu/archive/2006/10/08/Gotcha_...

    Gotcha: Fixing Error with VS 2005 SP1 Beta and older Web Application Project Templates

    Two weeks ago we released the VS 2005 SP1 Beta.  You can download it for free by visiting and registering on the Microsoft Connect Site

    This release contains several hundred fixes for customer reported bugs with VS 2005.  It also includes built-in support for the VS 2005 Web Application Project, which we previously released earlier this year as a standalone download (SP1 adds a few additional features to it, fixes a few bug, and has some additional performance tunings for it).  Before installing VS 2005 SP1 (both the Beta and final release), you'll need to uninstall any previous installs of the VS 2005 Web Application Project standalone download that you have on your machine. 

    One bug that we've seen some developers run into is an issue where - if they have installed an older build of the VS 2005 Web Application Project on their machine before - they get the below error dialog message when they try to add new items to a Web Application project immediately after installing the new SP1 Beta Support for it:

    Error: this template attempted to load an untrusted component 'Microsoft.VisualStudio.Web.Application, Version=8.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'. For more information on this problem and how to enable this template, please see documentation on Customizing Project Templates.

    This is caused by a bug with the SP1 Beta setup.  You can fix this with the SP1 Beta by closing VS 2005, and then open up the "Visual Studio 2005 Command Prompt" (there is an icon for it in your Start Menu->Visual Studio 2005->Visual Studio Tools folder), and run this command from within it:

    devenv.exe /InstallVSTemplates

    This will correctly re-configure the add item templates and make everything work fine. 

    Hope this helps,

    Scott

    P.S. The setup issue will be fixed for the final SP1 release - so the steps above are only needed with the Beta. 

    WindowsDevCenter.com: Creating Visual Studio Project Templates
    www.windowsdevcenter.com/lpt/a/6761

    Creating Visual Studio Project Templates

    by Ron Petrusha
    10/17/2006

    Often, developers perform the same repetitive operations when creating a new Visual Studio project. They frequently find themselves adding the same references and sometimes the same project items to their projects. Sometimes, developers even add more or less identical "framework" code (that is, code that performs essential operations, such as instantiating top-level objects found in an application object model). When these repetitive operations involve adding references or otherwise empty project items, they are just mildly annoying and inefficient. When they require adding repetitive code, they often become irritating and, depending on how the repetitive code is generated, error-prone as well. However, Visual Studio 2005 offers a solution to the inefficiencies of creating largely identical projects: custom project and item templates, supplemented by wizards, can automate the process of project creation and eliminate the need for developers to add the same references, the same project items, or even largely identical code to new projects.

    Note: This article applies to Visual Studio project templates and wizards for Visual Studio 2005 only. In particular, it introduces a new object model for wizard development that is completely incompatible with previous versions of Visual Studio .NET.

    Inside Visual Studio Project Templates

    The starting point for creating a Visual Studio project is to select the type of project to be created from the New Project dialog, as shown in Figure 1. Each project that the user can create is represented by a Visual Studio template, which in turn corresponds to a Zip file stored in a predetermined location. Visual Studio recognizes two different kinds of templates:


    Figure 1: The Visual Studio New Project dialog.

    • Standard templates, which are installed along with Visual Studio. The root directory of standard project templates is defined by the ProjectTemplatesLocation property defined in the Visual Studio settings (.vssettings) file, while the root directory of standard item templates is defined by the ProjectItemTemplatesLocation property in the Visual Studio settings file.
    • User templates, which are custom templates. The root directory of custom project templates is defined by the UserProjectTemplatesLocation value in the HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\8.0 registry key, while the root directory of custom item templates is defined by the UserItemTemplatesLocation value in the same key.

    If we open the Zip file that defines a particular Visual Studio project template, we see that it includes a number of files that will eventually be added to a newly created project. For example, in Figure 2, WinZip is displaying the files contained within the Visual Basic Console Application project template, ConsoleApplication.zip. Of the 10 files contained in the Zip file, each corresponds to a file included in a new console mode application and accessible from the Visual Studio Solution Explorer, with just two exceptions:

    • consoleapplication.vbproj, the Visual Basic project file. Although it is not displayed in Solution Explorer, it is a standard .NET project file whose values are reflected on the Application tab of the project's Properties dialog.
    • consoleapplication.vstemplate, a Visual Studio template file.


    Figure 2: The files contained in the ConsoleApplication.zip project template file. (Click for full-size image.)

    Since it is the template file that distinguishes this collection of files as a Visual Studio project template, it is instructive to examine its contents. As Figure 3 shows, consoleapplication.vstemplate turns out to an XML file that consists of two sections, TemplateData (which provides information about how the template is to be handled) and TemplateContent (which defines the individual items to be included in the project).


    Figure 3: The contents of a .vstemplate file. (Click for full-size image.)

    In the TemplateData section, the ProjectType tag defines the project language. The DefaultName and ProvideDefaultName tags determine whether Visual Studio provides a root project name (such as ConsoleApplication followed by a number), which it increments depending on the number of projects found in a particular solution directory to form a unique project name.

    In the TemplateContent section, each ProjectItem tag defines a file to be included in a new project. The TargetFileName attribute determines the location of the project item if it is not placed in the project directory. Most interesting is the ReplaceParameters attribute, which suggests that the project item files stored in the template have replaceable parameters. In the Console Application template, only some items, including Module1.vb, AssemblyInfo.vb, and the three Designer files, have replaceable parameters. We can get some sense of what these replaceable parameters are by looking at the files whose ProjectItem tag has a ReplaceParameters attribute set to true. For example, Figure 4 shows Module1.vb, the file used to store developer code in a console application. Its replaceable parameter, $safeitemname$, is easy to pick out, since it is delimited by dollar signs. Table 1 contains a complete list of replaceable parameters used in the Visual Basic console application project template, including those found in the consoleapplication.vbproj file.


    Figure 4: A project item with a replaceable parameter.

    File Replaceable Parameter
    Module1.vb $safeitemname$
    AssemblyInfo.vb $projectname$
    AssemblyInfo.vb $registeredorganization$
    AssemblyInfo.vb $year$
    AssemblyInfo.vb $guid1$
    MyApplication.Designer.vb $clrversion$
    Resources.Designer.vb $clrversion$
    Resources.Designer.vb $safeprojectname$
    Settings.Designer.vb $clrversion$
    Settings.Designer.vb $safeprojectname$
    consoleapplication.vbproj $safeprojectname$

    Table 1: A list of replaceable parameters in the Visual Basic console mode application template.

    To get a better sense of how the contents of the Visual Studio project template relate to a new project, it's also worthwhile to take a look at the project file stored in a template. Example 1 shows it in full. In addition to defining build and debug settings and determining the path of the output assembly, the project file is also responsible for defining the following:

    
    http://schemas.microsoft.com/developer/msbuild/2003">
    
      
        Debug
        AnyCPU
        
        
        
        
        {00000000-0000-0000-0000-000000000000}
        Exe
        $safeprojectname$.Module1
        $safeprojectname$
        $safeprojectname$
        Console
      
        
      
        true
        full
        true
        true
        bin\Debug\
        $safeprojectname$.xml
        42016,41999,42017,42018,42019,42032,42036,42020,42021,42022
      
        
      
        pdbonly
        false
        true
        true
        bin\Release\
        $safeprojectname$.xml
        42016,41999,42017,42018,42019,42032,42036,42020,42021,42022
      
        
      
        
        
        
        
      
    
        
      
        
        
        
        
        
        
      
        
      
        
        
        
          True
          Application.myapp
        
        
          True
          True
          Resources.resx
        
        
          True
          Settings.settings
          True
        
      
        
      
        
          VbMyResourcesResXFileCodeGenerator
          Resources.Designer.vb
          My.Resources
          Designer
        
      
        
      
        
          MyApplicationCodeGenerator
          Application.Designer.vb
        
        
          SettingsSingleFileGenerator
          My
          Settings.Designer.vb
        
      
        
      
        
      
    
     

    Example 1: The project file in the Visual Basic console application template

    • The references automatically added to a project when it is created.
    • The namespaces that are automatically imported by the compiler. They are available on a project-wide basis without your having to use the Visual Basic Imports statement or the C# using statement.
    • The files to be included in the compilation. Note that "child" files, such as designers that are dependent on "parent" files, are marked with a DependentUpon tag.
    • Any resources to be included in the compilation.

    A Visual Studio project template along with its project file allows you to define the source code files (which of course can contain custom code) to be included in a project, replace parameter strings with their values, define the assemblies references by the project, and indicate which namespaces the compiler imports on a project-wide basis.

    Modifying or Creating a Visual Studio Project Template

    One of two approaches can be used to modify a Visual Studio project template:

    • You can extract the files from an existing template that it similar to the template you'd like to create, make whatever modifications you'd like (either using a text editor or by accessing the files as an existing project from the Visual Studio environment), and store the new files either in the same Zip file or a new Zip file.
    • You can create a new project, make whatever modifications you'd like, and use the Export Template option on the Visual Studio File menu to walk you through the process of generating the template.

    Although the second is the easier option, it suffers from a major limitation: If you create the project that you plan to export from a template, all replaceable string parameters will be replaced by their values, and the value of your template will be substantially reduced. This makes the first alternative the most attractive. However, you can combine these approaches without losing a project template's replaceable string parameters. To do this, follow these steps:

    1. Create a directory in which to store your project. If you store multiple projects in a single solutions directory, also create a solutions directory as the parent of the project directory.
    2. Locate the Zip file containing the Visual Studio template that most closely resembles the template you plan to create.
    3. Extract the contents of the Zip file to the project directory you've just created. If the Zip file does not do it for you, you should create the directory structure that would be created if you created the project from a template in Visual Studio. For example, in Visual C#, the AssemblyInfo.cs file is placed in the Properties subdirectory of the project directory; in Visual Basic, AssemblyInfo.vb is placed in the My Project subdirectory of the project directory.
    4. Delete the template (*.vstemplate) file.
    5. Review the files listed in the project (*.vbproj or *.csproj) file. All of the files listed must be present for Visual Studio to successfully generate a template. Modify the contents of the project file accordingly. This is also a good opportunity to rename files. The project file itself is a particularly good candidate for renaming.
    6. In Visual Studio, select the Open Project option from the File menu, and navigate to the project file that you'd like to open.
    7. Make whatever changes you desire to create the new template.
    8. Use the Export Template option from the File menu to generate the new template.
    9. Since this approach does not create a new project from an existing template, it preserves replaceable parameter strings in the project's files. This does, however, have one disadvantage: because the Visual Basic and C# compilers will not recognize the parameter strings as valid language elements, the project cannot be compiled. Instead, you'll have to use your template to create a project, which you can then compile and debug.

    Visual Studio and the Templates Cache

    Once a Visual Studio project session starts, it stores its templates in a cache. This means that any changes you make to a template will not be reflected until Visual Studio is closed and restarted. This is an important detail to keep in mind: you can waste hours making changes to a template, only to find that they do not appear to be reflected in new project or project items generated from that template.

    Some Template Customizations

    You can use this technique to generate templates that you've customized by adding new references, importing new namespaces, and using replaceable string parameters. In this section, we'll examine how to perform these three kinds of customizations before generating a new Visual Studio project template.

    Adding References

    To generate a template that includes additional references to .NET assemblies, right click on the Visual Basic or C# project in the Solution Explorer and select the Add Reference option from the context menu. (In Visual Basic, you can also open the project's Properties dialog and navigate to its References pane.) You can then select the references that you'd like to add to the project.

    When you save your project and then export it as a template, the references you've added are present in any projects are created from that template. Both Visual Basic and C# projects behave identically in this regard.

    Importing Namespaces in Visual Basic

    Another of the repetitive operations that developers have to perform in each project is importing the namespaces whose types will be referenced by the project's code. Most commonly, developers find themselves importing the same namespaces into their projects, usually by adding an Imports statement (in Visual Basic) or the using statement (in C#) to the beginning of each source code file. (The statement applies only to the source code file in which it is placed, rather than to a project as a whole.)

    Visual Basic provides direct support for importing namespaces into a project, rather than importing them on a file by file basis. To import a namespace for the project as a whole, open the project's Properties dialog and select the References tab. The Imported Namespaces list box, which is shown in Figure 5, allows you to see which namespaces are globally imported for your project, as well as to import additional namespaces found in the assemblies references by your project.


    Figure 5: The Imported Namespaces list box

    Project-wide imports are reflected in Import tags in the projects .vbproj file. For example, the namespaces imported in Figure 5 result in the following entries in the project's .vbproj file:

    <ItemGroup>
       <Import Include="Microsoft.VisualBasic" />
       <Import Include="System" />
       <Import Include="System.Collections" />
       <Import Include="System.Collections.Generic" />
       <Import Include="System.Data" />
       <Import Include="System.Diagnostics" />
    </ItemGroup>

    Unlike Visual Basic, C# does not recognize the Import tag, and therefore does not allow you to import namespaces on a project-wide basis. However, you can modify the individual source code (.cs) files in your template to reflect the namespaces that you most commonly import.

    Using Replaceable String Parameters

    In addition to adding references to .NET assemblies and to importing namespaces, you can also add replaceable string parameters within project templates. Visual Studio automatically recognizes the following replaceable parameters:

    Parameter Name Description
    $guid1$ The GUID that identifies the project if it is exposed to COM.
    $guid2$ A second project-defined GUID.
    $guid3$ A third project-defined GUID.
    $guid4$ A fourth project -defined GUID.
    $guid5$ A fifth project -defined GUID.
    $guid6$ A sixth project -defined GUID.
    $guid7$ A seventh project -defined GUID.
    $guid8$ An eighth project -defined GUID.
    $guid9$ A ninth project -defined GUID.
    $guid10$ A tenth project -defined GUID.
    $time$ The current time.
    $year$ The current year.
    $username$ The name of the authorized user who is creating the project.
    $userdomain$ The domain of the authorized user who is creating the project.
    $machinename$ The name of the computer on which the project is crated.
    $clrversion$ The version of the .NET runtime under which the project will run.
    $registeredorganization$ The organization for which the project is being created.
    $runsilent$ A flag indicating whether the template should display any user interface elements.
    $projectname$ The name of the project. This is the name that the user has entered into the Name textbox when creating the project.
    $safeprojectname$ The name of the project with any unsafe characters removed.
    $installpath$ The path to the directory containing the project template used to generate the project.
    $exclusiveproject$ Determines whether the current solution is closed and a new one opened before the new project is created. If True, the solution is closed; if False, the new project is added to an existing solution.
    $destinationdirectory$ The path to the project directory.

    Table 2.

    For example, you may find that, when creating console mode projects, you always change the name of the class or module in your source code to match that of your project. By making the following simple modification to the Console Application template, you can have Visual Studio make this change for you when it generates the project's files:

    ' Modification to Module1.vb in Visual Basic projects
    Module $safeprojectname$
    
    // Modification to Program.cs in C# projects
       class $safeprojectname$

    In addition to making use of existing replaceable strings in your custom templates, you can also add your own custom replaceable strings. These are defined by the <CustomParameters> node in the Visual Studio template (.vstemplate) file and take the following form:

    <CustomParameters>
       <CustomParameter Name="$name$" Value="value" />
    </CustomParameters>

    To add custom replaceable string parameters to a project, you first have to generate the Visual Studio project or item template, extract and edit its .vstemplate file. The <CustomParameters> node should be inserted just before the closing </TemplateContent> tag. Then replace the old version of .vstemplate file in the template Zip file with the new one.

    Custom string parameters are most useful when a Visual Studio project or item template is linked with a wizard. Even without custom templates, however, they can be useful. For example, a single project template might be used to target different versions of a particular object model. To take a very simple example, version 1.0 of a small object model is contained in a class named CompanySDK1 in an assembly of the same name. Version 2.0 of the object model is contained in a class named CompanySDK2, also in an assembly of the same name. CompanySDK1 includes a DisplaySDKVersion method, which is inherited by CompanySDK2, that displays the version of the SDK used by the application. A single template can be developed to handle both versions. For example, the code contained in a template that instantiates the versioned application object and calls its DisplaySDKVersion method might appear as follows:

    Imports CompanySDK
    
    Module Module1]
       Sub Main()
          Dim versionedSDK As New $versionedClass$
          versionedSDK.DisplaySDKVersion ()
       End Sub
    End Module

    Two templates that differ only in their single custom parameter can then be generated from this project. The template supporting version 1.0 of the SDK has the following <CustomParameters> node:

    <CustomParameters>
       <CustomParameter Name="$versionedClass$" Value="CompanySDK1" />
    </CustomParameters>

    The template supporting version 2.0 of the SDK has the following <CustomParameters> node:

    <CustomParameters>
       <CustomParameter Name="$versionedClass$" Value="CompanySDK2" />
    </CustomParameters>

    Ron Petrusha is the author and coauthor of many books, including "VBScript in a Nutshell."

    Migrating a VS 2003 Web Project to VS 2005
    webproject.scottgu.com/VisualBasic/Migration/Migra...

    Upgrading VS 2003 Web Projects to be VS 2005 Web Application Projects

    The below tutorial helps explain how you can now use the VS 2005 Web Application Project to more easily migrate existing web projects in VS 2003 to VS 2005.

    Migrating from VS 2003 to VS 2005 using the Web Application Project

    There are a couple of different strategies you can take when migrating a VS 2003 Web Project to VS 2005. One option (which is the default with the shipping VS 2005 bits) is to migrate the project to use the new VS 2005 Web Site project model. For more information click here

    An alternative (and now much easier) approach, is to migrate a VS 2003 Web Application Project to use the VS 2005 Web Application Project option instead. The VS 2005 Web Application Project model uses the same conceptual approach as VS 2003 (project file to include/exclude files, and compilation to a single assembly, etc), and so doesn't require any architectural changes (the only things that might impact you are deprecated API warnings -- which can optionally be ignored -- and/or language changes).

    Once VS 2005 Web Application Project RC1 is installed, the default migration wizard in VS 2005 is to migrate VS 2003 Web Projects to VS 2005 Web Application Projects. This model is much less invasive and requires fewer changes.

    Please follow the below steps in exact order. If you have problems using the below steps, please post in this forum and VS team members will be able to help.

    Step 0: Install the VS 2005 Web Application Project Preview

    VS 2005 Web Application Project support is not built-into the shipping VS 2005 (although it will be included with the SP1 release). So the first step (if you haven't done it already) is to install it.

    You can learn more about it and install the release candidate build from here. After installing it, please spend a few minutes following the tutorials on this site to test it out and learn the basics of how it works.

    Step 1: Backup Your VS 2003 Projects

    Please make sure to save a backup of your VS 2003 Web Projects and Solutions before attempting any of the below steps. There is the chance that you might need to roll-back to the VS 2003 solution if you run into issues, or start over from any step.

    Step 2: Copy Remote Frontpage Projects to your local machine

    If you use Frontpage Server Extensions to access your project on a remote server, you will need to copy it locally before migration (if your web projects run locally then you can skip this step). VS 2005 Web Application Projects do not support accessing projects remotly via the Frontpage Server Extensions. If you do not copy the project locally first, migration will convert the project to a VS 2005 Web Site project instead.

    To copy the remote site locally use the VS 2003 Copy Project menu command under the project menu. This will lauch the copy project dialog.

    Change the destination to "http://localhost/ProjectName" and be sure to select "All project files".

    After the project has been copied locally you should remove the remote one from the solution and use "Add Existing Project" navigating to the c:\inetpub\wwwroot\VS03Web folder where your project was copied.

    Make sure the project still builds in the IDE.

    Step 3: Open Your VS 2003 Web Project and Make Sure it Compiles Clean

    Before attempting to migrate anything, please make sure that you open up your existing VS 2003 solution and perform a clean-re-compile of the entire solution -- and then run the application. Spending 2 minutes to verify everything is working before you migrate can save a lot of grief later (especially if the cause is because of a last-minute directory move, etc).

    Step 4: Temporarily remove the VS 2003 Solution from Source Control

    If your project is currently under source control, you should temporarily remove/unbind it from source control prior to converting it. Once converted, you can then add it back under source control.

    Step 5: Open the solution or project in VS 2005 and perform a project migration

    Close the solution in VS 2003, and start VS 2005. Choose "File->Open Project" from the File menu, and select the .sln or .vbproj file for the solution you wish to migrate. This will cause the VS 2005 Migration Wizard dialog to launch:

    Choose a location to backup the project:

    The conversion wizard will then perform a few steps:

    1) Update the project file to be a VS 2005 MSBuild based project file

    2) Add an xhtmlcompliance section within your web.config file to enable "legacy" html rendering

    3) Update your web project to go directly against IIS instead of use FrontPage Server Extensions

    4) Fixup any language changes between .NET 1.1 and .NET 2.0

    When complete, the solution will open up within VS 2005:

    Step 6: Compiling and Running your Web Project

    You are now ready to compile your web project. Choose "Build->Build Solution" or Ctrl-Shift-B to do this.

    The most common issue I've seen people run into at this stage are compile conflicts with new class names introduced with V2.0. For example: ASP.NET now has a built-in menu and treview control (as well as built-in profile, membership and rolemanagent APIs) -- so if you declare these types in your VS 2003 web projects today you may get an "ambiguous type" compiler error when you compile (note: the compiler lists each line and typename for you). If this happens, you should update your declarations to fully qualify the TreeView/Menu/Etc. you want to use (for example: change "TreeView" to "MyCompany.MyControls.TreeView").

    You may or may not also run into some API changes when moving to V2. This site lists the known set of breaking API or functionality changes that were made between V1.1 and V2.0. You should consult this if you run into a problem.

    Once your project is compiling clean, you should be able to run and debug in on IIS using ASP.NET 2.0.

    Step 7: Run the Web Application

    Run the application to verify that the application is working fine. Fix up any runtime issues that you find. This site lists the known set of breaking API or functionality changes that were made between V1.1 and V2.0, and will be able to help if you run into any issues.

    Step 8: Convert to Partial Classes

    When you migrate your project using the above steps, none of your code-behind page code or classes are modified. This means that the code should look (and work) just like it did in VS 2003. This makes it much easier to migrate existing code to VS 2005.

    You can optionally choose to keep your code in this format. Doing so will require you to manually update the control field declarations within your code-behind file -- but everything else will work just fine in VS 2005.

    Alternatively, you can update your pages/controls to use the new partial-class support in VS 2005 to more cleanly organize the tool-generated code from your code-behind logic. This is done by separating out your current code-behind files into two separate files -- one for your code and event handlers, and the other a .designer.vb file that contains the control field declarations for the code-behind. Please review this tutorial for more details on how this new code-behind model works.

    To migrate your code to use this new model you should follow these two steps:

    1) Make sure your web project is compiling clean without errors. You want to make sure that all code is compiling clean without problems before attempting to update it to use partial classes. Please test this before proceeding.

    2) Right click on the root web project node within the solution explorer and choose the "Convert to Web Application" menu item. This will then iterate through each page or user control within your project and migrate all control declarations to be declared within the .designer.vb file, and event handlers to be declaratively specified on the controls in the .aspx markup.

    You should then do a clean re-build of your web solution. This should hopefully compile clean without errors (the cases where I've seen errors are situations where you've done custom modification of the control declarations within your code-behind class, and the upgrade wizard mishandles them). If you have any errors in your task list, you can navigate to the appropriate page in the solution explorer and choose the "View Code" and "View CodeGen" context menu items to examine the code and fix any issues.

    If for some reason the .designer.vb file doesn't have a control declaration added, you can manually declare it within the code-behind file of the page (just like you would in VS 2003). One issue we've sometimes seen reported are cases where a developer has specifically overriden the type of a Usercontrol declaration in a VS 2003 code-behind file (for example: MyControl1 instead of the generic UserControl base class), and the type isn't correctly transferred to the .designer.vb file (producing a compile error). If the correct user-control type declaration isn't added to the .designer.vb file, you can optionally just delete it from the .designer.vb and add it the code-behind file of the page instead. VS 2005 will then avoid re-adding it to the .designer.vb file (it first looks in the code-behind file for declarations before updating the .designer.vb file on changes).

    Note: as an advanced option, you can also upgrade each page on a page-by-page basis (just right-click on the page and choose the "Convert to Web Application" option on it). You might consider doing this if you want to watch closely the changes made on each page.

    Visual Studio 2005: Create Reusable Project And Item Templates For Your Development Team -- MSDN Mag
    msdn.microsoft.com/msdnmag/issues/06/01/CodeTempla...

    Create Reusable Project And Item Templates For Your Development Team


    Matt Milner



    This article discusses:
    • Consuming existing templates
    • Creating new project and item templates
    • Customizing templates through XML metadata
    • Extending the new project wizard
    This article uses the following technologies:
    Visual Studio 2005, XML


    Code download available at:
    CodeTemplates.exe (139KB)

    O
    ver my years of working with clients on building Microsoft® .NET Framework-based applications, I have often heard a common feature request for Visual Studio® .NET: "It would be great if we could create our own project types and item types so that all developers in our organization would have access to them." Many orgs want to create shared templates for projects such as Web sites or for project items, such as a default Web page or forms. Visual Studio 2005 introduces a new model for defining templates and starter kits for projects and items that make this not only possible, but relatively simple. I'll take a look at how to consume, create, and customize these templates.

    In the past, users have been able to create project or item templates, but it took a lot of inside knowledge of arcane text file formats, JavaScript files, and folder naming conventions, as well as a little bit of magic, to make it work. With the new model, a user can create a simple ZIP file of the items to be included in the template and an XML metadata file that describes the template and its contents. This new model supports creating item templates such as a Web page, a C# or Visual Basic® code file, or a configuration file. It also supports single-project templates like Web sites, class libraries, or smart client applications, as well as multiproject solutions. In fact, most of the built-in project and item templates that you use to create new solutions in Visual Studio 2005 are based on this same template mechanism.


    Consuming an Existing Template

    The easiest way to understand how these templates work is to first take a look at how to consume a template that has already been created. In Visual Studio 2005, you can configure the locations to search for templates, and these locations can use any Universal Naming Convention (UNC) path. In the Options dialog box, under the Projects and Solutions settings node, you can configure the user-specific locations to look for project or item templates (see Figure 1). The default values for these settings point to template folders in your My Documents folder. The templates included with Visual Studio 2005 are located under the Visual Studio installation directory and are included in the project setup dialog boxes by default.


    Figure 1 Template Location Options

    By dropping a ZIP file with the appropriate metadata file in the user-configured template location, the new project or item template will appear in the respective dialog boxes. The sample code included with this article includes sample project item and project templates that can be copied into these directories for testing purposes. After adding the ZIP file, start Visual Studio 2005, choose File | New | Project, and notice that the new project template is included under the My Templates section of the dialog box (see Figure 2). Selecting this project template will create a new project with the items included in the ZIP file.


    Figure 2 Your Templates Appear in the New Project Dialog Box

    There are several benefits gained by being able to consume templates in this way. First, because the user locations are configurable, development groups can define a file share on a common server where templates are deployed for the group. All developers in the group can configure their environment to look to that file share for templates. When a new template is deployed to the server by copying the ZIP file to the share, all developers immediately have access to it from within Visual Studio. After the initial deployment, changes to the template can be made in one place instead of having to be deployed to each developer's workstation.

    In addition, because the template is used as a true template, it remains unchanged and can be used repeatedly to create new projects. This is a vast improvement over the old "copy and paste" approach where one developer would create a file or project and hand it off to another. Without careful management, the original was quickly lost and modified with no chance to get back to a known good configuration. Because the project and item template locations are configured independently, an organization can choose to have only project templates centralized and allow developers to create their own item templates.

    In addition to templates, Visual Studio 2005 includes a starter kit concept. Starter kits enable broad consumption of samples through community sites from which templates can be downloaded. From the New Project dialog box, one of the options is Search Online Templates. Choosing this option allows you to search for starter kits online by keyword. When a starter kit is found, you can choose to download and install it into the appropriate directory so that you can then use it as a template for adding items or projects. As the name implies, starter kits are generally used as learning tools or samples to help convey knowledge or provide a starting point for larger projects.


    Creating Templates

    Consuming templates is a great start, but most developers will want to know how to create their own templates. Visual Studio 2005 makes it extremely easy to create your own templates right in the IDE. Once they are created, you have the ability to extend and change them.

    The simplest type of template to create is an item template. To do so, simply open a project that includes the file you want to use as a template and then chooses File | Export Template to run the Export Template Wizard shown in Figure 3.


    Figure 3 Exporting an Item Template

    The wizard will prompt you to indicate the item to export and any assembly references to include. Adding references allows you to ensure that assemblies your template depends on are included in the project when the item is added. For example, if an item template uses the System.Data.SqlXml assembly to access SQL Server XML functionality, it is important to make sure this assembly gets referenced by the project when the template is added. The references available to choose from in the wizard include the default assembly references for the current project type as well as any references that have been added to the current project before running the wizard.

    In addition to the assembly references, the export wizard is intelligent enough to export resource files required by the item be exported. For example, if an ASP.NET Web page is the item being exported, the C# or Visual Basic codebehind file and resource file for that page will also get exported and included when the template is used to create new items. The wizard also edits these files to add placeholders for class names and namespaces so that when they are added to a new project they can be named differently and show up in the correct code namespace. For example, instead of having a class that's always named BusinessObjectClass when you add your template, the user can specify a name for the new code file, and this will be used as the name for the class in your template.

    When the parameters have been selected for the item template—including a name, a description, and the icon to use when displaying the template—you have the option of installing the template. When exporting the template, it gets created in your My Documents\Visual Studio 2005\My Exported Templates folder. Templates in this folder are not included in the New Item or New Project dialog boxes by default. In order to make the template available for use, it must be installed by copying it to the configured directory for templates. Checking the box to install the template simply means that the ZIP file will get copied to the folder you configured for templates in addition to the exported templates folder.

    Not installing the template allows you to edit the metadata directly or add other files to the ZIP file before installing the template into the appropriate folder. For example, if you created a Master Page template for use in Web sites, you might want to include a CSS file that includes the styles used on the Master Page to ensure that the styles used are also included when the Master Page template is selected. In this case, you would not want to install the template at this point until you have had a chance to add the stylesheet file to the ZIP file and update the metadata file to include the stylesheet when installing the template. I will look at the steps required to customize a template like this later in the article.

    Creating a project template works in much the same way with the exception that assembly references are not needed as they are inferred from the current project configuration. The main difference between the project template and an item template is that a project template provides the means to create a new project, whereas the item template can only be added to an existing project. Both template types, however, allow for multiple items to be added to a project when the wizard is run.


    Customizing Templates

    As you can see, creating a project or item template has been made extremely simple in Visual Studio 2005. However, like many wizards, the default template created may not be exactly what you want. Fortunately, because the design of the templates involves a ZIP file and a metadata file, it is not difficult to extend a template with new features once it's created.

    The template metadata file is an XML file that describes the template and indicates the files and resources to be included in with the project or item. A sample metadata file created by using the wizard to export an item template is shown in Figure 4.

    The first thing to notice is the general structure of this metadata, which includes a root VSTemplate node and two child nodes: TemplateData and TemplateContent. The VSTemplate node simply acts as a wrapper for the other two items and indicates the type of the template. Because of the XML namespace declaration in the file, any developer editing this file in Visual Studio 2005 will automatically get IntelliSense® support for the items that are allowed, based on the schema for template files.

    The TemplateData element contains information that is used to provide details about the template in the New Project or New Item dialog box. The name, description, and icon elements are all displayed directly in the dialog box as you would expect. The DefaultName element is used as the root of the default name provided for the new item or project. The ProjectType element indicates the type of project that the item can be included in and affects the location in the dialog box. The example shown in Figure 4 will cause this item template to be available only for C# Web projects.

    Figure 5 shows some other optional elements that can be used in the TemplateData section of the metadata file, as well as options for the items already mentioned.

    The TemplateContent element is where you define the files and references for your item template. The TemplateContent element references any assemblies to be included with the item or project and generally will not need to be edited since you can specify these in the export wizard.

    Each item in the ZIP file that is to be included in the project is called out with a ProjectItem element. In addition to identifying the source files, the attributes of the ProjectItem element allow you to indicate whether an item has a subtype, whether parameters in the file should be replaced, and whether the target file name differs from the source file name. The subtype attribute can include values such as Form or Component, which provide Visual Studio with hints about how to display the item in the correct designer when it is opened.

    Replacing parameters allows the New Item wizard to create namespaces, class names, and similar items dynamically so that the template is not entirely static. Without specifying true for this attribute, the template files will appear in the new project exactly as they are in the ZIP file.

    The TargetFileName attribute allows you to specify either a constant or parameterized value for the file name as it should appear in the new project. This allows you to reuse a template file to create multiple instances of the item in the same project. For example, a class file could be added to the same project twice with different TargetFileName attributes. When the item is added, two new classes will be added to the project based on the same template file. Allowing for this type of reuse within the template makes it much easier to manage the files involved in creating a template.

    When creating project templates, the process is very similar to that used for item templates, but there are a few slight differences in the metadata file. First, because this is a project, all ProjectItem elements are included under a Project element, which indicates the project file name and whether parameters should be replaced when naming the project file. An abbreviated project template file is shown in Figure 6.

    Notice that in addition to the Project element that identifies the project information, this example also uses the Folder element to indicate that the ZIP file contains a folder containing the project items indicated. The folder will be included in the project structure when the template is used. The project template also provides different options for the TemplateData section to influence the user interaction with the New Project dialog box. These items are shown in Figure 7.

    Web projects are configured in a slightly different manner as they use a different wizard for project creation. Instead of using a language for the project type such as Visual Basic and then a ProjectSubType such as Windows, a Web project uses Web as the project type, and the language as the ProjectSubType.


    Templates for Multiple Projects

    In addition to being able to configure a template for a single project, it is often useful to create an entire solution as a template or starting point. For example, a development group might not simply want a single Web project as the starting point for a new Web solution as they may want to include business object and data access projects. One resolution would be to create a template for each project and allow the developer to add each project they needed individually. However, a much simpler approach, and one that provides more consistency, is to create a template for the entire solution and give a developer all of the projects they need to get started.

    Multi-project templates still involve only one ZIP file, but they include one master metadata file and an individual metadata file for each project in the solution. The project metadata files are no different than what has been discussed so far, but the solution template file now provides the template data to the wizard through the TemplateData element and its children and provides pointers to the other project metadata files through the TemplateContents element and its children. The abbreviated sample in Figure 8 shows a solution metadata file that includes three projects: a Web site, a business logic layer, and a data access layer.


    Figure 9 Solution Folder

    Notice that the Type for the template is a ProjectGroup and that the template content is simply a collection of links to the other project templates. A friendly name can be given to the projects, and this is how they will be named in the Solution Explorer. The paths to the other projects are based on the folder structure of the items in the ZIP file. The ZIP file used with the example template metadata in Figure 8 would look something like Figure 9. Each project in the solution has its own folder and its own vstemplate metadata file, and the links in the example provide relative paths to the project template files based on the folder structure in the ZIP file.


    Providing Guidance

    In addition to providing templates, it is often helpful to provide guidance or extra information about how to use the template. One way to do this is to provide well-commented template code files, but this requires a user to dig through your template code to understand how to use it. Visual Studio 2005 templates provide a better mechanism for not only delivering the documentation your users will need, but for making sure they are presented with the documentation after adding an item or project from your template.

    In both project and item templates, the ProjectItem element can have several attributes applied to it that allow for displaying items to the user. The OpenInEditor Boolean attribute allows a project item to be opened in the default editor based on the file type of that item. For example, an XML file included in your project and marked with OpenInEditor set to true will be opened in the XML editor. The OpenInWebBrowser attribute allows a text or HTML file to be opened in a Web browser window. This is an easy way to distribute help or guidance with your templates. Simply create an HTML file providing your guidance and include it in the template. Users will view your help file whenever they use your template so you can be sure they have the information they need to properly use your template files once installed.

    In addition to having files open in the browser or editor windows, you can control the order in which the items appear when you have multiple items opening. You should use the OpenOrder attribute on the ProjectItem element in order to control the order of the displayed items in Visual Studio. The lower the number supplied for OpenOrder, the higher the priority and the closer to the front the item will be displayed.


    Parameter Replacement

    In many of the examples so far, parameter replacement has been used to allow for the dynamic creation of item and project names and namespaces. Parameter replacement involves using placeholders in the metadata file or in the included source files to indicate where a certain parameter value should be inserted. When you create a new item or project from a template, Visual Studio inserts actual values for the placeholders. There are several built-in parameters that can be used; it is also possible to add your own parameters to be passed to the wizard for replacement.

    For parameter replacement to occur, the ReplaceParameters attribute must be set to true on either the Project element or the ProjectItem element. These are the two main locations where parameter replacement is used. When the wizard processes the project files and the included items, for each item where ReplaceParameters is set to true, the wizard searches through a dictionary of values to see if it has any known keys that match the parameters in the file. Parameters with a match are replaced with the stored values. Figure 10 shows the built-in parameters that are available to be used in metadata files and source files.

    Custom parameters can also be added to the template and are then available in the item templates and metadata file as well. To add custom parameters, you add a CustomParameters section within the TemplateContent element of the metadata file. For each parameter you want to add, you can specify a CustomParameter element with a name and the value (see Figure 11). Name your values using the $parametername$ syntax in this file in order to use the same naming convention in the source files.


    Extending the Wizard

    If all the extensibility and parameter replacement is not enough to meet your needs and you really have to write some code to provide the dynamic data you need for your template, you can write a wizard extension and configure it in the template metadata. An example of when you might need this type of extension is a case where you have several items in a template and need to be able to include or exclude some at run time. With a wizard extension, you can augment the standard New Project or New Item wizard with your own code and make decisions about what items to include or exclude as well as adding dynamic custom parameters for replacement in the project files.

    In order to create your own wizard extension, you need to create a .NET library project in your managed language of choice and add a reference to the Microsoft.VisualStudio.TemplateWizardInterface.dll and EnvDTE.dll assemblies. Create a class in your library project and implement the Microsoft.VisualStudio.TemplateWizardInterface.IWizard interface. The IWizard interface members are listed in the following code:

    public interface IWizard
    {
        void BeforeOpeningFile(EnvDTE.ProjectItem projectItem) ;
        void ProjectFinishedGenerating(EnvDTE.Project project);    
        void ProjectItemFinishedGenerating(EnvDTE.ProjectItem projectItem);
        void RunFinished();
        void RunStarted(object automationObject, 
            Dictionary<string, string> replacementsDictionary,
            WizardRunKind runKind, 
            object[] customParameters);
        bool ShouldAddProjectItem(string filePath);
    }
    

    The first three methods provide access to the IDE extensibility model for the items that have been added and might be opening. The RunFinished and RunStarted methods provide a means for running initialization and clean-up code. RunStarted is a common method to provide an implementation for and to write logic that sets flags used by the ShouldAddProjectItem method; these flags will determine whether an item from the template should be added to the project.

    In order to configure the extension, you must modify the VSTemplate element in the template metadata file to add a WizardExtension element that defines the assembly information for your custom wizard extension. The abbreviated template file in Figure 12 shows how this configuration works. When the wizard is run, the wizard extension will be loaded and its methods called at the appropriate time to augment the wizard processing.


    Conclusion

    Visual Studio 2005 has greatly improved the template capabilities and the ease with which you can create reusable, dynamic templates to provide robust starting points for projects and project items. The use of a simple ZIP file and an XML metadata file make customizing templates straightforward. In addition, the export features in Visual Studio make starting a template as simple as opening an existing project. The samples available for download provide several simple illustrations of the items discussed in this article put to use. See the Visual Studio Template Schema Reference in the MSDN® Library for additional details about adding customization to your template metafiles.



    Matt Milner is an independent software consultant specializing in Microsoft technologies including .NET, Web services, XML, and BizTalk Server. As an instructor for Pluralsight, Matt teaches courses on Web services, BizTalk Server and ASP.NET. Matt lives in Minnesota with his wife Kristen and his son Max.

    From the January 2006 issue of MSDN Magazine.
    Figure 4 Metadata File for an Item Template
    http://schemas.microsoft.com/developer/vstemplate/2005"
      Type="Item">
        
            __TemplateIcon.ico
            MasterPage.master
            Example Org Master Page
            
                The default master page for Example Org web sites
            
            Web
            CSharp
        
        
            
                
                    System, Version=2.0.0.0, 
                        Culture=neutral, PublicKeyToken=B77A5C561934E089
                    
                
                
                    mscorlib, Version=2.0.0.0, 
                        Culture=neutral, PublicKeyToken=B77A5C561934E089
                    
                
            
            master~1.mas
            master~1.cs
        
    
    

    Figure 5 TemplateData Elements for an Item

    Element NameDescription
    ProjectType Indicates the type of project that this item can be added to. Possible values include CSharp, VisualBasic, VisualC, JSharp, and Web.
    Supports­MasterPageFor items being added to Web projects only, this Boolean option allows you to indicate that the user should be able to select a master page to go along with your item. This is useful for custom Web page item templates that you create.
    Supports­CodeSeparationFor items being added to Web projects only, this Boolean option allows you to indicate that the user should be able to choose if they want their code separated from their presentation. Again, this is most useful for custom Web page item templates that you might create.
    Supports­LanguageDropdown If a template is identical for multiple languages, providing a value of true in this element will allow the user to choose a language for the template. This is only used in Web projects where the template may come from different languages such as the WebForm template. When the language is changed, the metadata file from the new template location is used and the dialog updated. For example, if you switch the language from C# to Visual J# when adding a Web form, the code separation option is disabled because the Visual J# template does not support code separation.

    Figure 6 Metadata for a Project Template
    http://schemas.microsoft.com/developer/vstemplate/2005"
        Type="Project">
        
            ...
        
        
            
                ...
            
            
                Class1.cs
                
                    Class2.cs
                
            
        
    
    

    Figure 7 TemplateData Elements for a Project

    Element NameDescription
    LocationField Options include Hidden, Disabled, or Enabled. This allows you to indicate whether the user should be able to specify a location for their new project. Defaults to Enabled.
    EnableLocationBrowseButtonIndicates if the user should be able to browse the file system for a location to choose for the new project. Defaults to True.
    CreateNewFolderIndicates that the wizard should create a new folder for the project at the location selected. Defaults to True.
    AllowCreationWithoutSaveIndicates if this project template can be an in-memory temporary project. This element provides support for a new feature in Visual Studio 2005 which allows a user to create a new project and work on it, but if the project is not saved, it is discarded. This allows for quick testing or prototyping without having to fill up the My Projects folder with a lot of projects that will never get used.
    ProjectSubType Used by internal templates to further restrict the location within the dialog to certain project subtypes such as Windows, Office, or Database. Used by Web projects to specify the language type.

    Figure 8 Metadata for a Solution
    http://schemas.microsoft.com/developer/vstemplate/2005"
      Type="ProjectGroup">
        
            ...
        
        
            
                
                .\DAL\DAL.vstemplate
                
                
                .\BAL\BAL.vstemplate
                
                
                .\WebSite\WebSite.vstemplate
                
            
        
    
    

    Figure 10 Built-In Replacement Parameters

    ParametersDescription
    itemnameThe user-provided name from the dialog. This value is often used for naming classes and files.
    safeitemnameThe same as itemname but with all unsafe characters removed.
    safeitemrootnameThe name of the root item, which can be used for multi-item templates. For example, if you have partial classes, or a code-beside or code-behind scenario, you can use this item in the secondary files when you need the safeitemname of the main item being created.
    projectnameThe user-provided name of the project for a project template.
    safeprojectnameThe user-provided name of the project for a project template with all unsafe characters removed.
    rootnamespaceThe root namespace of the project for an item template, which can be used to replace the namespace in a project item source file.
    guid[1-10]A GUID that can be used as a unique identifier for uses such as project GUID in the project file. You can specify up to 10 different GUID parameters using the syntax: guidx, where x is a number between 1 and 10.
    timeThe current time in the format DD/MM/YYYY HH:MM:SS.
    yearThe current four-digit year.
    usernameThe Windows username of the logged-in user.
    userdomainThe Windows domain of the logged-in user.
    machinenameThe name of the machine where the template is being used.
    clrversionThe version of the common language runtime being used.
    registered-organizationThe registered organization of the user based on the system settings.
    wizarddataA single string that can be any XML or string data included in the WizardData element in a template metadata file.

    Figure 11 Using the CustomParameter Element
    http://schemas.microsoft.com/developer/vstemplate/2005"
      Type="Item">
        
            ...
        
        
            ...
            
                
            
        
    
    

    Figure 12 Using the WizardExtension Element
    http://schemas.microsoft.com/developer/vstemplate/2005"
      Type="Item">
        
            ...
        
        
            ...
        
        
            MSDNMagazine.TemplateWizardExtension.dll
            
            MSDNMagazine.TemplateWizardExtension.ExampleOrgWebPageExtension
            
        
    
    

    © 2006 Microsoft Corporation and CMP Media, LLC. All rights reserved; reproduction in part or in whole without permission is prohibited.
    Related Articles From MSDN Magazine:
    ASP.NET: 10 Tips for Writing High-Performance Web Applications -- MSDN Magazine, January 2005
    msdn.microsoft.com/msdnmag/issues/05/01/ASPNETPerf...

    10 Tips for Writing High-Performance Web Applications



    This article discusses:
    • Common ASP.NET performance myths
    • Useful performance tips and tricks for ASP.NET
    • Suggestions for working with a database from ASP.NET
    • Caching and background processing with ASP.NET
    This article uses the following technologies:
    ASP.NET, .NET Framework, IIS


    Contents
    Sidebars


    W
    riting a Web application with ASP.NET is unbelievably easy. So easy, many developers don't take the time to structure their applications for great performance. In this article, I'm going to present 10 tips for writing high-performance Web apps. I'm not limiting my comments to ASP.NET applications because they are just one subset of Web applications. This article won't be the definitive guide for performance-tuning Web applications—an entire book could easily be devoted to that. Instead, think of this as a good place to start.

    Before becoming a workaholic, I used to do a lot of rock climbing. Prior to any big climb, I'd review the route in the guidebook and read the recommendations made by people who had visited the site before. But, no matter how good the guidebook, you need actual rock climbing experience before attempting a particularly challenging climb. Similarly, you can only learn how to write high-performance Web applications when you're faced with either fixing performance problems or running a high-throughput site.

    My personal experience comes from having been an infrastructure Program Manager on the ASP.NET team at Microsoft, running and managing www.asp.net, and helping architect Community Server, which is the next version of several well-known ASP.NET applications (ASP.NET Forums, .Text, and nGallery combined into one platform). I'm sure that some of the tips that have helped me will help you as well.

    You should think about the separation of your application into logical tiers. You might have heard of the term 3-tier (or n-tier) physical architecture. These are usually prescribed architecture patterns that physically divide functionality across processes and/or hardware. As the system needs to scale, more hardware can easily be added. There is, however, a performance hit associated with process and machine hopping, thus it should be avoided. So, whenever possible, run the ASP.NET pages and their associated components together in the same application.

    Because of the separation of code and the boundaries between tiers, using Web services or remoting will decrease performance by 20 percent or more.

    The data tier is a bit of a different beast since it is usually better to have dedicated hardware for your database. However, the cost of process hopping to the database is still high, thus performance on the data tier is the first place to look when optimizing your code.

    Before diving in to fix performance problems in your applications, make sure you profile your applications to see exactly where the problems lie. Key performance counters (such as the one that indicates the percentage of time spent performing garbage collections) are also very useful for finding out where applications are spending the majority of their time. Yet the places where time is spent are often quite unintuitive.

    There are two types of performance improvements described in this article: large optimizations, such as using the ASP.NET Cache, and tiny optimizations that repeat themselves. These tiny optimizations are sometimes the most interesting. You make a small change to code that gets called thousands and thousands of times. With a big optimization, you might see overall performance take a large jump. With a small one, you might shave a few milliseconds on a given request, but when compounded across the total requests per day, it can result in an enormous improvement.


    Performance on the Data Tier

    When it comes to performance-tuning an application, there is a single litmus test you can use to prioritize work: does the code access the database? If so, how often? Note that the same test could be applied for code that uses Web services or remoting, too, but I'm not covering those in this article.

    If you have a database request required in a particular code path and you see other areas such as string manipulations that you want to optimize first, stop and perform your litmus test. Unless you have an egregious performance problem, your time would be better utilized trying to optimize the time spent in and connected to the database, the amount of data returned, and how often you make round-trips to and from the database.

    With that general information established, let's look at ten tips that can help your application perform better. I'll begin with the changes that can make the biggest difference.


    Tip 1—Return Multiple Resultsets

    Review your database code to see if you have request paths that go to the database more than once. Each of those round-trips decreases the number of requests per second your application can serve. By returning multiple resultsets in a single database request, you can cut the total time spent communicating with the database. You'll be making your system more scalable, too, as you'll cut down on the work the database server is doing managing requests.

    While you can return multiple resultsets using dynamic SQL, I prefer to use stored procedures. It's arguable whether business logic should reside in a stored procedure, but I think that if logic in a stored procedure can constrain the data returned (reduce the size of the dataset, time spent on the network, and not having to filter the data in the logic tier), it's a good thing.

    Using a SqlCommand instance and its ExecuteReader method to populate strongly typed business classes, you can move the resultset pointer forward by calling NextResult. Figure 1 shows a sample conversation populating several ArrayLists with typed classes. Returning only the data you need from the database will additionally decrease memory allocations on your server.


    Tip 2—Paged Data Access

    The ASP.NET DataGrid exposes a wonderful capability: data paging support. When paging is enabled in the DataGrid, a fixed number of records is shown at a time. Additionally, paging UI is also shown at the bottom of the DataGrid for navigating through the records. The paging UI allows you to navigate backwards and forwards through displayed data, displaying a fixed number of records at a time.

    There's one slight wrinkle. Paging with the DataGrid requires all of the data to be bound to the grid. For example, your data layer will need to return all of the data and then the DataGrid will filter all the displayed records based on the current page. If 100,000 records are returned when you're paging through the DataGrid, 99,975 records would be discarded on each request (assuming a page size of 25). As the number of records grows, the performance of the application will suffer as more and more data must be sent on each request.

    One good approach to writing better paging code is to use stored procedures. Figure 2 shows a sample stored procedure that pages through the Orders table in the Northwind database. In a nutshell, all you're doing here is passing in the page index and the page size. The appropriate resultset is calculated and then returned.

    In Community Server, we wrote a paging server control to do all the data paging. You'll see that I am using the ideas discussed in Tip 1, returning two resultsets from one stored procedure: the total number of records and the requested data.

    The total number of records returned can vary depending on the query being executed. For example, a WHERE clause can be used to constrain the data returned. The total number of records to be returned must be known in order to calculate the total pages to be displayed in the paging UI. For example, if there are 1,000,000 total records and a WHERE clause is used that filters this to 1,000 records, the paging logic needs to be aware of the total number of records to properly render the paging UI.


    Tip 3—Connection Pooling

    Setting up the TCP connection between your Web application and SQL Server can be an expensive operation. Developers at Microsoft have been able to take advantage of connection pooling for some time now, allowing them to reuse connections to the database. Rather than setting up a new TCP connection on each request, a new connection is set up only when one is not available in the connection pool. When the connection is closed, it is returned to the pool where it remains connected to the database, as opposed to completely tearing down that TCP connection.

    Of course you need to watch out for leaking connections. Always close your connections when you're finished with them. I repeat: no matter what anyone says about garbage collection within the Microsoft® .NET Framework, always call Close or Dispose explicitly on your connection when you are finished with it. Do not trust the common language runtime (CLR) to clean up and close your connection for you at a predetermined time. The CLR will eventually destroy the class and force the connection closed, but you have no guarantee when the garbage collection on the object will actually happen.

    To use connection pooling optimally, there are a couple of rules to live by. First, open the connection, do the work, and then close the connection. It's okay to open and close the connection multiple times on each request if you have to (optimally you apply Tip 1) rather than keeping the connection open and passing it around through different methods. Second, use the same connection string (and the same thread identity if you're using integrated authentication). If you don't use the same connection string, for example customizing the connection string based on the logged-in user, you won't get the same optimization value provided by connection pooling. And if you use integrated authentication while impersonating a large set of users, your pooling will also be much less effective. The .NET CLR data performance counters can be very useful when attempting to track down any performance issues that are related to connection pooling.

    Whenever your application is connecting to a resource, such as a database, running in another process, you should optimize by focusing on the time spent connecting to the resource, the time spent sending or retrieving data, and the number of round-trips. Optimizing any kind of process hop in your application is the first place to start to achieve better performance.

    The application tier contains the logic that connects to your data layer and transforms data into meaningful class instances and business processes. For example, in Community Server, this is where you populate a Forums or Threads collection, and apply business rules such as permissions; most importantly it is where the Caching logic is performed.


    Tip 4—ASP.NET Cache API

    One of the very first things you should do before writing a line of application code is architect the application tier to maximize and exploit the ASP.NET Cache feature.

    If your components are running within an ASP.NET application, you simply need to include a reference to System.Web.dll in your application project. When you need access to the Cache, use the HttpRuntime.Cache property (the same object is also accessible through Page.Cache and HttpContext.Cache).

    There are several rules for caching data. First, if data can be used more than once it's a good candidate for caching. Second, if data is general rather than specific to a given request or user, it's a great candidate for the cache. If the data is user- or request-specific, but is long lived, it can still be cached, but may not be used as frequently. Third, an often overlooked rule is that sometimes you can cache too much. Generally on an x86 machine, you want to run a process with no higher than 800MB of private bytes in order to reduce the chance of an out-of-memory error. Therefore, caching should be bounded. In other words, you may be able to reuse a result of a computation, but if that computation takes 10 parameters, you might attempt to cache on 10 permutations, which will likely get you into trouble. One of the most common support calls for ASP.NET is out-of-memory errors caused by overcaching, especially of large datasets.


    Figure 3 ASP.NET Cache

    There are a several great features of the Cache that you need to know. The first is that the Cache implements a least-recently-used algorithm, allowing ASP.NET to force a Cache purge—automatically removing unused items from the Cache—if memory is running low. Secondly, the Cache supports expiration dependencies that can force invalidation. These include time, key, and file. Time is often used, but with ASP.NET 2.0 a new and more powerful invalidation type is being introduced: database cache invalidation. This refers to the automatic removal of entries in the cache when data in the database changes. For more information on database cache invalidation, see Dino Esposito's Cutting Edge column in the July 2004 issue of MSDN®Magazine. For a look at the architecture of the cache, see Figure 3.


    Tip 5—Per-Request Caching

    Earlier in the article, I mentioned that small improvements to frequently traversed code paths can lead to big, overall performance gains. One of my absolute favorites of these is something I've termed per-request caching.

    Whereas the Cache API is designed to cache data for a long period or until some condition is met, per-request caching simply means caching the data for the duration of the request. A particular code path is accessed frequently on each request but the data only needs to be fetched, applied, modified, or updated once. This sounds fairly theoretical, so let's consider a concrete example.

    In the Forums application of Community Server, each server control used on a page requires personalization data to determine which skin to use, the style sheet to use, as well as other personalization data. Some of this data can be cached for a long period of time, but some data, such as the skin to use for the controls, is fetched once on each request and reused multiple times during the execution of the request.

    To accomplish per-request caching, use the ASP.NET HttpContext. An instance of HttpContext is created with every request and is accessible anywhere during that request from the HttpContext.Current property. The HttpContext class has a special Items collection property; objects and data added to this Items collection are cached only for the duration of the request. Just as you can use the Cache to store frequently accessed data, you can use HttpContext.Items to store data that you'll use only on a per-request basis. The logic behind this is simple: data is added to the HttpContext.Items collection when it doesn't exist, and on subsequent lookups the data found in HttpContext.Items is simply returned.


    Tip 6—Background Processing

    The path through your code should be as fast as possible, right? There may be times when you find yourself performing expensive tasks on each request or once every n requests. Sending out e-mails or parsing and validation of incoming data are just a few examples.

    When tearing apart ASP.NET Forums 1.0 and rebuilding what became Community Server, we found that the code path for adding a new post was pretty slow. Each time a post was added, the application first needed to ensure that there were no duplicate posts, then it had to parse the post using a "badword" filter, parse the post for emoticons, tokenize and index the post, add the post to the moderation queue when required, validate attachments, and finally, once posted, send e-mail notifications out to any subscribers. Clearly, that's a lot of work.

    It turns out that most of the time was spent in the indexing logic and sending e-mails. Indexing a post was a time-consuming operation, and it turned out that the built-in System.Web.Mail functionality would connect to an SMTP server and send the e-mails serially. As the number of subscribers to a particular post or topic area increased, it would take longer and longer to perform the AddPost function.

    Indexing e-mail didn't need to happen on each request. Ideally, we wanted to batch this work together and index 25 posts at a time or send all the e-mails every five minutes. We decided to use the same code I had used to prototype database cache invalidation for what eventually got baked into Visual Studio® 2005.

    The Timer class, found in the System.Threading namespace, is a wonderfully useful, but less well-known class in the .NET Framework, at least for Web developers. Once created, the Timer will invoke the specified callback on a thread from the ThreadPool at a configurable interval. This means you can set up code to execute without an incoming request to your ASP.NET application, an ideal situation for background processing. You can do work such as indexing or sending e-mail in this background process too.

    There are a couple of problems with this technique, though. If your application domain unloads, the timer instance will stop firing its events. In addition, since the CLR has a hard gate on the number of threads per process, you can get into a situation on a heavily loaded server where timers may not have threads to complete on and can be somewhat delayed. ASP.NET tries to minimize the chances of this happening by reserving a certain number of free threads in the process and only using a portion of the total threads for request processing. However, if you have lots of asynchronous work, this can be an issue.

    There is not enough room to go into the code here, but you can download a digestible sample at www.rob-howard.net. Just grab the slides and demos from the Blackbelt TechEd 2004 presentation.


    Tip 7—Page Output Caching and Proxy Servers

    ASP.NET is your presentation layer (or should be); it consists of pages, user controls, server controls (HttpHandlers and HttpModules), and the content that they generate. If you have an ASP.NET page that generates output, whether HTML, XML, images, or any other data, and you run this code on each request and it generates the same output, you have a great candidate for page output caching.

    By simply adding this line to the top of your page

    <%@ Page OutputCache VaryByParams="none" Duration="60" %> 
    
    you can effectively generate the output for this page once and reuse it multiple times for up to 60 seconds, at which point the page will re-execute and the output will once be again added to the ASP.NET Cache. This behavior can also be accomplished using some lower-level programmatic APIs, too. There are several configurable settings for output caching, such as the VaryByParams attribute just described. VaryByParams just happens to be required, but allows you to specify the HTTP GET or HTTP POST parameters to vary the cache entries. For example, default.aspx?Report=1 or default.aspx?Report=2 could be output-cached by simply setting VaryByParam="Report". Additional parameters can be named by specifying a semicolon-separated list.

    Many people don't realize that when the Output Cache is used, the ASP.NET page also generates a set of HTTP headers that downstream caching servers, such as those used by the Microsoft Internet Security and Acceleration Server or by Akamai. When HTTP Cache headers are set, the documents can be cached on these network resources, and client requests can be satisfied without having to go back to the origin server.

    Using page output caching, then, does not make your application more efficient, but it can potentially reduce the load on your server as downstream caching technology caches documents. Of course, this can only be anonymous content; once it's downstream, you won't see the requests anymore and can't perform authentication to prevent access to it.


    Tip 8—Run IIS 6.0 (If Only for Kernel Caching)

    If you're not running IIS 6.0 (Windows Server 2003), you're missing out on some great performance enhancements in the Microsoft Web server. In Tip 7, I talked about output caching. In IIS 5.0, a request comes through IIS and then to ASP.NET. When caching is involved, an HttpModule in ASP.NET receives the request, and returns the contents from the Cache.

    If you're using IIS 6.0, there is a nice little feature called kernel caching that doesn't require any code changes to ASP.NET. When a request is output-cached by ASP.NET, the IIS kernel cache receives a copy of the cached data. When a request comes from the network driver, a kernel-level driver (no context switch to user mode) receives the request, and if cached, flushes the cached data to the response, and completes execution. This means that when you use kernel-mode caching with IIS and ASP.NET output caching, you'll see unbelievable performance results. At one point during the Visual Studio 2005 development of ASP.NET, I was the program manager responsible for ASP.NET performance. The developers did the magic, but I saw all the reports on a daily basis. The kernel mode caching results were always the most interesting. The common characteristic was network saturation by requests/responses and IIS running at about five percent CPU utilization. It was amazing! There are certainly other reasons for using IIS 6.0, but kernel mode caching is an obvious one.


    Tip 9—Use Gzip Compression

    While not necessarily a server performance tip (since you might see CPU utilization go up), using gzip compression can decrease the number of bytes sent by your server. This gives the perception of faster pages and also cuts down on bandwidth usage. Depending on the data sent, how well it can be compressed, and whether the client browsers support it (IIS will only send gzip compressed content to clients that support gzip compression, such as Internet Explorer 6.0 and Firefox), your server can serve more requests per second. In fact, just about any time you can decrease the amount of data returned, you will increase requests per second.

    The good news is that gzip compression is built into IIS 6.0 and is much better than the gzip compression used in IIS 5.0. Unfortunately, when attempting to turn on gzip compression in IIS 6.0, you may not be able to locate the setting on the properties dialog in IIS. The IIS team built awesome gzip capabilities into the server, but neglected to include an administrative UI for enabling it. To enable gzip compression, you have to spelunk into the innards of the XML configuration settings of IIS 6.0 (which isn't for the faint of heart). By the way, the credit goes to Scott Forsyth of OrcsWeb who helped me figure this out for the www.asp.net severs hosted by OrcsWeb.

    Rather than include the procedure in this article, just read the article by Brad Wilson at IIS6 Compression. There's also a Knowledge Base article on enabling compression for ASPX, available at Enable ASPX Compression in IIS. It should be noted, however, that dynamic compression and kernel caching are mutually exclusive on IIS 6.0 due to some implementation details.


    Tip 10—Server Control View State

    View state is a fancy name for ASP.NET storing some state data in a hidden input field inside the generated page. When the page is posted back to the server, the server can parse, validate, and apply this view state data back to the page's tree of controls. View state is a very powerful capability since it allows state to be persisted with the client and it requires no cookies or server memory to save this state. Many ASP.NET server controls use view state to persist settings made during interactions with elements on the page, for example, saving the current page that is being displayed when paging through data.

    There are a number of drawbacks to the use of view state, however. First of all, it increases the total payload of the page both when served and when requested. There is also an additional overhead incurred when serializing or deserializing view state data that is posted back to the server. Lastly, view state increases the memory allocations on the server.

    Several server controls, the most well known of which is the DataGrid, tend to make excessive use of view state, even in cases where it is not needed. The default behavior of the ViewState property is enabled, but if you don't need it, you can turn it off at the control or page level. Within a control, you simply set the EnableViewState property to false, or you can set it globally within the page using this setting:

    <%@ Page EnableViewState="false" %>
    
    If you are not doing postbacks in a page or are always regenerating the controls on a page on each request, you should disable view state at the page level.


    Conclusion

    I've offered you some tips that I've found useful for writing high-performance ASP.NET applications. As I mentioned at the beginning of this article, this is more a preliminary guide than the last word on ASP.NET performance. (More information on improving the performance of ASP.NET apps can be found at Improving ASP.NET Performance.) Only through your own experience can you find the best way to solve your unique performance problems. However, during your journey, these tips should provide you with good guidance. In software development, there are very few absolutes; every application is unique.

    See the sidebar "Common Performance Myths".



    Rob Howard is the founder of Telligent Systems, specializing in high-performance Web apps and knowledge management and collaboration systems. Previously, Rob was employed by Microsoft where he helped design the infrastructure features of ASP.NET 1.0, 1.1, and 2.0. You can contact Rob at rhoward@telligentsystems.com.
    Cutting Edge: Implement Custom Cache Dependencies in ASP.NET 1.x -- MSDN Magazine, July 2004
    msdn.microsoft.com/msdnmag/issues/04/07/CuttingEdg...
    Cache Object - Dino Esposito

    One of the most compelling improvements that ASP.NET brought to ASP programming was the Cache object. The Cache has some similarities to the Application object and is a container of global data (as opposed to session-specific data) that features a fair number of innovative characteristics. At its core, the ASP.NET Cache is a sealed data container class built around a hashtable and defined in the System.Web.Caching namespace. The Cache object is central to the whole ASP.NET infrastructure. Various runtime components use the ASP.NET Cache to store working data. For example, the output of cached pages and controls is stored there. Likewise, applications that keep session state in memory use the ASP.NET Cache to store it.

    The Cache object is made up of two independent data stores—a public and a private cache. The private cache is reserved for system components and is accessed through a private API. The public cache is the one that is available to developers. In order to enable you to loop through the public store, the Cache class implements the IEnumerable interface.

    Unlike data stored in the Application object, items stored in the Cache object can have a number of additional attributes applied to them, including an expiration policy, a priority, and one or more dependencies. These attributes form the substrate of the advanced Cache features such as the ability to remove least-used items, have items expire at a given time, and set logical connections between a group of items and disk files.

    Like many other ASP.NET features, the Cache object has whetted the appetites of developers and given them more ideas for features to request in future versions. The most requested new Cache features are most likely support for database-driven data invalidation and custom dependencies. And if they aren't, they should be. The good news is that these two features will be available in ASP.NET 2.0 through a dazzling new class, SqlCacheDependency, which links cached items to database tables, and through the newly unsealed CacheDependency class.

    The SqlCacheDependency class detects changes to tables in SQL Server™ 7.0, SQL Server 2000, and SQL Server 2005 (formerly code-named "Yukon") and invalidates all dependent cache entries. In ASP.NET 2.0, CacheDependency has new virtual members and, more importantly, is unsealed and can be derived from. SqlCacheDependency derives from CacheDependency.

    In this column, I won't go through all the new caching features of ASP.NET 2.0, but I'll demonstrate how to implement similar custom cache dependencies in ASP.NET 1.x. A good understanding of cache dependencies is crucial if you are to create custom dependencies. So without further ado, let's start with a quick refresher of what a cache dependency is and how it works.


    What's a Cache Dependency, Anyway?

    The whole ASP.NET cache dependency mechanism is encapsulated in the CacheDependency class. This class represents a relationship between a cached item and an array of objects like files, directories, and other cached objects. To establish a dependency between a cached item and an external component, you add the item to the ASP.NET Cache object using a specific overload of the Insert method, like so:

    CacheDependency dep = new CacheDependency(fileName);
    cache.Insert(key, value, dep);
    
    In this code snippet, the item is fully identified by the key/value pair and its lifetime is bound to the timestamp of the specified file name. The item will automatically be removed from the Cache when the timestamp of the specified file changes. To add a new item to the Cache you can also use the Add method or the set accessor of the Item property:
    Cache[key] = value;     
    

    The Add method is not overloaded and it differs from the Insert method in that it throws an exception if the specified item already exists. (Insert, on the other hand, will overwrite the existing item.) You should note that if you use the set accessor of the Item property to add a new item, the item is correctly inserted into the cache, but no dependency is created. As a result of this, the item will be removed only when the application shuts down or if the item is explicitly removed.

    A cached item can also be bound to other cached items instead of or in addition to file dependencies. The following code snippet shows how to accomplish this:

    CacheDependency dep;
    dep = new CacheDependency(fileNames, otherKeys);
    Cache.Insert(key, value, dep);
    
    When either the files or the other cached items change, the recently added cache object is invalidated and then removed. To make an item dependent only upon a cached item, you set the file name parameter to null. A cache dependency can be subordinate to another cache dependency. This feature is particularly useful for implementing cascading changes to stored items. So much for the programming interface of the CacheDependency class; now it's time to take a closer look at its internal implementation and interaction with the Cache data store.

    Overall, items are automatically removed from the cache based on time, file, and key dependencies. It is interesting to look at how each type of dependency is handled. Time and key dependencies are managed by the Cache object itself. Items with an expiration are associated with a timer and removed at the end of the specified countdown interval.

    Resolving key dependencies is just one of the many tasks important to the update of the cache. Whenever a write operation occurs, an internal update process fires and frees all of the items that have a broken dependency.

    The file change notification mechanism keeps track of file dependencies. It's an operating system function that various ASP.NET modules, including the HTTP runtime, use extensively. When a file dependency is created, the ASP.NET Cache starts to monitor that file or directory. Thanks to the capabilities of the OS, any change on a monitored resource results in an event raised to the Cache object. The handler of this internal event takes care of the removal of the linked item.

    That's the only way in ASP.NET 1.x to force the Cache to evict stored elements. This means that any form of custom dependency must be implemented in terms of file, time, or, more likely, key dependencies. So what's a custom cache dependency? It is primarily a class that listens to a data source for changes. When a change is detected, the class bubbles that change up to a particular stored cache item so that the item is evicted from the cache. This loose description can be translated into three actions that any custom cache dependency class should take in ASP.NET 1.x.

    • Define the data source to listen to
    • Define a channel with the Cache to bubble changes up
    • Define a custom API

    The first two points are common to all versions of ASP.NET. However, only ASP.NET 2.0 provides an API that will create and manage custom dependencies. Let's proceed with a quick look at ASP.NET 2.0 cache dependencies before taking the plunge into my ASP.NET 1.x implementation.


    Cache Dependencies in ASP.NET 2.0

    As I mentioned, in ASP.NET 2.0 the CacheDependency class is unsealed and therefore inheritable. Subsequently, a custom dependency is simply a class that inherits from CacheDependency and implements a custom algorithm to detect changes in a given data source. Each detected change then causes an appropriate action to invalidate items in the cache. The use of inheritance guarantees that no breaking changes will ever be introduced in code ported from ASP.NET 1.x applications. In addition, there is no risk that your class misbehaves with the Cache object. The base class will handle all the wiring of the dependency object to the ASP.NET Cache object and all the issues surrounding synchronization and disposal. On the other hand, inheritance means that the memory footprint of your dependency class might be bigger than needed because your cache dependency class picks up all base class functionality, whether it needs it or not. Such functionality includes constructors that accept arrays of files or create dependencies on other cached items.

    A good example of a custom dependency class is the aforementioned SqlCacheDependency. It implements database dependencies by listening to table changes on SQL Server 7.0, SQL Server 2000, and SQL Server 2005. Figure 1 lists the new members added to the CacheDependency class to support custom dependencies.

    A custom dependency class relies on its parent for any needed interaction with the Cache object. The NotifyDependencyChanged method is called by classes that inherit from CacheDependency to tell the base class that the dependent item has changed. In response to this, the base class updates the values of the HasChanged and UtcLastModified properties. Any cleanup code needed when the custom cache dependency object is dismissed should go into the DependencyDispose method.

    The structure of a custom dependency object follows the pattern outlined in Figure 2. The class uses a timer to poll the data source at regular intervals and maintains a reference to the current data. When the data downloaded from the source is newer than the current copy, the corresponding item in the cache is invalidated. A call to the new NotifyDependencyChanged method breaks the internal dependency so that the Cache update process can evict the item.


    Figure 3 SqlCacheDependency with SQL Server 2005

    Note that in ASP.NET (no matter what the version) there are only two possible models for detecting changes—polling and notification. Polling means that the dependency class will periodically check the data source for changes. The notification mechanism is based on an external component that pushes changes to the dependency class in an asynchronous manner. A good example is the aforementioned file change notification mechanism. In ASP.NET 2.0, an implementation of the notification model is represented by the SqlCacheDependency class when it is connected to a SQL Server 2005 database. The overall picture is shown in Figure 3. The following is the code to set up the dependency:

    // data is a DataTable filled using this same command
    SqlCommand cmd = new SqlCommand(
       "SELECT * FROM Customers WHERE country='USA'", conn);
    SqlCacheDependency dep = new SqlCacheDependency(cmd);
    Cache.Insert("SqlSource", data, dep);
    

    In this case the SqlCacheDependency class listens to data coming from a SQL Server 2005 component—the Notification Delivery Service. The service monitors all the tables involved with the given query and invokes a callback function whenever something happens that modifies the resultset generated by the query. Internally, SqlCacheDependency exploits the new notification feature of SQL Server 2005 and runs code like the following:

    // cmd is the SqlCommand object defined above
    SqlDependency dep = new SqlDependency(cmd);
    dep.OnChanged += new OnChangedEventHandler(OnDepChanged);
    
    The callback function defined by SqlCacheDependency—the OnDepChanged method—calls NotifyDependencyChanged to invalidate the corresponding cache entry.


    Custom Cache Dependencies in ASP.NET 1.x

    At its core, a custom cache dependency is a class that incorporates a timer to periodically check the data source or that listens for event notifications pushed to it. You must link an instance of this class to a cached item. Because the CacheDependency class is sealed in ASP.NET 1.x, you can't rely on the Cache.Insert method to establish this link automatically. In addition, an instance of the custom dependency class must be stored in the ASP.NET Cache to survive for the application's lifetime. When the dependency class detects a change on the data source, it must do something to invalidate the cached item. To do so, you can only use one of the base ASP.NET 1.x expiration techniques—time, file, or key. Time just doesn't apply here. File or key dependencies are both acceptable and I don't see a reason to create or edit a temporary file. So I'll associate each item that has a custom dependency with an additional helper entry. By modifying the helper entry on changes to the monitored data source, you'll automatically invalidate the cached item. A new API is needed to transparently create the helper entry upon insertion of the item with a custom dependency:

    AmazonBooksCacheDependency dep = 
      new AmazonBooksCacheDependency(key, author); 
    CacheHelper.Insert(key, dataSet, dep);
    

    The AmazonBooksCacheDependency class represents a dependency on the list of books written by a given author. Some of the information from this data source is relatively static (books, titles, publishers); some other data, however, may vary on a regular, even daily basis. Price, sales rank, reviews, and rating are parameters that may change more than once a day. The previous code sample inserts a DataSet with book information into the ASP.NET cache and makes it dependent on the raw information published on the Amazon Web site. Whenever new information is posted, the cached DataSet is invalidated and refreshed.

    You can't just pass an instance of the AmazonBooksCacheDependency class to the Cache.Insert method. That's why I have written a helper method: CacheHelper.Insert. As you can see in Figure 4, the overall programming interface is nearly identical to that of ASP.NET 1.x for standard dependencies and to that of ASP.NET 2.0 for custom dependencies.

    Figure 4 shows the source code of the helper API to cache items with a custom dependency. CacheHelper is a class with a couple of static methods, the most important of which is Insert:

    static void Insert(string key, object value, MsdnMag.CacheDependency dep)
    
    This method wraps the overload of the Cache's Insert method that takes a CacheDependency object. The Insert method accepts a custom dependency object and does some extra work. Each new cache entry is paired with a second key whose name is programmatically built by concatenating the unique name of the entry with a standard (but arbitrary) string:
    internal static string GetHelperKeyName(string key)
    {
       return key + ":MsdnMag:CacheDependency";
    }
    

    The GetHelperKeyName is marked as internal (called friend, in Visual Basic® .NET) because I want it to be accessible from within other classes defined in the same assembly, but I don't want it to be callable from just any client.

    The Insert method in the CacheHelper class does two key things. First, it creates a helper key and stores the custom cache dependency object in it. The helper key is needed to convey notification changes to the principal item to which it is linked. Once placed in the cache, the custom dependency object is up and running all the time and can periodically query the data source looking for changes. When a change is detected, the custom dependency object updates its last modified date property in the cache. In doing so, it breaks the standard dependency between the helper item and the base item. As a result, the base item is evicted from the cache based on a change on an external data source.

    Figure 5 contains the source code of the MsdnMag.CacheDependency class. The class is built around a timer and an abstract method—HasChanged. The timer is instantiated in the constructor and executes a given callback method at specified intervals whose length is controlled by the Polling protected member. Polling indicates the extent of the interval in seconds, whereas the .NET Framework timers count in terms of milliseconds.

    The constructor of the CacheDependency custom class requires the name of the dependent key. This information is essential for establishing a link between the dependency object and the dependent item. With default ASP.NET dependencies (in both 1.1 and 2.0) this link is implicitly created by the Cache.Insert method when a cache entry is created or updated. The name of the linked item is stored in the DependentStorageKey protected property. Finally, the UtcLastModified date member contains the time of the last update to the dependency. When a change in the data source is detected, an update to this property triggers the eviction of the linked item from the cache.


    Setting Up the Timer

    As mentioned, there are two basic ways for a dependency object to know about changes in a monitored resource—polling and notification. Basically, either the dependency object sets up a timer and executes a method at specified intervals or registers to receive notifications about changes from an external module. The availability of a notification service is specific to the data source you're working with. SQL Server 2005 and Windows® have one for query and file changes, respectively. If you're going to create a dependency based on a message queue (for example, MSMQ), then a notification model might be appropriate to implement. For the sample dependency object covered in this column I'll use the polling model and implement it through a timer:

    if (InternalTimer == null)
    {
       TimerCallback func = new TimerCallback(InternalTimerCallback);
       int ms = Polling*1000;
       InternalTimer = new Timer(func, this, ms, ms);
    }
    

    The timer is an instance of the System.Threading.Timer class. The core of the timer is the delegate for the method that will be called periodically. In addition, the timer's constructor lets you specify a due time and a period. The due time is the time to wait before the first execution of the method; the period is the time to wait between subsequent executions of the callback method. It is important to note that you must keep a reference to the timer alive for as long as you use the object. Like any other managed object, the timer is subject to garbage collection if it goes out of scope. The timer callback is a TimerCallback delegate:

    public delegate void TimerCallback(object state);
    

    The parameter indicates an object containing specific information relevant to the method. The callback doesn't execute in the thread that created the timer; instead it executes in a separate thread that is provided by the system. The following code snippet illustrates the default implementation of the timer callback for a custom dependency object:

    private void InternalTimerCallback(object sender) 
    {
       // CacheDependency is the custom dep object
       CacheDependency dep = (CacheDependency) sender;
       if (dep.HasChanged())
          NotifyDependencyChanged(dep);
    }
    

    The callback calls the HasChanged method on the dependency class and based on the return value invokes the NotifyDependencyChanged method to break the dependency with the cache item. The NotifyDependencyChanged method I wrote belongs to the ASP.NET 1.x custom dependency class, but I named it after the equivalent method in ASP.NET 2.0 just to help you get familiar with ASP.NET 2.0 features more quickly. The method retrieves the name of the helper key and modifies its content. The content is just the dependency object modified to reflect the date of the last update:

    dep.UtcLastModified = DateTime.Now;
    HttpRuntime.Cache.Insert(key, dep);
    

    Since this is running on a background thread, in this case you must use the HttpRuntime object to get a valid reference to the ASP.NET Cache. If you try to reach the Cache through the current HttpContext object—what you normally would do from within a codebehind class—you'll run into a null exception:

    HttpContext.Current.Cache.Insert(key, dep);
    
    This line of code is equivalent to calling Cache through HttpRuntime but only within the context of a request, which is not necessarily the case in this instance.

    The NotifyDependencyChanged method is invoked by the timer callback, and the timer works independently of page requests. Basically, the dependency class, once placed in the ASP.NET Cache, remains active and running as long as the application lives, as does the timer it incorporates. The timer lives in the context of an ASP.NET application, but not in the context of a particular HTTP request. For this reason, HttpContext.Current just returns null. However, this doesn't mean that the Cache object is unavailable or unreachable. You simply have to take the right route to it; you have to use the HttpRuntime.Cache property.


    Creating a Web Service Dependency

    As you can see, there's a difference between the ASP.NET 2.0 customized dependency objects and my sample class. In ASP.NET 2.0, the base class doesn't incorporate any timer; each derived class can create its own timer if needed. In this ASP.NET 1.x implementation, I tried to reduce the amount of code that characterizes a particular dependency. I've accomplished this by deriving a new class from MsdnMag.CacheDependency and overriding the abstract HasChanged method:

    protected abstract bool HasChanged();
    

    The method is expected to check the monitored data source and return true if changes have occurred. Let's put it all together and write a custom dependency that gets broken if the output of a Web service method changes. (Rob Howard presented a similar ASP.NET 2.0 example at the Microsoft PDC 2003. You can find slides and source code on the ASP.NET site at http://www.asp.net/whidbey/downloads/WSV330_rhoward_demos.zip.)

    I'll build a dependency class to monitor books available on Amazon. In general, imagine you have an application that needs to work with relatively static data—data that changes, but not as frequently as to justify reading it back at every postback. So you download it the first time and place it in the ASP.NET Cache. How do you deal with changes? Well, if data is known to change often by your standards, you should use a time dependency that guarantees that your data is updated at regular intervals. If the frequency of changes is low, you can opt for a custom dependency—whenever the data changes on the server, your cached snapshot is automatically invalidated. When the cache entry is accessed next, it returns null and you know that it's time for you to get the records from the server. As you'll see in a moment, my sample code goes beyond this and automatically replaces the data in the cache. Figure 6 shows the full source code of the AmazonBooksCacheDependency class.

    The class inherits CacheDependency and defines two constructors—one takes the author's name, and one takes both a name and a poll time. The author's name is a parameter specific to the task of this dependency. The HasChanged method downloads book information for a given author and compares that to the snapshot currently stored in the cache. If the two don't match, the method returns false and the current cached item is freed:

    private void Refresh()
    {
       // Is the author cached already?
       string author = AuthorName.Text;
       if (!IsAuthorCached(author))
          PrepareCacheForAuthor(author);
    
       BindData(author);
    }
    

    When the user clicks the Refresh button, the code verifies that the books of the specified author have been cached. If not, a new entry in the cache is prepared:

    private void PrepareCacheForAuthor(string author)
    {
       string key = GetAuthorKey(author);
       AmazonBooksCacheDependency dep;
       dep = new AmazonBooksCacheDependency(key, author); 
                    
       // Create the cache entry
       CacheHelper.Insert(key, GetBooksInfo(author), dep);
    }
    

    GetBooksInfo uses the Amazon Web service to grab up-to-date book information. The data is an XML string that can be easily loaded into a DataSet. It is stored as a string in an ASP.NET Cache item named after the author. As mentioned, CacheHelper.Insert adds two keys to the cache—one with the actual data and one with the dependency object. The dependency object checks the Amazon Web service periodically and invalidates the cached item when changes have been detected. BindData uses a special method to read from the cache, as shown here:

    string ReadFromCacheForAuthor(string author)
    {
       string key = GetAuthorKey(author);
       string data = (string) Cache[key];
       if (data == null || data == string.Empty) {
          data = GetBooksInfo(author);
          Cache[key] = data;
       }
       return data;
    }
    

    If the contents of the cached item is null or empty, the method refills it with a new call to the Web service. Note that in order to develop with the Amazon Web service you must get a developer token, which you can apply for at http://xml.amazon.com. You'll also find licensing and reuse information there.

    It is worth noting that the solution outlined so far works, but could be improved upon. The key problem is with the implementation of the HasChanged method. In the Amazon example, book information is retrieved at every timer interval and compared to the contents currently stored in the cache. If a change is detected, the contents of the cache are freed and a second download occurs with the next read from the cache.

    As I've learned from sample implementations in ASP.NET 2.0, you should design the Web service to expose information about data changes through a simple Boolean method. A round-trip is still needed, but this trick will significantly reduce the amount of data being moved over the wire. ASP.NET 2.0 applies this pattern to database dependencies. Triggers set on monitored tables write a flag on a small helper table whenever a change is recorded. The helper table has just a few records—one for each monitored table in a database. The timer callback queries the small table looking for all records with the flag set. The result is guaranteed and the performance is optimal.

    If you don't have control over the Web service (which I don't have with Amazon), then I suggest a slightly different approach. Modify the HasChanged method so that it replaces data in the cache when changes are detected. In addition, have it always return false to prevent the custom dependency object from breaking the link and invalidating the data. This guarantees that no double read is needed when changes occur.


    Database Dependencies in ASP.NET 1.x

    Database dependencies are not supported in ASP.NET 1.1, but Jeff Prosise demonstrated how to achieve them in the April 2003 issue of MSDN® Magazine. The idea is that you define a trigger on a SQL Server table and configure it to monitor insertions, deletions, and updates. When fired, the trigger calls an extended stored procedure to modify a disk file. If you set a dependency between a cached item and this file, the ASP.NET Cache will dispose of it whenever the SQL Server table undergoes some changes. This solution can be reworked and optimized a bit in light of the ASP.NET 2.0 code. For example, you can create a helper table to record changes and a second cache entry to invalidate the cached data whenever changes to your data occur.


    Conclusion

    Once again, I have presented some code you can use now which is a much simplified version of code that will be completely built when ASP.NET 2.0 is released. You might as well get the functionality you want today, and you'll be ahead of the curve in understanding ASP.NET 2.0 tomorrow.

    Date and Time Format Patterns


    All the patterns:
    0MM/dd/yyyy11/06/2006
    1dddd, dd MMMM yyyyMonday, 06 November 2006
    2dddd, dd MMMM yyyy HH:mmMonday, 06 November 2006 02:09
    3dddd, dd MMMM yyyy hh:mm ttMonday, 06 November 2006 02:09 AM
    4dddd, dd MMMM yyyy H:mmMonday, 06 November 2006 2:09
    5dddd, dd MMMM yyyy h:mm ttMonday, 06 November 2006 2:09 AM
    6dddd, dd MMMM yyyy HH:mm:ssMonday, 06 November 2006 02:09:50
    7MM/dd/yyyy HH:mm11/06/2006 02:09
    8MM/dd/yyyy hh:mm tt11/06/2006 02:09 AM
    9MM/dd/yyyy H:mm11/06/2006 2:09
    10MM/dd/yyyy h:mm tt11/06/2006 2:09 AM
    11MM/dd/yyyy HH:mm:ss11/06/2006 02:09:50
    12MMMM ddNovember 06
    13MMMM ddNovember 06
    14yyyy'-'MM'-'dd'T'HH':'mm':'ss.fffffffK2006-11-06T02:09:50.0259680-05:00
    15yyyy'-'MM'-'dd'T'HH':'mm':'ss.fffffffK2006-11-06T02:09:50.0259680-05:00
    16ddd, dd MMM yyyy HH':'mm':'ss 'GMT'Mon, 06 Nov 2006 02:09:50 GMT
    17ddd, dd MMM yyyy HH':'mm':'ss 'GMT'Mon, 06 Nov 2006 02:09:50 GMT
    18yyyy'-'MM'-'dd'T'HH':'mm':'ss2006-11-06T02:09:50
    19HH:mm02:09
    20hh:mm tt02:09 AM
    21H:mm2:09
    22h:mm tt2:09 AM
    23HH:mm:ss02:09:50
    24yyyy'-'MM'-'dd HH':'mm':'ss'Z'2006-11-06 02:09:50Z
    25dddd, dd MMMM yyyy HH:mm:ssMonday, 06 November 2006 02:09:50
    26yyyy MMMM2006 November
    27yyyy MMMM2006 November

    The patterns for DateTime.ToString ( 'd' ) :
    0MM/dd/yyyy11/06/2006

    The patterns for DateTime.ToString ( 'D' ) :
    0dddd, dd MMMM yyyyMonday, 06 November 2006

    The patterns for DateTime.ToString ( 'f' ) :
    0dddd, dd MMMM yyyy HH:mmMonday, 06 November 2006 02:09
    1dddd, dd MMMM yyyy hh:mm ttMonday, 06 November 2006 02:09 AM
    2dddd, dd MMMM yyyy H:mmMonday, 06 November 2006 2:09
    3dddd, dd MMMM yyyy h:mm ttMonday, 06 November 2006 2:09 AM

    The patterns for DateTime.ToString ( 'F' ) :
    0dddd, dd MMMM yyyy HH:mm:ssMonday, 06 November 2006 02:09:50

    The patterns for DateTime.ToString ( 'g' ) :
    0MM/dd/yyyy HH:mm11/06/2006 02:09
    1MM/dd/yyyy hh:mm tt11/06/2006 02:09 AM
    2MM/dd/yyyy H:mm11/06/2006 2:09
    3MM/dd/yyyy h:mm tt11/06/2006 2:09 AM

    The patterns for DateTime.ToString ( 'G' ) :
    0MM/dd/yyyy HH:mm:ss11/06/2006 02:09:50

    The patterns for DateTime.ToString ( 'm' ) :
    0MMMM ddNovember 06

    The patterns for DateTime.ToString ( 'r' ) :
    0ddd, dd MMM yyyy HH':'mm':'ss 'GMT'Mon, 06 Nov 2006 02:09:50 GMT

    The patterns for DateTime.ToString ( 's' ) :
    0yyyy'-'MM'-'dd'T'HH':'mm':'ss2006-11-06T02:09:50

    The patterns for DateTime.ToString ( 'u' ) :
    0yyyy'-'MM'-'dd HH':'mm':'ss'Z'2006-11-06 02:09:50Z

    The patterns for DateTime.ToString ( 'U' ) :
    0dddd, dd MMMM yyyy HH:mm:ssMonday, 06 November 2006 02:09:50

    The patterns for DateTime.ToString ( 'y' ) :
    0yyyy MMMM2006 November

    Send E-Mail from your .NET application using your GMail Account - The Code Project - C# Programming
    www.codeproject.com/useritems/SendMailUsingGmailAc...
    Send E-Mail from your .NET application using your GMail Account
    CodeGuru: ASP.NET Tip: Sending Mail with ASP.NET 2.0
    www.codeguru.com/csharp/.net/net_asp/email/print.p...
    ASP.NET Tip: Sending Mail with ASP.NET 2.0
    Rating: none


    Eric Smith (view profile)
    October 20, 2006

    In ASP.NET 2.0, Microsoft deprecated the System.Web.Mail namespace and replaced it with System.Net.Mail. The new library introduces some new features, but it also includes some bugs in how mail is sent. Before discussing some of these in detail, let's go through some code sample (which assumes you've added a using System.Net.Mail at the top of the file):

    MailMessage msg = new MailMessage();
    msg.From = new MailAddress("address@domain.com", "Person's Name");
    msg.To.Add(new MailAddress("destination@domain.com",
    "Addressee's Name");
    msg.To.Add(new MailAddress("destination2@domain.com",
    "Addressee 2's Name");
    msg.Subject = "Message Subject";
    msg.Body = "Mail body content";
    msg.IsBodyHtml = true;
    msg.Priority = MailPriority.High;
    SmtpClient c = new SmtpClient("mailserver.domain.com");
    c.Send(msg);

    The code is similar with some minor changes to how you address the message. Instead of constructing an address, you can let the system do that for you. If you specify an e-mail address and a name, it will automatically display in the message as this:

    "Person's Name" <destination@domain.com>

    This is the "proper" form for an e-mail address. You can add multiple addresses to the To, CC, and BCC collections in the same way as shown above. This is programmatically easier to do than sending lots of messages—just add multiple addresses to the BCC property in order to send a mass mailing.

    Now, About Those Bugs...

    As previously mentioned, this new namespace has a couple of bugs. The first is when you send a message the headers are all added in lowercase letters. While the RFC for SMTP mail doesn't specify how the headers should be capitalized, many spam filters restrict messages where the headers are not properly capitalized.

    The other bug deals with the Priority setting, which should mark a message as important within the mail client. Because of the way the header is formatted, my mail program (Eudora) doesn't recognize it as the priority flag and doesn't mark the message as important. While this seems trivial, it's a change from the System.Web.Mail version for no apparent reason. I'm continuing to research this and if I can't find a good fix, I may switch back to System.Web.Mail and deal with the warning messages that Visual Studio displays about System.Web.Mail being deprecated.

    About the Author

    Eric Smith is the owner of Northstar Computer Systems, a Web-hosting company based in Indianapolis, Indiana. He is also a MCT and MCSD who has been developing with .NET since 2001. In addition, he has written or contributed to 12 books covering .NET, ASP, and Visual Basic. Send him your questions and feedback via e-mail at questions@techniquescentral.com.

    Chris Seary's blog :: Moving from Full Trust to partial trust with Code Access Security
    blog.searyblog.com/blog/_archives/2006/7/6/2088280...
    Moving from Full Trust to partial trust with Code Access Security
    by chris on Thu 06 Jul 2006 08:01 AM PDT  |  Permanent Link  |  Cosmos

    What do you do if your application has been developed using FullTrust, but you now find that it is necessary to move it to partial trust?

    This could arise from a risk assessment that requires mitigation against code injection attacks. An example of such an attack would be to insert a web page into an Asp.Net application using sql injection, although there are many other vectors that could get bad (malicious or poorly written) code onto a web server.

    So, after all the development and testing that's taken place, you find that altering the <trust /> element from Full to, say, Medium stops the application working. Perhaps it's so bad that you don't even get a detailed error message - just a line saying that the application requires more permissions than are granted by the security policy.

    Well, I've had to move applications to partial trust for a number of clients. The basic guide is that you must try to make your .aspx pages as dumb as possible.

    This means that Asp.Net pages should only contain code that relates to the user interface. Anything else (that accesses server resources such as files or a database etc.) should be moved to assemblies. Place these assemblies into the GAC, so that they have full trust.

    Next, create a business facade layer. A business facade roughly corresponds to use case diagrams for your system. The facade exposes methods that embody all interaction between the user and the system. Place this code into the GAC also.

    The facade methods will have Assert statements that stop the stack walk progressing further. This means that your web app won't be stopped by CAS Demands due to the lower trust level.

    Create a custom permission. Make demands for this permission within the business facade methods (remember - always combine an Assert with a Demand to ensure that the component is not being called by bad code or the wrong application).

    Now add the custom permission to the Asp.Net permission set for the web application in the security config file. This ensures that only this web app can call the business facade, rather than another web app or web service on the same server.

    You've now got a situation where CAS will only allow a specified web app to call the business facade. Supposing malicious code is placed onto the server, it can only call the business facade methods, which should not really let it do much more than use the intended system functionality anyway.

    If you've cut down the permissions declared in the Asp.Net permission set, then other server resources are locked down so that they can't be called directly by malicious code in this web app.

    Although CAS can be applied in different ways, this pattern fits with the advice in the 'Improving Web Application Security' document created by Patterns and Practices.

    Prevent Session Timeout in ASP.NET - The Code Project - ASP.NET
    www.codeproject.com/aspnet/Reconnect.asp

    Introduction

    I have developed ASP and ASP.NET sites for many years and one of the most common end user problems (apart from basic stupidity ;-) is that while the user is entering information into a web form or HTML edit box, the session timeout period will elapse and they lose all the work they have done.

    I have tried solutions such as making JavaScript alert the user to click a button or refresh page, but this has restrictions, especially if they are not able to submit the form yet due to required field limitations.

    Solution

    I recently came across some code which attempted to fix this problem but that was unsuccessful because the author had forgotten the issue of client side caching.

    Add to your page the following code:

    private void Page_Load(object sender, System.EventArgs e)
    {
    this.AddKeepAlive();
    }
    Collapse
    private void AddKeepAlive()
    {
    int int_MilliSecondsTimeOut = (this.Session.Timeout * 60000) - 30000;
    string str_Script = @"
    <script type='text/javascript'>
    //Number of Reconnects
    var count=0;
    //Maximum reconnects setting
    var max = 5;
    function Reconnect(){

    count++;
    if (count < max)
    {
    window.status = 'Link to Server Refreshed ' + count.toString()+' time(s)' ;

    var img = new Image(1,1);

    img.src = 'Reconnect.aspx';

    }
    }

    window.setInterval('Reconnect()',"
    + _
    int_MilliSecondsTimeOut.ToString()+ @"); //Set to length required

    </script>

    "
    ;

    this.Page.RegisterClientScriptBlock("Reconnect", str_Script);

    }

    This code will cause the client to request within 30 seconds of the session timeout the page Reconnect.aspx.

    The Important Part

    Now this works the first time but if the page is cached locally then the request is not made to the server and the session times out again, so what we have to do is specify that the page Reconnect.aspx cannot be cached at the server or client.

    This is done by creating the Reconnect.aspx page with this content:

    <%@ OutputCache Location="None" VaryByParam="None" %>
    <html>
    </html>

    The OutputCache directive prevents this from being cached and so the request is made each time. No code behind file will be needed for this page so no @Page directive is needed either.

    And that's it.

    ASP.NET.4GuysFromRolla.com: Accessing Embedded Resources through a URL using WebResource.axd
    aspnet.4guysfromrolla.com/articles/080906-1.aspx
    Accessing Embedded Resources through a URL using WebResource.axd
    By Scott Mitchell


    Introduction
    Many of the built-in ASP.NET server controls require additional, external resources in order to function properly. For example, when using any of the ASP.NET validation controls, the controls rely on a bevy of JavaScript functions to perform their client-side validation. While each validation control could emit such script directly into the page's content, a more efficient approach would be to package these JavaScript functions into an external JavaScript file and then include that file in the page using <script src="PathToExternalJavaScriptFile" type="text/javascript" >. This would reduce the total page size and would allow the browser to cache the external JavaScript file (rather than having to send the JavaScript code down to the browser on each and every page visit/postback).

    Prior to ASP.NET 2.0, such external resources that needed to be accessible to the visitor's browser had to be implemented as actual files on the file system. If you've worked with ASP.NET 1.x's validation controls, your pages have included a reference to a JavaScript file /aspnet_client/system_web/version/WebUIValidation.js and there is an actual file with that name residing in the web application's root. Such external resources hamper deployment - if you deploy your application from the testing server to production, it's imperative that the production server have the same external resources (WebUIValidation.js, in this case), in the same locations in the file system.

    To remedy this, ASP.NET 2.0 allows for external resources to be embedded within the control's assembly and then be accessed through a specified URL. With the external images, JavaScript files, CSS files embedded in the control's assembly, deployment is a breeze, as all of the resources are now contained within the assembly (the .dll file). There are no external resources whose file names and location on the file system must map up. Once embedded into the assembly, these resources can be accessed from an ASP.NET 2.0 web page through a special URL (WebResource.axd).

    In this article we'll examine how to embed external resources into an assembly and how to retrieve these resources on the web page using a special URL. This technique helps simplify installation and deployment for creating custom, compiled server controls that are shipped to customers. Read on to learn more!

    Maintain Scroll Position on Postback in ASP.NET 2.0

    When working with a lengthy web page that spans many vertical “pages,” scrolling down and then doing something that causes a postback results with the posted back page sitting back up at the top rather than auto-scrolling down to the location where the postback was triggered. With ASP.NET 1.x, this problem can be easily overcome with a bit of JavaScript and server-side code. See Maintaining Scroll Position on Postback for more information.

    ASP.NET 2.0 makes maintaining scroll position on postback much easier, although it's not a well-document or oft-discussed feature. When I show this little tip at User Group talks, to clients, or to my class, I'd say more than 90% of the people didn't know about this, are impressed, and wonder aloud, “Why isn't this common knowledge?”

    Anyway, to have a single page remember scroll position on postback, simply set the MaintainScrollPositionOnPostback attribute in the @Page directive to True. That's it! The page will then automagically inject the needed JavaScript and server-side logic to make this feature a reality.

    <%@ Page Language="..." MaintainScrollPositionOnPostback="true" ... %>

    Alternatively, you can enable this feature for all pages in the site by setting it in the <pages> element in Web.config.

    <pages maintainScrollPositionOnPostBack="true" />

    See my article Client-Side Enhancements in ASP.NET 2.0 for more information on this feature, as well as additional client-side features added to ASP.NET 2.0!

    ScottGu's Blog : Recipe: Deploying a SQL Database to a Remote Hosting Environment (Part 1)
    weblogs.asp.net/scottgu/archive/2006/12/22/recipe-...

    Recipe: Deploying a SQL Database to a Remote Hosting Environment (Part 1)

    Scenario:

    You finish building a great ASP.NET application, have everything tested and working right on your local system, are taking full advantage of the new ASP.NET 2.0 Membership, Role and Profile features, and are ready to publish it to a remote hosting environment and share it with the world. 

    Copying the .aspx files and compiled assemblies to the remote system is pretty easy (just FTP or copy them up).  The challenge that confronts a lot of developers, though, is how to setup and recreate the database contents - both schema and data - on the remote hosted site.  Unfortunately there hasn't historically been a super-easy way to accomplish this.

    The good news is that this week the SQL Server team published the release candidate of a new SQL Server Hosting Toolkit that will make it much, much easier to deploy your SQL solutions remotely to a hosted environment.  The toolkit allows you to work with SQL Express, SQL Server 2000, and SQL Server 2005 databases locally, and then easily transfer your schema and data and install them into a shared hosting remote SQL Server account.

    The below post describes how you can start using this today.

    Your Websites, Our Passion! : Free refactoring tools for ASP.NET code in Visual Studio 2005
    blogs.msdn.com/webdevtools/archive/2007/02/02/free...
    Free refactoring tools for ASP.NET code in Visual Studio 2005

    DevExpress today announced the availability of Refactor! for ASP.NET -- a free add-on to Visual Studio 2005 that enables very cool refactoring capabilities for ASP.NET code. The add-on can be downloaded from here:

    http://www.devexpress.com/refactorasp

    The add-on includes the following ASP.NET refactorings:

    • Add Validator
    • Extract ContentPlaceHolder
    • Extract ContentPlaceHolder (and create master page)
    • Extract Style (Class)
    • Extract Style (id)
    • Move to Code-behind
    • Move Style Attributes to CSS
    • Rename Style
    • Surround with Update Panel
    K. Scott Allen : Sorting the Visual Studio "Add New Item" Dialog with PowerShell
    odetocode.com/Blogs/scott/archive/2007/02/10/10505...

    Sorting the Visual Studio "Add New Item" Dialog with PowerShell

    The items in the "Add New Item" dialog box of Visual Studio appear in an arbitrary order. After a bit of sleuthing, I put together a brute force Powershell script to sort my items alphabetically. Now "Code File" appears near "Class", and I can always find "XML File" near the bottom of the dialog.

    SortOrder, and my sanity, is restored.

    What follows is the script. Download sort-vsItems.ps1. Let me caveat this script by saying it has only been tested on two machines. If you have any problems, do let me know.

    # sort-vsItems
    # scott@OdeToCode.com
    # Use at your own risk!
    # Script will make a backup of each template ... just in case...

    #vjslib for .zip support
    [System.Reflection.Assembly]::Load(
    "vjslib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
    ) | out-null

    # Get list of VS installed templates
    $installDir = (gp HKLM:Software\Microsoft\VisualStudio\8.0).InstallDir
    $installDir += "ItemTemplates\"
    $templateFiles = gci -recurse $installDir | ? {$_.extension -eq ".zip"}

    # Append list of custom templates
    $installDir = $home
    $installDir += '\My Documents\Visual Studio 2005\Templates\ItemTemplates'
    $templateFiles += gci -recurse $installDir | ? {$_.extension -eq ".zip"}

    $templateFiles = $templateFiles | sort name

    $i = 1
    $count = 0
    $tmpDir = new-item ([System.IO.Path]::GetRandomFileName()) -type directory
    $buffer = new-object System.SByte[] (8192)

    foreach($templateFile in $templateFiles)
    {
    write-host "Processing" $templateFile.FullName

    #extract all files (no methods appear available to modify zip in place)
    $zip = new-object java.util.zip.ZipFile($templateFile.FullName)
    $entries = $zip.entries()
    while($entries.hasMoreElements())
    {
    $zipEntry = $entries.nextElement()
    $in = $zip.getInputStream($zipEntry)
    $out = new-object java.io.FileOutputStream(
    $tmpDir.FullName + "\" + $zipEntry.getName()
    )
    while(($count = $in.read($buffer, 0, $buffer.Count)) -gt 0)
    {
    $out.write($buffer, 0, $count)
    }
    $out.Close()
    $in.Close()
    }
    $zip.Close()

    #tweak the sort order element
    $vst = gci -recurse $tmpDir | ? {$_.extension -eq ".vstemplate"}

    $xmlDoc = new-object System.Xml.XmlDocument
    $xmlDoc.Load($vst.FullName)
    if($xmlDoc.VSTemplate.TemplateData.SortOrder -ne $null)
    {
    $xmlDoc.VSTemplate.TemplateData.SortOrder = ($i++).ToString()
    $xmlDoc.Save($vst.FullName)
    }

    #backup existing zip file
    $backupName = $templateFile.FullName + ".bak"
    if(test-path $backupName) { remove-item $backupName }
    move-item $templateFile.FullName $backupName

    #zip up modified version
    $files= gci -recurse $tmpDir
    $zip = new-object java.util.zip.ZipOutputStream(
    new-object java.io.FileOutputStream(
    $templateFile.FullName))
    foreach($file in $files)
    {
    $zipEntry = new-object java.util.zip.ZipEntry($file.Name)
    $zip.putNextEntry($zipEntry)
    $in = new-object java.io.FileInputStream($file.FullName)
    while(($count = $in.read($buffer, 0, $buffer.Count)) -gt 0)
    {
    $zip.write($buffer, 0, $count)
    }
    $in.close()
    $zip.closeEntry()

    }
    $zip.close()
    del $tmpDir\*
    }

    del $tmpDir -force -recurse

    write-host "Running Visual Studio to refresh templates"
    $vstudio = (gp HKLM:Software\Microsoft\VisualStudio\8.0).InstallDir
    & $vstudio\devenv /setup

    Powershell is beauty!

    Infinities Loop : TRULY Understanding ViewState
    weblogs.asp.net/infinitiesloop/archive/2006/08/03/...

    TRULY Understanding ViewState

    ViewState is a very misunderstood animal. I would like to help put an end to the madness by attempting to explain exactly how the ViewState mechanism works, from beginning to end, and from many different use cases, such as declared controls vs. dynamic controls.

    There are a lot of great articles out there that try to dispel the myths about ViewState. You might say this is like beating a dead horse (where ViewState is the horse, and the internet is the assailant). But this horse isn't dead, let me tell you. No, he's very much alive and he's stampeding through your living room. We need to beat him down once again. Don't worry, no horses were harmed during the authoring of this article.

    AJAX

    Atlas and more : DOM events in the Microsoft AJAX Library
    weblogs.asp.net/bleroy/archive/2006/11/06/DOM-even...
    DOM events in the Microsoft AJAX Library

    In previous CTPs, the client-side DOM event model was the IE model. You would use attachEvent and get the event data from window.event. In other words, we had just implemented the IE model in Firefox and Safari. This didn't fly as well as we expected for a number of reasons. For instance, it wasn't very well received from a philosophical point of view: making standards-compliant browsers behave like the one non-compliant browser was interpreted by some as a malicious attempt by the Evil Empire to undermine the standardization of the Web by enforcing proprietary APIs. It wasn't. It just seemed at the time like a smart way to build cross-browser compatibility and the reason we did it this way and not the other way around is that both Safari and Firefox have extensible DOM Element prototypes whereas IE doesn't. In other words, there was no way we could make IE behave like the standard, but we could make the others behave like IE. Any other way to make a library cross-browser has to introduce a third API that abstracts the standard and proprietary APIs. This third API is of course just as proprietary as the IEism, whereas our previous approach had the advantage of not introducing a new one. Still, the implementation was fairly complex and relied on the presence of extensibility points that we had no guarantee we would find on other browsers that we may want to support in the future. Another problem with our first implementation was its reliance on the server to detect browsers and selectively send the compat scripts to the client. So we decided to change our compat layer and come back to a more conventional approach that will be easier to adapt to new browsers and that doesn't rely on the server, let alone on browser detection.

    The new model for DOM events is thus introducing a new API, but at least it's closely modeled after the standard APIs so it should feel pretty familiar. There are many differences in the implementations of DOM events that we needed to abstract. The first one is in the names of the methods that you call to add an event. In standard browsers, you use add/removeEventListener, in IE it's attach/detachEvent. The event names themselves are different: "click" is "onclick" in IE. Then, you have to abstract the signature of the event handlers themselves: in IE the parameters come from the global window.event object, in other browsers they are passed as a parameter. Finally, the contents of the event parameter object are themselves widely divergent from one browser to the other: mouse buttons don't have the same values for example, and some very useful stuff like mouse positions is missing altogether from the standard.

    Here's how you register a click event handler in the AJAX Library now:

    $addHandler(myDomElement, "click", someFunction);

    As you can see, we use the standard event name here. $addHandler is an alias for Sys.UI.DomEvent.addHandler. You can unhook an event using $removeHandler. For instance, you should do that from your dispose methods to break circular references between your JavaScript objects and the DOM and to prevent memory leaks. From $addHandler, we wrap your function pointer into a closure that will be what will really be attached as the DOM event handler to abstract browser differences. What's nice is that you don't need to worry about that, you just provide a function that takes the event parameters object as its argument, and you write this function exactly the way you would write it for a standard-compliant browser. That means that even in IE, the event parameter object will contain standard fields: the key codes will be the right ones, as will be the mouse button values. By the way, so that you don't need to use integers when testing keys and mouse buttons, we have two enums, Sys.UI.Key and Sys.UI.MouseButton, that you can use in your event handlers:

    function myClickHandler(e) {
      if (e.button === Sys.UI.MouseButton.leftButton) {
        //...
      }
    }
    
    function myKeyUpHandler(e) {
      if (e.keyCode === Sys.UI.Key.enter) {
        //...
      }
    }

    From the event handler, it's worth mentioning what the "this" pointer means. Just like in a standard event handler, it represents the DOM element the event was attached to, not necessarily the element that triggered the event. Those are different if the event bubbled up. For example, you may have subscribed to the click event of a div element and what was really clicked was a button inside of it. In this case, "this" represents the div, not the button, but you can still get to the button using the target field of the event parameter object. Now, if you're wiring events from a component, chances are you're using delegates as your handler functions. In this case, "this" still refers to your component, not to any DOM element. One more thing to note is that the native, proprietary event object can still be got from the rawEvent field of the event parameter object.

    Another "interesting" divergence is the way you cancel an event or prevent it from bubbling up. In IE, you set returnValue to false (resp. set cancelBubble to true), whereas the standard is to call preventDefault (resp. stopPropagation). The event parameter object that you get as the argument of your handler has the two standard methods (preventDefault and stopPropagation) so you can use them without having to worry about IE.

    The last things I'd like to show on the new DOM event model are some of the helpers we've added to make component developers' lives easier. In a control or behavior, you typically have to wire up multiple handlers. For example, an accessible hover behavior might want to subscribe to mouseover, mouseout, focus and blur. To do that, you'd typically create delegates to your handlers and then wire up these delegates to the DOM events one by one. From your "dispose" method, you'd also have to remove those handlers one by one and get rid of the delegates. Seeing that this pattern was repeated over and over again in almost any control or behavior sample, we decided to add helpers to batch those operations. So here's how you would wire up all those events:

    $addHandlers(this.get_element(), {
      mouseover: this._onHover,
      mouseout: this._onUnhover,
      focus: this._onHover,
      blur: this._onUnhover
    }, this);

    No need to create delegates here, this will be done for you under the hood. From dispose, it gets even simpler as we are keeping track of everything that was added using the $add APIs:

    $clearHandlers(this.get_element());

    In a future post, I'll also look at AJAX class events, which are events that you can expose from your own objects, and that are closer to .NET events than to the DOM events I've been showing here.

    WindowsDevCenter.com: Drag and Drop Ajax Programming With Atlas
    www.windowsdevcenter.com/lpt/a/6675

    Drag and Drop Ajax Programming With Atlas

    by Jesse Liberty
    08/01/2006

    Just about every book or article I've read about Atlas has given me a headache. They seem to have two messages in common: (1) all my server-side ASP.NET applications are now obsolete, and (2) the only way to fix them is to add really complicated JavaScript. Woe is me.

    These books and articles (which shall remain nameless because lawyers are so expensive these days) all seem to imply that to be a real Atlas programmer, I should stop hiding behind black-box controls and understand Ajax and JavaScript and, for good measure, XMLHTTPRequest objects! It's like a nightmare flashback to 1985 when, to be a real C programmer, you really needed to understand Assembler so that when things went wrong, you could drop down into the snap-shot debugger to see what was in the registers. (Haven't done that for a while, and I don't really miss it much.)

    No! I won't have it. The move up the ladder of abstraction is a good thing. We were right when we said that by using a higher level of abstraction, we can better focus on the problem domain, leaving the "plumbing" to well-tested controls provided by Microsoft and other vendors. I'll be damned if I want to slip back into writing type-unsafe non-object-oriented JavaScript. Twenty years of progress up in smoke, down the drain, and out the door. Feh. (Feh is Yiddish for Yuck.)

    Fortunately, it is a chimera, appearing on the horizon through the fractured lens of an early adopter's telescope. The script-centric approach is an artifact of pre-release technology being explored by those who know the underlying foundation of JavaScript best. When Ajax started to get very hot, the first ones to write about it were, naturally enough, JavaScript aficionados. They were the ones who could create JavaScript client-side code that made an appreciable difference in the user experience.

    Atlas expertise has become equated with script expertise; a false identity needs to be exposed and broken as quickly as possible. When Programming ASP.NET,4th Edition is released, we will have a great deal of Atlas, but very little JavaScript. I feel very strongly that application programmers should focus on applications, and controls programmers should focus on building nicely encapsulated controls (no user servable parts, opening black box may void warranty).

    This Article Shows No JavaScript

    For this article, I am going to start top-down, using Atlas controls and drag-and-drop within Visual Studio 2005, and I am not going to look inside the black box at all if I can help it. (Almost seems impossible, or at least sacrilegious, no? But of course, that is exactly how I would teach normal ASP.NET controls.)

    Will I cover everything? Nope. But we'll get a darn good look at how quickly and easily you can improve a working ASP.NET program with tested and working Atlas controls that you can download for free from Microsoft. And my guess is they'll be turning them out almost as fast as you can learn them.

    What You Need to Write Atlas Applications

    The Microsoft Atlas website provides a very good explanation of what you need to download and how to install the files (as well as how to integrate them into Visual Studio), so I won't waste your time recapitulating the instructions here. Please navigate to the ASP.NET Atlas home page and click on the Download icon to retrieve the JUNE CTP (presumably if there is a later CTP by now, you'll want that, and the example code for this article will work as is or with minor modifications). The June CTP comes in three parts: Documentation, Samples, and Setup. Only the third part is required, but you'll want all three. When you are done, return to the home page and click on the Atlas Control Toolkit; you'll need that later in the article.

    The downloads include directions on adding two tabs to your toolbox (Atlas and Atlas Toolkit) and populating them with the controls (a matter of browsing for the appropriate DLLs). It is pretty easy, but if the drag-and-drop approach doesn't work, you can always right-click on the toolbox tab and select "Choose Items," then click Browse, find the Bin directory, and choose the DLL. Doing so will add all the Atlas controls from that dll to the tab, as shown in Figure 1.


    Figure 1. Adding controls from the dll

    Starting with a Working Program

    In May, I published Building a Web-Based Bug Tracking Application (Part 2). The article and complete source is available on my website (click on Books, then Articles). We'll use that application, which builds Tiny Bug Tracker as a starting point, adding Atlas functionality to improve the existing application, much as you might add Atlas controls to improve any web application you have already written.

    The purpose of the Tiny Bug Tracker is to provide a single programmer (or a very small group of programmers) with a simple web-based application for tracking the progress of bugs reported, worked on, fixed, and closed by various members of the team. You begin by creating users and roles using the Web Site Administrator Tool, as shown in Figure 2.


    Figure 2. Adding a user with the WAT (for more on ysabell see Discworld)

    Users then sign in to use the application, as shown in Figure 3.


    Figure 3. Logging In

    Users can click on the menu choice to Enter a bug, which brings them to the New Bug form (also used to edit bugs) as shown in Figure 4.


    Figure 4. Enter a bug

    All of these bugs are placed into the TBTData database, with one entry for each bug in the Bugs table, and one entry for each modification of the bug in the BugHistories table, as indicated in Figure 5.


    Figure 5. Bug Database

    Once you have bugs in your database, you can review them in TBTReview.aspx. Clicking on Details brings up the Details panel for that bug, revealing the fields that do not fit in the grid. Clicking History brings up a grid with each change for the given bug, and you can then reveal the details for that change, as shown in Figure 6.


    Figure 6. Review

    There are a few problems with this application that Atlas can help us resolve. Each time you move from the details of one bug to the details of the next, the entire page is posted back to the server; there is a delay and hence, there is flicker. Also, as you scroll the review, you may realize you want to enter a new bug, but the menu is up on top, which forces you to scroll back. It would be nice to have the menu available. Finally, if you click Cancel when entering a new bug, it cancels--potentially throwing away a good deal of work accidentally.

    Creating the Atlas Version

    To make life easier, rather than adding Atlas controls to the existing application, I'm going to build a new Atlas application and then add the pages from the previous application. This ensures that Visual Studio will set up all the references that Atlas requires, and by using the pages from the previous article, I know that the basic application code should work, and I won't have to take time re-examining the underlying functionality. Note that I'm using the June CTP of Atlas; if you are using a different release, your mileage may vary.

    What follows will make much more sense if you take a few moments to read through the original article. I'll wait here. Take your time. I'll work on other projects; wake me when you are ready.

    First Improvement: Reduce Flicker--Only Update Part of Each Page

    One of the biggest problems with the Tiny Bug Tracker application is that each time you ask for details on a different bug, the entire page must be redrawn, causing annoying flicker. (Due to caching, the database is not "hit" each time, but the round trip to the server does cause a noticeable pause).

    We can use Atlas to ensure that we update only that part of the page that must be updated. To do so, we'll add four update panels to the TBTReview Page. An Update Panel is an Atlas control specifically designed to be updated independently of anything else on the page. The details of how it works may well be fascinating, but all you really care about is that it works asynchronously; that is, there is no need for a full-page postback. Even better, you can create each update panel by dragging it from the toolbox right onto the page and hey! presto! Visual Studio does the work for you. (You can also create an Update Panel by adding one by hand in source view, or you can create one dynamically in C# at runtime).

    Creating the Atlas Application Step-by-Step

    You can create your new application by starting with your existing application and adding the Atlas controls, libraries, and references. Really. But as I said above, I'm not going to do that because with beta software, I like to let Visual Studio set everything up just right. So, I'll create a new website called Atlas Bug Tracker by creating a New Web Site and choosing "Atlas Web Site" from the templates, as shown in Figure 7.


    Figure 7. Create new Atlas website

    The initial site will be built with a few files included by default: a readme.txt, a eula.rtf, a default.aspx, a web.config and, within bin, a copy of Microsoft.Web.Atlas.dll. Delete the readme, the eula, and Default.aspx; you won't be needing them.

    Open the WAT, and on the security tab, choose forms-based authentication, and create the user names and roles you had in the previous application (Alex, Dan, Jesse, and Stacey and Developer, Manager, QA, and User).

    Copy over the pages TBTMaster.master, TBTReview.aspx, TBTBug.aspx, and TBTWelcome.aspx and their codebehind pages, and add them to the project. Do not copy over the web.config file.

    The WAT has modified the web.config file created for you when you chose the Atlas project, so you now have the combined configuration of both: an Atlas project with forms-based security.

    You'll want to run the program and make sure everything is right before you begin making changes.

    Note: the complete source code is available for download from my website. (Click on Books, and then on Articles), along with a link to my private support forum and other material that may be of interest.

    The Script Manager

    The key control for Atlas is the ScriptManager. Virtually every Atlas control requires that its page has a ScriptManager whose job is to coordinate all the Atlas controls on that page. Fortunately, this application has a master page, so we can drop our ScriptManager on the master, providing one ScriptManager for every page in the application (One ScriptManager to rule them all, one ScriptManager to find them...*).

    The one attribute that we will need to add to the ScriptManager is EnablePartialRendering="True" because it turns on the ability to render part of the page asynchronously without a full-page postback.

    You can place the ScriptManager control anywhere in the master page. I've put mine right below the login status control:

           <atlas:ScriptManager ID="ScriptManager1" runat="server" EnablePartialRendering="True" />

    Dragging and dropping the control from the toolbox is probably the easiest way to add it to the page, and doing so ensures the control is properly registered on the page. When you do, however, you are likely to see the Destination File Exists dialog box, as shown in Figure 8.


    Figure 8. Destination File Exists

    When the control is added to the page, it brings Microsoft.Web.Atlas.dll with it. There is no reason not to click Yes and refresh the file.

    Breaking TBTReview into Sections

    With the ScriptManager in place, you are ready to add asynchronous updates for the various grids and DetailsView objects on the TBTReview page. To do so, drag four UpdatePanel objects from the Atlas tab of the Toolbox onto the page. Each UpdatePanel represents a block of UI that can be updated independently. I placed BugReviewGrid and its data source into the first, BugDetailsView and its data source into the second, BugHistoryGrid and its data source into the third, and BugHistoryDetailsView and its data source into the fourth. You can do this with drag-and-drop in Design view, or by moving the HTML in Source view.

    For example, in Figure 9, I've dragged UpdatePanel2 onto the form, and I'm dragging BugDetailsView into the Panel.


    Figure 9. Dragging into the Update Panel

    When I release the mouse, BugDetailsView and its SqlDataSource will be inside the Update panel, as reflected in the HTML in Source view (severely abridged here):

    
        <span class="style1"><atlas:UpdatePanel ID="UpdatePanel2" runat="server">
          <ContentTemplate></span>
              <asp:DetailsView ...  </asp:DetailsView>
               <asp:SqlDataSource ...>   </asp:SqlDataSource>
          <span class="style1"></ContentTemplate>
       </atlas:UpdatePanel></span>
    </span>

    The change in performance is instant and remarkable. Suddenly, the page stops flickering. When you move from one bug's details to another, it is smooth, instantaneous, and flicker-free. To make sure the difference is real, remove the attribute EnablePartialRendering="True" from the ScriptManager in the master page and rerun the application. You can't miss it.(Don't forget to put the attribute back!)

    Look Ma, No JavaScript

    Stop a moment and notice that at no time did you write JavaScript. You didn't touch XMLHttpRequest, and you have no idea how the work is done; it is all every bit as magical as how, for example, the DataGrid control itself works. Here's what I say: that is not only OK, that is jolly good. This ignorance lets you focus on what you're trying to do, and lets Microsoft focus on building the controls. (Hey! Stop whimpering. If you really want to know how it works, I promise, we have lots of books on JavaScript and on Programming Atlas that go into all the gory details. But isn't it nice, just for the moment, to focus on getting the job done?)

    But There's More...

    OK, we've accomplished asynchronous updates, and we could stop there and feel pretty good about Atlas, but let's take a peek at control extenders. You get quite a few with the Atlas Toolkit (and more are on the way all the time). One I really like already is the AlwaysVisible extender. This allows you to take any control and say, "I want this to stay visible on my page as the user scrolls through it." Not only is this impressive as all get-out, it is actually quite useful. It gives you the power of frames without any of the hassle (and frames, I'm told by my designer friends, are oh-so-five minutes ago).

    Our master page has a few items on it, one of which is a menu that lets the user add new bugs. It would be nice if the user could get to that menu even if s/he has scrolled down on the review page. This turns out to be so easy as to be embarrassing.

    Open TBTMaster.master and drag an instance of AlwaysVisibleControlExtender onto the page. Do this in source view. Between the open and close tags, you'll add one element (Intelligence will offer it for you) of type AlwaysVisibleControlProperties. Intelligence will help with the properties, but I'll show them to you here:

    TargetControlID="mnuTBTNavigation"
    VerticalSide ="top"
    VerticalOffset = "10"
    HorizontalOffset ="10"
    HorizontalSide ="right"
    ScrollEffectDuration =".1" />

    The TargetControlID is the control that you will be making visible--in this case, the menu. The vertical and horizontal sides dictate where you want the menu to appear (see Figure 10). The offset is how far from the top/right you want the menu to appear, and the scroll effect duration is how long a delay you want (in this case, a tenth of a second). I hate to admit it, but that is all you do; Atlas takes care of the rest. As you scroll, the menu follows you, like a loyal puppy.


    Figure 10. Always Visible Menu

    It is almost magical how the menu chases after you as you scroll down to look at a bug's detail. It is magical how little you have to do, and it is damnable how hard some people make it seem. I love the fact that Microsoft has encapsulated all this into a control that I can just drag onto my form and--poof!--it works. Yes, over time, I'll want to understand (maybe) how they did it, but you know what? I have deadlines, and this is very cool functionality.

    Creating a Trap for Cancel

    One bit of coding that we all write a dozen (or a thousand) times is a "trap" for a Cancel button. The user clicks Cancel, and we want a popup that asks the user to confirm that s/he intended to cancel out of the work being done. This can be tricky to write in an ASP.NET application, as dialog boxes do require some JavaScript. Once again, however, Atlas not only makes this trivial, it does so in a way that is totally consistent with the rest of Atlas and integrates seamlessly with your existing application.

    The TBTBug.aspx file has two controls, btnSave and btnCancel. To add a trap for btnCancel, all you need do is drag a ConfirmExtender into the cell with the two buttons:

    <td colspan="2">
       <AtlasToolkit:ConfirmButtonExtender 
       ID="ConfirmButtonExtender1" runat="server">
          <AtlasToolkit:ConfirmButtonProperties ConfirmText="Are you sure you want to discard this?"
           TargetControlID="btnCancel" />
       </AtlasToolkit:ConfirmButtonExtender>
        <asp:Button ID="btnSave" runat="server" Text="Save" BackColor="Lime" Font-Bold="True" OnClick="btnSave_Click" />
        <asp:Button ID="btnCancel" runat="server" Text="Cancel" BackColor="Red" Font-Bold="True" ForeColor="Yellow" OnClick="btnCancel_Click" />
    </td>
    

    The effect is illustrated in Figure 11.


    Figure 11. Cancel Confirmation

    The ConfirmExtender takes one internal attribute of type ConfirmButtonProperties, which itself takes two attributes, one for the text to display in the message box, and the other to identify the button that triggers the confirmation.

    For this to work, the control must be registered, but that is done for you when you drag the control onto the form. Sweet. In the version I'm using, the tag is set at CC1, but it's simple to change the tag to something you like; I changed it to AtlasToolkit

    <%@ Register Assembly="AtlasControlToolkit" Namespace="AtlasControlToolkit" TagPrefix="AtlasToolkit" %>

    so that when I use the control, I can write code like this: <AtlasToolkit:ConfirmButtonProperties, which reminds me of the source of the control. Not necessary, but nice.

    There are a lot of useful controls that come with the Toolkit, and I strongly encourage you to take a look at the video tutorials available in the Atlas How Do I series, which will walk you through much of this material and beyond. You may want to subscribe to one or more of the RSS feeds that will keep you up-to-date on what Atlas controls are being released.

    Above all, don't let anyone convince you that Atlas is difficult. The whole point is for it to be as easy as ASP.NET, allowing you to keep your focus on building your application, and not on building the plumbing.

    Jesse Liberty , Microsoft .NET MVP, is the best-selling author of O'Reilly Media's Programming ASP.NET, Programming C#, Programming VB2005 and over a dozen other books on web and object-oriented programming. He is president of Liberty Associates, Inc. where he provides contract programming, consulting and on-site training in .NET.

    ScottGu's Blog : Tip/Trick: How to Register User Controls and Custom Controls in Web.config
    weblogs.asp.net/scottgu/archive/2006/11/26/tip-tri...

    Tip/Trick: How to Register User Controls and Custom Controls in Web.config

    I've been including this technique in my ASP.NET Tips/Tricks talks the last year, but given how many people are always surprised by its existence I thought it was worth a dedicated tip/trick post to raise the visibility of it (click here to read other posts in my ASP.NET Tips/Tricks series).

    Problem:

    In previous versions of ASP.NET developers imported and used both custom server controls and user controls on a page by adding <%@ Register %> directives to the top of pages like so:

    <%@ Register TagPrefix="scott" TagName="header" Src="Controls/Header.ascx" %>
    <%@ Register TagPrefix="scott" TagName="footer" Src="Controls/Footer.ascx" %>
    <%@ Register TagPrefix="ControlVendor" Assembly="ControlVendor" %>

    <html>
    <body>
        
    <form id="form1" runat="server">
            
    <scott:header ID="MyHeader" runat="server" />
        </
    form>
    </body>
    </html>

    Note that the first two register directives above are for user-controls (implemented in .ascx files), while the last is for a custom control compiled into an assembly .dll file.  Once registered developers could then declare these controls anywhere on the page using the tagprefix and tagnames configured.

    This works fine, but can be a pain to manage when you want to have controls used across lots of pages within your site (especially if you ever move your .ascx files and need to update all of the registration declarations.

    Solution:

    ASP.NET 2.0 makes control declarations much cleaner and easier to manage. Instead of duplicating them on all your pages, just declare them once within the new pages->controls section with the web.config file of your application:

    <?xml version="1.0"?>

    <configuration>

      
    <system.web>
        
        
    <pages>
          
    <controls>
            
    <add tagPrefix="scottgu" src="~/Controls/Header.ascx" tagName="header"/>
            <
    add tagPrefix="scottgu" src="~/Controls/Footer.ascx" tagName="footer"/>
            <
    add tagPrefix="ControlVendor" assembly="ControlVendorAssembly"/>
          </
    controls>
        
    </pages>

      
    </system.web>

    </configuration>

    You can declare both user controls and compiled custom controls this way.  Both are fully supported by Visual Studio when you use this technique -- and both VS 2005 Web Site Projects and VS 2005 Web Application Projects support them (and show the controls in WYSIWYG mode in the designer as well as for field declarations in code-behind files).

    One thing to note above is the use of the "~" syntax with the user-controls.  For those of you not familiar with this notation, the "~" keyword in ASP.NET means "resolve from the application root path", and provides a good way to avoid adding "..\" syntax all over your code.  You will always want/need to use it when declaring user controls within web.config files since pages might be using the controls in different sub-directories - and so you always need to resolve paths from the application root to find the controls consistently.

    Once you register the controls within the web.config file, you can then just use the controls on any page, master-page or user control on your site like so (no registration directives required):

    <html>
    <body>
        
    <form id="form1" runat="server">
            
    <scott:header ID="MyHeader" runat="server" />
        </
    form>
    </body>
    </html>

    Hope this helps,

    Scott

    P.S. Special thanks to Phil Haack who blogged about this technique as well earlier this month (for those of you who don't know Phil, he helps build the very popular SubText blog engine and has a great blog).

    Robert Seder : More than just the basics with ASP.NET user controls
    90statehouse.com/forums/blogs/robertseder/archive/...

    More than just the basics with ASP.NET user controls

    I was messing around with user controls, and this is like the 3rd time I’ve needed to look this up, so I figured I’d blog about it so I have it written down.

     

    As you know, if you have some controls on an ASP.NET page, you could scoop those out and put those into a “User Control” – then drag that user control (the .ascx file) onto your .aspx page and voila. It’s a great way to isolate functionality and code.

     

    Now, what if you need to pass the user control some data? Well, in the class of the user control, you can create public properties. For example:

     

    private Unit _width = new Unit(400);

     

    public Unit Width

    {

        get

        {

            return _width;

        }

        set

        {

            _width = value;

        }

    }

     

    Then, when you create the control on your page, you’ll see that property available, like this:

     

    <uc1:SectionHeader ID="sh1" runat="server" Width="400px">

    </uc1:SectionHeader>

     

    Now, what if you need to pass in something more? Perhaps you want to pass in a chunk of data. Or what if that chunk of data needs to be something even more complex, like ASP.NET controls? Well, there is this kind of obscure, poorly documented concept you can steal from the “templated controls” concept within web controls. You can have a user control, but also have “templates” that you can populate at runtime – with ANYthing that is valid within ASP.NET.

     

    Let’s say you have this as your user control:

     

    <div class="SectionHeader">

        <asp:PlaceHolder ID="Title" runat="server"></asp:PlaceHolder><br />

        <asp:PlaceHolder ID="Description" runat="server"></asp:PlaceHolder>

    </div>

    And let’s say we want to fill in Title and Description at runtime, by the caller – and I might want to put a bunch of things into that Description. Like, a calendar control, a gridview, just regular text - anything. What we need to do is create public ITemplate properties like this, in the user control class:

     

    private ITemplate _title;

    private ITemplate _description;

     

    [PersistenceMode(PersistenceMode.InnerProperty), TemplateContainer(typeof(TemplateControl))]

    public ITemplate TitleTemplate

    {

        get { return _title; }

        set { _title = value; }

    }

    [PersistenceMode(PersistenceMode.InnerProperty), TemplateContainer(typeof(TemplateControl))]

    public ITemplate DescriptionTemplate

    {

        get { return _description; }

        set { _description = value; }

    }

     

    Now, we just need to add some code to the OnInit, so that it will take the controls and/or text that a user entered for the Title and Description templates – and it puts them into the respective PlaceHolders on the user control page:

     

    protected override void OnInit(EventArgs e)

    {

        base.OnInit(e);

        if (_title != null)

        {

            _title.InstantiateIn(Title);

        }

        if (_description != null)

        {

            _description.InstantiateIn(Description);

        }

    }

     

    Note that the Title and Description up there, are the references to the placeholders on the page. So this is where it assigns what the user passed in – to the placeholders on your user control.

     

    Lastly, add the attribute ParseChildren to the UserControl class, like this:

     

    [ParseChildren(true)]

    public partial class SectionHeader : System.Web.UI.UserControl

    {

     

    This tells intellisense on the caller page, to look for child objects (which TitleTemplate and DescriptionTemplate will be “templates” that you put in between the opening and closing tag of the control). So ParseChildren will just make sure it acts correctly when you create the control on the page.

     

    When you do all this, this is what this will look like (and Intellisense supports this too) on the consuming side:

     

    <uc1:SectionHeader ID="sh1" runat="server" Width="400px">

        <TitleTemplate>This is the title</TitleTemplate>

        <DescriptionTemplate>

            This is the description, and this has have controls in them

            too. For example:

    <asp:Calendar ID="cal1" runat="server">

    </asp:Calendar>

        </DescriptionTemplate>

    </uc1:SectionHeader>

     

    So this is quite a powerful and easy way to encapsulate and segregate UI functionality. Plus, it’s like a zillion times easier than manually rendering your own WebControl which is compiled in a .dll – which is nice, but there is no user interface, and you have to do all of your control creation programmatically. You have to figure that you are usually writing a custom control because you need to do something complicated - so that's the last place you want to be stuck without an interface!

    posted on Sunday, June 04, 2006 9:47 AM by RobertSeder
    JAVASCRIPT

    JavaScript - Get selection
    www.quirksmode.org/js/selected.html

    Get selection

    Page last changed 8 months ago
    This page is supposed to be in my frameset.

    The script theoretically works in Opera 7.5, but any selection is removed before the mousedown event occurs, so that this script won't ever catch a selection. Solved in Opera 8.

    It does not work in Explorer 4 on Mac, Opera, Konqueror, Ice Browser, Escape and Omniweb.

    Netscape 4 on Linux doesn't support onMouseDown on the button.

    See below for more browser compatibility details.

    On this page I give the simple code you need for finding out what text the user has selected with his mouse.

    Example

    First of all, select some text on this page and press the button or the link below:

    Get selection

    The script

    The script that is executed when you click on the link or the button is extremely simple:

    function getSel()
    {
    	var txt = '';
    	var foundIn = '';
    	if (window.getSelection)
    	{
    		txt = window.getSelection();
    		foundIn = 'window.getSelection()';
    	}
    	else if (document.getSelection)
    	{
    		txt = document.getSelection();
    		foundIn = 'document.getSelection()';
    	}
    	else if (document.selection)
    	{
    		txt = document.selection.createRange().text;
    		foundIn = 'document.selection.createRange()';
    	}
    	else return;
    	document.forms[0].selectedtext.value = 'Found in: ' + foundIn + '\n' + txt;
    }
    

    So I try to find the selection by three methods: first window.getSelection(), then document.getSelection(), then document.selection.createRange().text.

    The resulting browser incompatibilities:

    Method Explorer 5 Win Explorer 6 Win Explorer 5.2 Mac Mozilla 1.75* Safari 1.3 Opera 8 Netscape 4 iCab 2.9.8
    window.getSelection()
    No No No Yes Yes No No No
    var txt = window.getSelection();
    document.getSelection()
    No No Yes Yes No Yes Yes Yes
    var txt = document.getSelection();
    document.selection
    Yes Yes No No No No No No
    var txt = document.selection.createRange().text

    *: also tested in Firefox 1.0

    Old browsers: use mousedown

    Netscape 4 on Linux doesn't support onMouseDown on the button.

    Whether you want to use a button or a link to get the selection, always use the mouseDown event handler to call the script.

    <input type="button" value="Get selection" onmousedown="getSel()" />
    <a href="#" onmousedown="javascript:getSel(); return false"
    	class="page">Get selection</a>
    

    Netscape and Explorer 4 each have their specific problems when you use an onClick. Of course, what's a problem in one browser works fine in the other.

    Netscape 4

    Netscape 4 un-selects a text when you press the mouse button. Since an onClick event fires after the mouse button has been released, you would lose the selected text. The onMouseDown event fires just before Netscape 4 un-selects the text, so the script can still grab it. If you add the

    return false
    

    to the action, you cancel the unselecting of the text. I've done that so that Netscape 4 users can also compare their selected text to the text in the textarea.

    Furthermore, Netscape 4 does not see any selected texts inside form fields (try selecting text inside the textarea).

    Finally, the button does not work in Netscape 4 on Linux because it does not support the onMouseDown event handler on buttons. The link works fine, though.

    Explorer 4

    Explorer 4 has the same problem as Netscape 4, but then with the button. When you click on a button, the text is un-selected. Therefore, also use the mouseDown event handler here. Unfortunately adding return false does not have any effect: the text is still un-selected.

    Dynamic Drive DHTML Scripts- Select (and copy) Form Element Script
    www.dynamicdrive.com/dynamicindex11/selectform.htm

    Select (and copy) Form Element Script

    Author: Dynamic Drive

    Description: Allow your surfers to easily select the contents of your form elements- as we have done with our script containers throughout Dynamic Drive- with this script. And just to outdo ourselves, we've also embedded in this script the ability to concurrently copy the content to clipboard (memory) as well (applicable only in IE 4+). Enjoy!

    function HighlightAll(theField) {
        var tempval=eval("document."+theField)
        tempval.focus()
        tempval.select()
        if (document.all&&copytoclip==1){
            therange=tempval.createTextRange()
            therange.execCommand("Copy")
            window.status="Contents highlighted and copied to clipboard!"
            setTimeout("window.status=''",1800)
        }
    }
     
    Form submission and the ENTER key?
    ppewww.ph.gla.ac.uk/~flavell/www/formquestion.html
    When there is only one single-line text input field in a form, the user agent should accept Enter in that field as a request to submit the form.

    This was evidently meant as a convenient way to submit simple queries, but reducing the risk, on a complex form, of prematurely submitting it while trying to fill it in. Numerous browser implementers (e.g Netscape, Opera, various versions of Lynx,...) followed this advice, though it seems with slightly different interpretations. If you use only one "single-line text input field" (i.e input type="text"), along with other kinds of form widget such as buttons, selections etc., then these browsers will submit the form. Some (e.g Netscape) did this even if the form also contained multi-line input field(s) i.e textarea element(s), but some others will not. Therefore, for widest accessibility of Enter=>Submit, you would want to use no textarea elements in the same form.

    There's nothing more that the author needs to do in order to make this possible, and nothing that the author can do, within the context of HTML itself, to help a browser that doesn't make this possible: the HTML4 specification makes no particular ruling about what Return or Enter should do (and it looks as if recent versions of Lynx have a different behaviour in this respect). So, you really have to include a Submit function (preferably a straightforward input type="submit") to be sure that all bases are covered.

    Note that a page can contain several forms: almost all of the browsers tried were applying the rule to each form separately.

       MSDN Home >  MSDN Library >  Web Development >  HTML and CSS >  HTML and DHTML Overviews/Tutorials

    Activating ActiveX Controls


    Users cannot directly interact with Microsoft ActiveX controls loaded by the APPLET, EMBED, or OBJECT elements. Users can interact with such controls after activating their user interfaces. This topic describes how Microsoft Internet Explorer handles ActiveX controls, shows how to load ActiveX controls so their interfaces are activated, and describes the impact of this behavior on accessibility tools and applications hosting the WebBrowser Control.

    This topic contains the following sections.

    For an introduction to the user experience, please see Internet Explorer 6: ActiveX Update .

    For additional information regarding the platforms affected by this update, please see Internet Explorer ActiveX Update .

    Understanding Control Activation

    Interactive controls are ActiveX controls that provide user interfaces. When a web page uses the APPLET, EMBED, or OBJECT elements to load an ActiveX control, the control's user interface is blocked until the user activates it. If a page uses these elements to load multiple controls, each interactive control must be individually activated.

    When a control is inactive, the following effects occur.

    • Dynamic HTML (DHTML) events related to user interaction, such as onblur and onclick, are blocked. Appendix A lists the DHTML events that are blocked when a control is inactive.

    • The control does not respond to window messages generated by the keyboard or mouse, such as WM_CLICK and WM_KEYPRESS, and so on.

    • An overlay window, created on the control's OLE site, prevents keyboard and mouse messages from reaching the inactive control.

    When an inactive control is created, Internet Explorer uses different techniques to prevent keyboard or mouse window messages from reaching the control. When the inactive control is a windowed control, such as the HTML Help Control, Internet Explorer uses the EnableWindow Function to disable the inactive control's window. When the user activates a windowed control, the same function activates the disabled window. When the inactive control is a windowless control, such as the Office Web Components, keyboard and mouse messages are filtered by the control's container.

    When a control is inactive, it does not respond to user input; however, it does perform operations that do not involve interaction. If, for example, you open a web page that uses Microsoft Windows Media Player to play a music file, the music plays after the page loads. You cannot interact with Windows Media Player until the control's user interface is activated, as shown in the following figure.

    Note  While inactive controls do not respond to direct user interaction; they do respond to script commands.

    To activate an interactive control, either click it or use the TAB key to set focus on it and then press the SPACEBAR or the ENTER key. Interactive controls loaded from external script files immediately respond to user interaction and do not need to be activated.

    Some windowed controls use Windows API functions, such as GetKeyState and GetCursorPos, to determine the state of the keyboard and mouse and then respond to the function results. For these controls only, a prompt appears before the control is run in Internet Explorer. To run the control, the user needs to click the button in the message window before the page loads. After loading, the control will not require activation. At present, the following controls have this behavior, but the vendors are working on new controls that would not have this behavior.

    • Virtools (TM) Web Player from Virtools SA
    • Macromedia Shockwave Player (TM) from Adobe Systems Inc.
    • QuickTime (TM) from Apple Computer, Inc.

    When loaded from external script files, these controls do not display a prompt.

    The following figure shows the prompt dialog.

    Loading Interactive Controls Externally

    To create Web pages that load interactive controls that respond immediately to user input, use Microsoft JScript to load controls from external script files. You cannot write script elements inline with the main HTML page to load your control externally. If the script is written inline programmatically, for example with the writeln function, the loaded control will behave as if it was loaded by the HTML document itself and will require activation. To ensure a control is interactive when it is loaded, use one of the following techniques to load your control from an external file.

    The following example uses document.write to load a control dynamically.

    <!-- HTML File -->
    <html>
      <body leftmargin=0 topmargin=0 scroll=no>
        <script src="docwrite.js"></script>
      </body>
    </html>
    // docwrite.js
    document.write('<object classid="clsid:6BF52A52-394A-11d3-B153-00C04F79FAA6">');
    document.write('<param name="URL" value="example.wmv">');
    document.write('<param name="autoStart" value="-1"></object>');
    

    External script files can also modify an element's outerHTML property to achieve the same effect, as shown in the following example.

    <!-- HTML File -->
    <html>
      <body> 
        <div>
          <script src="embedControlOuterHTML.js"></script>
        </div>
      </body>
    </html>
    // outerhtml.js
    embedControlLocation.outerHTML = '<embed src="examplecontrol">';

    The next example uses document.createElement to load an ActiveX control using the OBJECT element.

    Important  When using createElement to add an Object or Embed element to a web page, use care to create the element, initialize its attributes, and add it to the page's DOM before creating the ActiveX control to be loaded by the new element. For more information, please see the createElement documentation.
    <!-- HTML File -->
    <html>
      <body> 
        <div id="DivID">
          <script src="createElementExplicit.js"></script>
      </body>
    </html>
    // createElementExplicit.js
    var myObject = document.createElement('object');
    DivID.appendChild(myObject);
    myObject.width = "200";
    myObject.height = "100";
    myObject.classid= "clsid:6BF52A52-394A-11d3-B153-00C04F79FAA6"; 
    myObject.URL = "example.wmv";
    myObject.uiMode = "none" ;

    The next example uses innerHTML and a JScript function to load an ActiveX control while specifying parameter values.

    <!-- HTML File -->
    <html>
     <head>
       <script src="external_script.js" language="JScript"></script>
     </head>
     
     <body>
       <div id="EXAMPLE_DIV_ID">
          This text will be replaced by the control
       </div>
       <script language="JScript">
         CreateControl( "EXAMPLE_DIV_ID",
                        "clsid:6BF52A52-394A-11d3-B153-00C04F79FAA6",
                        "EXAMPLE_OBJECT_ID", "600", "400", "example.wmv",
                        "-1")
       </script>
     </body>
    </html>
    // external_script.js
    function CreateControl(DivID, CLSID, ObjectID,
                           WIDTH, HEIGHT, URL, AUTOSTART)
    {
      var d = document.getElementById(DivID);
      d.innerHTML = 
        '<object classid=' + CLSID + ' id=' + ObjectID + 
        ' width=' + WIDTH + ' height=' + HEIGHT +'>
        <param name="URL" value=' + URL + '>
        <param name="autoStart" value=' + AUTOSTART + '/>';
    }

    Because the next example uses the writeln function to insert the script into the original HTML document, the resulting control requires activation. To load a control without requiring activation, use one of the previous examples.

    <!-- HTML File -->
    <html>
      <body> 
        <div id="embedControlLocation">
          <script id="elementid" src="embedControl.js"></script> 
        </div>
      </body>
    </html>
    // embedControl.js
    document.writeln('<script>');
    document.write('document.writeln(\'');
    document.write( '<object classid = 
                    "clsid:6BF52A52-394A-11D3-B153-00C04F79FAA6" 
                    width="100" height="100" />');
    document.write('\');');
    document.writeln('</script>');
    
    Note  To automatically activate ActiveX controls, Internet Explorer must be using a version of jscript.dll newer than September 30, 2003. Earlier versions of this DLL will require all controls to be activated, regardless of the mechanism used to load them from a web page. For the current version of jscript.dll, please see Windows Script Download .

    Programmatically Determining Whether a Control is Inactive

    You cannot use JScript functions or server-side scripts to determine whether or not a control is active. Application hosting the web browser control cannot determine whether or not a control is active.

    Controls can determine activation state via the DISPID_AMBIENT_UIDEAD ambient property by calling through IDispatch::Invoke. Controls that implement the IOleControl interface are notified when this property changes through IOleControl::OnAmbientPropertyChange.

    Accessibility Impact

    When accessibility tools encounter ActiveX controls, they can use the object's IAccessible interface to obtain information about the control. Inactive controls can be activated with the IAccessible::accDoDefaultAction  method.

    The following table describes the results when IAccessible methods are called on inactive controls.

    MethodDescription
    IAccessible::accDoDefaultActionActivates the control and will expose the ActiveX control or Java Applet within the MSAA tree.
    IAccessible::accHitTestReturns CHILDID_SELF
    IAccessible::accLocationLocation of the underlying ActiveX control or Java Applet
    IAccessible::accNavigateReturns E_NOTIMPL
    IAccessible::accSelectReturns E_NOTIMPL
    IAccessible::get_accChildReturns S_FALSE
    IAccessible::get_accChildCountReturns 0 and S_OK
    IAccessible::get_accDefaultActionReturns "Select this control"
    IAccessible::get_accDescriptionReturns E_NOTIMPL
    IAccessible::get_accFocusReturns E_NOTIMPL
    IAccessible::get_accHelpReturns "This control is inactive. Select the control to activate and use it."
    IAccessible::get_accHelpTopicNo Change - Returns E_NOTIMPL
    IAccessible::get_accKeyboardShortcutNo Change - Delegates the object. If there is no object, the method returns E_NOTIMPL.
    IAccessible::get_accNameReturns "Inactive Control"
    IAccessible::get_accParentNo Change - Returns the closest accessible element in the parent chain.
    IAccessible::get_accRoleReturns ROLE_SYSTEM_PUSHBUTTON
    IAccessible::get_accSelectionReturns E_NOTIMPL
    IAccessible::get_accStateReturns current state of the object. This state always includes STATE_SYSTEM_FOCUSABLE
    IAccessible::get_accValueReturns E_NOTIMPL
    IAccessible::put_accNameReturns E_NOTIMPL
    IAccessible::put_accValueReturns E_NOTIMPL

    For information on activated controls, or controls that do not require activation please see the Active Accessibility SDK.

    Note  Accessibility tools should refresh after triggering the default action in order to properly display the ActiveX control's data and the data of its children, if any.

    WebBrowser Control Impact

    By default, custom applications hosting the WebBrowser Control do not block interactive ActiveX controls loaded by the APPLET, EMBED, or OBJECT elements. Inactive control blocking only applies to the following applications.

    • Windows Explorer
    • Internet Explorer
    • MSN Explorer
    • AOL® Explorer
    • AOL® 8.0
    • AOL® 9.0
    • CompuServe 2000
    • AIM®
    • NetCaptor
    • Browse3D
    • Macromedia Dreamweaver
    • Macromedia Contribute
    • Netscape® 8 (when using Internet Explorer as the rendering engine)

    To match the behavior of Internet Explorer in your application, add the DOCHOSTUIFLAG_ENABLE_ACTIVEX_INACTIVATE_MODE flag to the dwFlags parameter of your DOCHOSTUIINFO structure, as shown in the following example.

    HRESULT GetHostInfo(DOCHOSTUIINFO *pInfo)
    {
       ...
    
        pInfo->cbSize = sizeof(DOCHOSTUIINFO);
        pInfo->dwFlags = { Other DOCHOSTUIFLAGs } | 
          DOCHOSTUIFLAG_ENABLE_ACTIVEX_INACTIVATE_MODE;  
       ...
    
        return S_OK;
    }

    You can also enable interactive control blocking by adding your application's process name to the following registry key.

    HKEY_LOCAL_MACHINE (or HKEY_CURRENT_USER)
    SOFTWARE
    Microsoft
    Internet Explorer
    Main
    FeatureControl
    FEATURE_ENABLE_ACTIVEX_INACTIVATE_MODE
    process_name.exe=(DWORD) 0x00000001

    Note  Because users can modify the registry, the DOCHOSTUIINFO flag is the preferred way to enable interactive control blocking.

    Applications can register to incorporate ActiveX control activation by default. For more information, please engage your Technical Account Manager or contact Microsoft Product Support.

    Appendix A: DHTML Events Blocked by Inactive Controls

    The following table lists the DTHML events that are blocked when ActiveX controls are inactive.

    onactivateondragleaveonmouseout
    onbeforeactivateondragoveronmouseover
    onbeforecopyondragstartonmouseup
    onbeforecutondroponmousewheel
    onbeforedeactivateonfocusonmove
    onbeforepasteonfocusinonmoveend
    onbluronfocusoutonmovestart
    onclickonhelponpage
    oncontextmenuonkeydownonpaste
    oncontrolselectonkeypressonresize
    oncopyonkeyuponresizeend
    oncutonlosecaptureonresizestart
    ondblclickonmousedownonscroll
    ondeactivateonmouseenteronselectstart
    ondragendonmouseleave 
    ondragenteronmousemove 



    © 2006 Microsoft Corporation. All rights reserved. Terms of Use |Trademarks |Privacy Statement
    Atlas and more : A nice and compact way to coerce to Boolean in JavaScript
    weblogs.asp.net/bleroy/archive/2006/09/29/A-nice-a...
    A nice and compact way to coerce to Boolean in JavaScript

    JavaScript is always the strange beast as far as comparisons are concerned. There are cases where the automatic contextual casting is not quite convenient. For example, we like to reliably return booleans from some of our methods, not null, not undefined and not some random object. Being able to say that this function will return a boolean is a Good Thing that the users of the API will appreciate when debugging.

    Anyway, I used to do this to coerce something to Boolean:

    something ? true : false
    Dave Reed just showed me a much more compact way of doing that:
    !!something

    Probably not the most readable thing in the world but I could get used to that, the same way I got used to

    return something || null;
    when I want to coerce undefined into null as a return value.
    CSS

    ScottGu's Blog : Eliminating CSS Image Flicker with IE6
    weblogs.asp.net/scottgu/archive/2006/05/29/Elimina...

    Eliminating CSS Image Flicker with IE6

    One challange that web designers and developers often wrestle with is an annoying "image flicker" issue that sometimes shows up when using CSS image references.  For example, when using a CSS rule like so:

         .someClassName
            {
                background:#AABBCC url(someBackGroundImage.gif) repeat-x;
            }

    This can cause some browsers (including IE 6) to have an annoying flicker when rendering the image (especially when used with hover styles or for background images).  In particular, this often shows up when building hierarchical and show/hide menus, and can degrade the UI experience for the site.  There are two ways to fix the issue for clients:

    1) Adjust your web-server's cache content settings for the static images being referenced from the CSS file.  This unfortunately requires admin access on the machine with IIS 6 (although not with IIS7).

    2) Use ASP.NET to define a handler that dynamically renders images with the appropriate cache content settings set.  This does not require any special configuration on your web-server, and can be done by simply copying a .ashx handler file into your app.

    Russ Helfand (who did the work on the ASP.NET CSS Adapter Toolkit) has a great blog post that details how to-do option #2.  You can read all about his solution here.

    This is a great tip/trick you can use for the Menu and TreeView CSS Adapters, as well as for any static element CSS rules you are using within your page.

    Hope this helps,

    Scott

    ASP.NET Resources - Stop Image Flicker With Cache-Control Extensions
    www.aspnetresources.com/blog/cache_control_extensi...

    Image flicker in Internet Explorer/Win causes IE to get bitchslapped quite often. To get rid of this effect Dean Edwards suggests to configure Apache to keep images cached. Otherwise you need to manually change caching preferences in IE which a lot of people don’t know how to do. Besides, when images flicker, it’s you who looks bad, not the users’ IE. Dean says he’s no server expert, and neither am I, but there’s a way to configure IIS in a similar fashion.

    Cache-Control Extensions is a little-known feature that was rolled out with Internet Explorer 5.0. In a nutshell, it’s a proprietary extension to the Cache-Control header IE 5.x and 6.x understand. The two extensions are pre-check and post-check.

    Internet Explorer applies the following logic to objects served with these extensions:

    1. Upon first request, the object is cached and is served from cache until the post-check interval expires.
    2. Once the post-check interval expires IE fetches the object from cache and checks for an updated one in the background. If a newer object is available it caches it. Upon every subsequent request this updated (and now cached) object is served until the pre-check interval expires.
    3. Once the pre-check interval elapses the object is treated as expired. IE will first ask the HTTP server if the object has changed since it was requested by the browser. If it has, IE will load the updated object.

    Note: I’m not referring to web pages. I refer to objects because we might be talking about images, web pages, style sheets, external JavaScript files, etc.

    As MSDN states, the Refresh button will not trigger this logic because Refresh always sends the if-modified-since request to the server. Hyperlinks do trigger this logic.

    How about an example? Suppose an HTTP server sends an image with the following header:

    Cache-Control: post-check=3600,pre-check=43200

    Both pre-check and post-check specify time intervals in seconds. We tell IE to cache the mentioned image for 12 hours (60 * 60 * 12 seconds). The first hour (60 * 60 seconds) IE will simply display the image from its local cache. However, after 60 minutes we want it to check for a newer one in the background, i.e. it will display the cached one and then do a background check. When 12 hours are up, IE checks for a modified image first.

    Set Cache-Control Extensions In IIS

    The only missing piece of the puzzle is where to set these extensions to get the ball rolling. You do it in the IIS Manager snap-in. You may choose to set them on a specific folder, such is a folder with images. You may also set them on your entire web app, but hardly ever would you want to cache every single page on your site.

    Fire up the IIS Manager snap-in from Administrative Tools, pick a folder in your web app, right click, go to Properties, switch to the HTTP Headers tab, and click Add. Add cache-control extensions like this:

    Click Ok to dismiss the dialog. Your HTTP Headers tab should have extensions listed.

    Set Cache-Control Extensions Programmatically

    I was in for a big surprise when I found documentation for HttpCachePolicy.AppendCacheExtension. You can accomplish what we’ve talked about in C# like this:

    Response.Cache.AppendCacheExtension(
            "post-check=900,pre-check=3600");

    Now, if you want to serve images with this cache policy (which is a good idea) you need to assign them to the ASP.NET ISAPI extension in IIS because by default ASP.NET is not configured to pass them through its HTTP pipeline.

    What I Don’t Know

    What happens if either pre-check or post-check is missing? I don’t know. What happens if post-check is greater than pre-check? I don’t know either. I found no documentation on MSDN that talks about it.

    Ajaxian » No More IE6 Background Flicker
    ajaxian.com/archives/no-more-ie6-background-flicke...

    No More IE6 Background Flicker

    Have you been bugged by IE background flicker?

    Cristi Balan talked about the issue and the solution:

    html {
      filter: expression(document.execCommand("BackgroundImageCache", false, true));
    }
     

    Cristi links to a blog posting by Dan Popa, who discovered the IE6 background image flicker fix. And while you're on Dan's site, check out a forensic analysis of the IE6 BackgroundImageCache command identifier.

    SQL

    Understanding SQL Server Full-Text Indexing
    www.developer.com/db/print.php/3446891
    Understanding SQL Server Full-Text Indexing
    By Mike Gunderloy
    December 13, 2004

    Microsoft SQL Server supports T-SQL, an implementation of ANSI standard SQL. T-SQL is designed to (among other things) search for matches in your data. For example, if you've created a table with a column named Notes you could construct these queries:

    
    SELECT * FROM MyTable WHERE Notes = 'Deliver Tuesday'
    SELECT * FROM MyTable WHERE Notes LIKE '%caution%'
    

    But what if you're not looking for an exact match, either to the full text of the column or a part of the column? That's when you need to go beyond the standard SQL predicates and use SQL Server's full-text search capabilities. With full-text searching, you can perform many other types of search:

    • Two words near each other
    • Any word derived from a particular root (for example run, ran, or running)
    • Multiple words with distinct weightings
    • A word or phrase close to the search word or phrase

    In this article, I'll show you how to set up and use full-text searching in SQL Server 2000, and give you a sneak peek of the changes that are coming in this area when SQL Server 2005 ships next year.

    Full-Text Indexing Architecture

    You might be a bit surprised to learn that SQL Server doesn't handle its own full-text indexing tasks. Any version of Windows that SQL Server will run on includes an operating system component named the Microsoft Search Service. This service provides indexing and searching capabilities to a variety of applications, including SQL Server, Exchange, and SharePoint.

    SQL Server uses an interface component, the SQL Server Handler, to communicate with the Microsoft Search Service. The Handler extracts data from SQL Server tables that have been enabled for full-text searching and passes it to the search service for indexing. Another component, the full-text OLE DB provider, gets invoked by SQL Server when you actually perform a full-text search. The provider takes the portion of the search that needs to be satisfied by the full-text index and passes it off to the Search Service for evaluation.

    You need to be aware of one consequence of this architecture: because the full-text indexes are not in your SQL Server database, they can't be backed up from within SQL Server. Instead, you need to backup the disk files created by the Search Service. You'll find these files located under Program Files\Microsoft SQL Server\MSSQL\FTDATA.

    Enabling Full-Text Indexing

    As you can probably guess, there's a certain amount of overhead involved in passing data back and forth between SQL Server and the Search Service. To speed things up, SQL Server doesn't pass any data to the Search Service unless you explicitly tell it to do so. After all, you might never want to do any full-text searches, in which case it would be silly to spend time indexing your data for them.

    To get started, you need to add a full-text catalog to your database. The easiest way to do this is to open SQL Server Enterprise Manager and expand the node for your database to find the Full-Text Catalogs node (if that node isn't present, check to make sure that the Microsoft Search Service is installed on the server). Right-click on the node and select New Full-Text Catalog. SQL Server will prompt you for a name and location for the catalog (and it will supply a default location). Name the catalog anything you like and click OK to create it.

    Next you need to tell SQL Server what data to include in the catalog. Again, you can do this in Enterprise Manager. Right-click on a table and select Full-Text Index Table, Define Full-Text Indexing on a Table. This will launch the SQL Server Full-Text Indexing Wizard. You need to make these choices to complete the wizard:

    1. Select a unique index on the table
    2. Select the columns to index. You can optionally specify a language to use for word breaking.
    3. Select the catalog to contain the index, or create a new catalog.
    4. Create a schedule to repopulate the index on a regular basis (this is also optional).

    When you finish the wizard, it will create the index for the table. But the index won't have any entries in it yet. Right-click on the table again anfd select Full-Text Index Table, Start Full Population to build the actual index

    Performing a Full-Text Search

    Now you're ready to actually do some searches. For these examples, I added a full-text index to the ProductName column in the Northwind Products table. Four T-SQL predicates are involved in full-text searching:

    • FREETEXT
    • FREETEXTTABLE
    • CONTAINS
    • CONTAINSTABLE

    FREETEXT is the easiest of these to work with; it lets you specify a search term but then tries to look at the meaning rather than the exact term when finding matches. For instance, here's a query using FREETEXT together with its results:

    
    SELECT ProductName
    FROM Products
    WHERE FREETEXT (ProductName, 'spread' )
    
    ProductName                              
    ---------------------------------------- 
    Grandma's Boysenberry Spread
    Vegie-spread
    
    (2 row(s) affected)
    

    As you can see, FREETEXT finds the word or words you give it anywhere in the search column. FREETEXTTABLE works like FREETEXT except that it returns its results in a Table object.

    CONTAINS (and CONTAINSTABLE, which works the same but delivers results in a table) offers a much more complex syntax for using a full-text indexed column:

    
    CONTAINS
        ( { column | * } , '< contains_search_condition >' 
        ) 
    
    < contains_search_condition > ::= 
            { < simple_term > 
            | < prefix_term > 
            | < generation_term > 
            | < proximity_term > 
            | < weighted_term > 
            } 
            | { ( < contains_search_condition > ) 
            { AND | AND NOT | OR } < contains_search_condition > [ ...n ] 
            } 
    
    < simple_term > ::= 
        word | " phrase "
    
    < prefix term > ::= 
        { "word * " | "phrase * " }
    
    < generation_term > ::= 
        FORMSOF ( INFLECTIONAL , < simple_term > [ ,...n ] ) 
    
    < proximity_term > ::= 
        { < simple_term > | < prefix_term > } 
        { { NEAR | ~ } { < simple_term > | < prefix_term > } } [ ...n ] 
    
    < weighted_term > ::= 
        ISABOUT 
            ( { { 
                    < simple_term > 
                    | < prefix_term > 
                    | < generation_term > 
                    | < proximity_term > 
                    } 
                [ WEIGHT ( weight_value ) ] 
                } [ ,...n ] 
            ) 
    

    For instance, you can search for one word "near" another this way:

    
    SELECT ProductName
    FROM Products
    WHERE CONTAINS(ProductName, '"laugh*" NEAR lager')
    
    ProductName                              
    ---------------------------------------- 
    Laughing Lumberjack Lager
    
    (1 row(s) affected)
    

    Note the use of "laugh*" to match any word starting with "laugh." You can also supply a weighted list of terms to CONTAINS, and it will prefer matches with a higher weight:

    
    SELECT ProductName
    FROM Products
    WHERE CONTAINS(ProductName, 'ISABOUT (stout weight (.8), 
       ale weight (.4), lager weight (.2) )' )
    
    ProductName                              
    ---------------------------------------- 
    Laughing Lumberjack Lager
    Steeleye Stout
    Sasquatch Ale
    Outback Lager
    
    (4 row(s) affected)
    

    Looking Forward to SQL Server 2005

    SQL Server 2005 features quite a number of changes and improvements in full-text searching:

    • A dedicated indexing service that works directly with SQL Serrver. This speeds up full-text operations and isolates SQL Server from changes to the search service made by other applications.
    • Data definition language (DDL) statements for creating and altering full-text catalogs and indexes.
    • Full-text queries against linked servers.
    • Full-text queries against arbitrary sets of columns (instead of just one column or all columns).
    • Specification of the language to be used for word-breaking in an index.
    • Integrated backup and restore for full-text catalogs.
    • Full-text indexing for XML data.
    • Integration with SQL Profiler and logging of index operations.

    If you were interested in full-text searching in SQL Server 2000 but ran into brick walls, take another look when the new version comes out. Microsoft's substantial work in this area means that full-text indexing and searching will be better than ever.

    Full-Text to the Rescue

    Many SQL Server problems can be solved without ever looking at full-text search. But it comes in very handy in one key scenario: when human beings are supplying search terms from their own head, instead of from a list. You may need to work at providing a good user interface for this facility, but if you have people searching through a large corpus of text, you should definitely consider full-text searching. The end result is likely to be a better application and happier users.

    Mike Gunderloy is the author of over 20 books and numerous articles on development topics, and the lead developer for Larkware. Check out his latest book, Coder to Developer from Sybex. When he's not writing code, Mike putters in the garden on his farm in eastern Washington state.

    Retrieving Scalar Data from a Stored Procedure
    By Scott Mitchell


    Introduction
    Virtually all ASP.NET applications of interest work with database data at some level, and one of the most common databases used in ASP.NET applications is Microsoft's own SQL Server database. With relational databases like SQL, commands are issued through the SQL syntax, which includes SELECT, INSERT, UPDATE, and DELETE statements, among others. One way to issue a command to a database from an ASP.NET application is to craft the SQL query in the application itself. Such queries are often called ad-hoc queries. The primary downside of ad-hoc queries is that they are hard to maintain - if you need to change your query you need to edit the string in your application, recompile, and redeploy.

    A better approach, in my opinion, is to use stored procedures. Stored procedures are pre-compiled functions that reside on the database server that can be invoked by name. This is similar to compartmentalizing programmatic functionality into methods. Stored procedures are not only more updateable than their ad-hoc counterpart, but also can be utilized by other applications. For example, you might have both an ASP.NET application and a Web services application that is driven on data from the same database. If you hard code your SQL queries in your source code, any changes will now require modifications in two places (as well as two places that now require recompilation and redeployment). However, by using stored procedures there's a single point that needs modification. (The debate between stored procedures and ad-hoc queries has been done in much greater detail in other venues; see Rob Howard's blog entry Don't use stored procedures yet? Must be suffering from NIHS (Not Invented Here Syndrome) for a pro-stored procedures slant, and Frans Bouma's entry Stored Procedures are Bad, M'Kay? for a look at why stored procedures aren't the end-all answer.)

    Stored procedures typically return resultsets, such as the results of a SELECT query. However, there are times when you may be getting back just scalar data from a stored procedure. For example, you might have a stored procedure that returns just the account balance for a particular customer, or one that returns the average age of all users in your database. When calling a stored procedure that INSERTs a new record into a table with an IDENTITY field, you may want to get back the ID for the newly inserted row.

    There are a couple of ways to get back scalar data from a SQL Server stored procedure. In this article we'll look at these various techniques along with how to fetch back the returned data in your ASP.NET code. Read on to learn more!

    Returning Data with a SELECT Statement
    Typically data is returned from a stored procedure using a SELECT statement, and typically the data returned is a resultset, consisting of multiple fields and records. For example, a stored procedure might be created to get all products in inventory, which might be accessed through the SQL query:

    CREATE PROCEDURE store_GetInventory AS
    
    SELECT InventoryID, ProductName, Price, UnitsOnStock
    FROM store_Inventory
    

    However, there's no reason why you can't return a simple scalar value. For example, if you were interested in the average price of all items in inventory - i.e., just a simple number, like $11.92 - you could return this scalar data using a SELECT statement:

    CREATE PROCEDURE store_GetAverageInventoryPrice AS
    
    SELECT AVG(Price) AS AveragePrice
    FROM store_Inventory
    

    Similarly, in stored procedures that insert a new record into a table that has an IDENTITY field, you can get the ID value of the newly inserted record through the SCOPE_IDENTITY() function. So, after INSERTing the new record you can simply return the value like so:

    CREATE PROCEDURE store_AddNewInventoryItem
    (
    	@ProductName	nvarchar(50),
    	@Price			money
    ) AS
    
    -- INSERT the new record
    INSERT INTO store_Inventory(ProductName, Price)
    VALUES(@ProductName, @Price)
    
    -- Now return the InventoryID of the newly inserted record
    SELECT SCOPE_IDENTITY()
    

    When returning scalar through a SELECT statement you can retrieve the data using the exact same technique used to retrieve a resultset. That is, you can, if you want, use a DataReader, DataTable, or DataSet. The only thing to keep in mind is that you're results will contain only one row with only one field. The following code would call the store_GetAverageInventoryPrice and grab back the scalar result:

    Dim myConnection as New SqlConnection(connection string)
    Dim myCommand as New SqlCommand("store_GetAverageInventoryPrice", myConnection)
    myCommand.CommandType = CommandType.StoredProcedure
    
    Dim reader as SqlDataReader = myCommand.ExecuteReader()
    
    'Read in the first record and grab the first column
    Dim avgPrice as Decimal
    If reader.Read() Then
      avgPrice = Convert.ToDouble(reader("AveragePrice"))
    End If
    

    This is a bit of overkill, though, thanks to the DataCommand's ExecuteScalar() method. The ExecuteScalar() method can be used in place of the ExecuteReader(), the difference being ExecuteScalar() returns a single Object instance as opposed to a DataReader. Using ExecuteScalar() the code would be simplified to:

    Dim myConnection as New SqlConnection(connection string)
    Dim myCommand as New SqlCommand("store_GetAverageInventoryPrice", myConnection)
    myCommand.CommandType = CommandType.StoredProcedure
    
    Dim avgPriceObject as Decimal = Convert.ToDecimal(myCommand.ExecuteScalar())
    

    (The above omits a check to see if the result is NULL. If there were no rows in store_Inventory or no rows with a non-NULL Price, the returned Object would be equal to DBNull.Value. Ideally you would either add such a check to the above code or edit the stored procedure to use ISNULL to convert any NULL result into a number (i.e., SELECT ISNULL(AVG(Price), 0.0) ...).)

    While the SELECT method just discussed provides an easy way to return a scalar value from a stored procedure it only works if the scalar value is the sole piece of data you want to return from the stored procedure. There are times, however, where you want to return a full resultset from the stored procedure along with some scalar value. The remaining two approaches we'll be looking at in this article address how to accomplish this feat.

    Using Output Parameters One way to retrieve scalar data in addition to a standard resultset from a stored procedure is to use one or more output parameters. An output parameter is a parameter that is passed into the SQL stored procedure, but whose value can be set in the stored procedure. This assigned parameter, then, is readable back from the application that called the stored procedure.

    To use an output parameter you need to indicate that the parameter is intended for output via the OUTPUT keyword. The following snippet shows a stored procedure that returns the set of inventory items through a SELECT statement and uses an output parameter to return the average price:

    CREATE PROCEDURE store_GetInventoryWithAveragePrice
    (
    	@AveragePrice	money	OUTPUT
    )
    AS
    
    SET @AveragePrice = (SELECT AVG(Price) FROM store_Inventory)
    
    SELECT InventoryID, ProductName, Price, UnitsOnStock
    FROM store_Inventory
    

    To access the value of an output parameter from your ASP.NET application you need to create a parameter object whose Direction property is set to Output. After you call the stored procedure the output parameter's value is accessible through the Value property, as the following code illustrates:

    Dim myConnection as New SqlConnection(connection string)
    Dim myCommand as New SqlCommand("store_GetInventoryWithAveragePrice", myConnection)
    myCommand.CommandType = CommandType.StoredProcedure
    
    'Create a SqlParameter object to hold the output parameter value
    Dim avgPriceParam as New SqlParameter("@AveragePrice", SqlDbType.Money)
    
    'IMPORTANT - must set Direction as Output
    avgPriceParam.Direction = ParameterDirection.Output
    
    'Finally, add the parameter to the Command's Parameters collection
    myCommand.Parameters.Add(avgPriceParam)
    
    'Call the sproc...
    Dim reader as SqlDataReader = myCommand.ExecuteReader()
    
    'Now you can grab the output parameter's value...
    Dim avgPrice as Decimal = Convert.ToDecimal(avgPriceParam.Value)
    

    (The same issue regarding NULLs applies here as in the previous example...)

    You are not limited to a single output parameter; additionally, you can have stored procedures with both input and output parameters.

    Using a Return Value
    The final technique I want to talk about for returning scalar values from a stored procedure is using return values. Whenever a stored procedure finishes executing, it always returns a return value. This return value is, by default, 0. You can use the RETURN statement yourself, however, to return a scalar integer value. For example, let's revisit the store_AddNewInventoryItem, but modify it to return the ID of the newly inserted row as a return value.

    CREATE PROCEDURE store_AddNewInventoryItem
    (
    	@ProductName	nvarchar(50),
    	@Price			money
    ) AS
    
    -- INSERT the new record
    INSERT INTO store_Inventory(ProductName, Price)
    VALUES(@ProductName, @Price)
    
    -- Now return the InventoryID of the newly inserted record
    RETURN SCOPE_IDENTITY()
    

    Note that the SCOPE_IDENTITY() value is being return via a RETURN statement now, whereas in the earlier example we used a SELECT.

    To retrieve the return value from a stored procedure use the same technique as with output parameters, the only difference being that you should use a Direction value of ReturnValue, as the following code snippet illustrates:

    Dim myConnection as New SqlConnection(connection string)
    Dim myCommand as New SqlCommand("store_GetInventoryWithAveragePrice", myConnection)
    myCommand.CommandType = CommandType.StoredProcedure
    
    'Create a SqlParameter object to hold the output parameter value
    Dim retValParam as New SqlParameter("@RETURN_VALUE", SqlDbType.Int)
    
    'IMPORTANT - must set Direction as ReturnValue
    retValParam.Direction = ParameterDirection.ReturnValue
    
    'Finally, add the parameter to the Command's Parameters collection
    myCommand.Parameters.Add(retValParam)
    
    'Call the sproc...
    Dim reader as SqlDataReader = myCommand.ExecuteReader()
    
    'Now you can grab the output parameter's value...
    Dim retValParam as Integer = Convert.ToInt32(retValParam.Value)
    

    That's all there is to it! As I mentioned earlier, you can only return integer values through the stored procedure's return type.

    Conclusion
    In this article we examined three ways to pass back scalar data from a stored procedure, along with the necessary code to process the returned value. You can use a SELECT statement, output parameter, or return value (assuming you want to pass back an integer value). When returning a scalar value via a SELECT statement you can read the resulting value using the ExecuteScalar() method. For output parameters and return values you need to create a parameter object with the proper Direction property value. Then, after you call the stored procedure, you can access the retrieved value through the parameter's Value property.

    Happy Programming!

  • By Scott Mitchell
  • You must precede all Unicode strings with a prefix N when you deal with Unicode string constants in
    support.microsoft.com/default.aspx/kb/239530

    You must precede all Unicode strings with a prefix N when you deal with Unicode string constants in SQL Server

    Article ID:239530
    Last Review:December 1, 2005
    Revision:6.0
    This article was previously published under Q239530

    SUMMARY

    When dealing with Unicode string constants in SQL Server you must precede all Unicode strings with a capital letter N, as documented in the SQL Server Books Online topic "Using Unicode Data". The "N" prefix stands for National Language in the SQL-92 standard, and must be uppercase. If you do not prefix a Unicode string constant with N, SQL Server will convert it to the non-Unicode code page of the current database before it uses the string.

    MORE INFORMATION

    This notation is necessary to provide backward compatibility with existing applications. For example, "SELECT 'Hello'" must continue to return a non-Unicode string because many applications will expect the behavior of SQL Server 6.5, which did not support Unicode data; the new syntax "SELECT N'Hello'" has been added to allow the passing of Unicode strings to and from SQL Server 7.0.

    Any time you pass Unicode data to SQL Server you must prefix the Unicode string with N. If your application is Unicode-enabled and sends data to SQL Server 7.0 as Unicode string constants without the N prefix, you may encounter a loss of character data. When SQL Server converts a Unicode string without the N prefix from Unicode to the SQL Server database's code page, any characters in the Unicode string that do not exist in the SQL Server code page will be lost. Note that this translation is not related to Autotranslation, OemToAnsi, or AutoAnsiToOem conversion, all of which occur on the client at the ODBC, OLEDB, or DB-Library layer.

    If your application does not send Unicode data to SQL Server and the client's ANSI code page matches the SQL Server code page, there is no need to prefix string constants with N, and you will not experience data loss as a result of omitting the prefix. However, SQL Server 7.0 allows you to select a Unicode collation during installation that is distinct from the sort order, and in some cases this can cause operations involving strings prefixed with N to have different results from those that do not have the prefix. For example, suppose that when you installed SQL Server 7.0, you selected a binary sort order (sort orders are used when comparing non-Unicode strings), and selected General Unicode as the Unicode collation (the Unicode collation is used for comparing Unicode strings). The expression comparing two non-Unicode strings ("ABC" = "abc") would return False since a capital letter "A" is not equivalent to a lower-case "a" according to a binary sort order. In contrast, the expression (N'ABC' = N'abc') would return True. Because the strings are prefixed with an N, they will be converted to Unicode and the Unicode collation will be used to compare them. Unlike the binary sort order, the General Unicode collation is case insensitive and would regard the two strings as equivalent.

    Note that if one of two string constant operands is prefixed with an N and the other is not, the non-Unicode string will be converted to Unicode and the Unicode collation will apply when comparing them. This behavior is explained in the SQL Server Books Online topic "Comparison Operators".
    Server-Side Programming with Unicode
    msdn2.microsoft.com/en-us/library/ms191313.aspx
    Server-Side Programming with Unicode 

    Updated: 17 July 2006

    To make a database Unicode-aware involves defining Unicode-aware client interactions in addition to using the nchar, nvarchar, and nvarchar(max) data types to define Unicode storage. You can define Unicode-aware client interactions by performing the following on the database server side:

    • Switch from non-Unicode data types to Unicode data types in table columns and in CONVERT() and CAST() operations.
    • Substitute using ASCII() and CHAR() functions with their Unicode equivalents, UNICODE() and NCHAR().
    • Define variables and parameters of stored procedures and triggers in Unicode.
    • Prefix Unicode character string constants with the letter N.
    Using UNICODE(), NCHAR(), and Other Functions

    The ASCII() function returns the non-Unicode character code of the character passed in. Therefore, use the counterpart UNICODE() function for Unicode strings where you would use the ASCII function on non-Unicode strings. The same is true of the CHAR function; NCHAR is its Unicode counterpart.

    Because the SOUNDEX() function is defined based on English phonetic rules, it is not meaningful on Unicode strings unless the string contains only the Latin characters A through Z and a through z.

    ASCII, CHAR, and SOUNDEX can be passed Unicode parameters, but these arguments are implicitly converted to non-Unicode strings. This could cause the possible loss of Unicode characters before processing, because these functions operate on non-Unicode strings by definition.

    Besides the UNICODE() and NCHAR() functions, the following string manipulation functions support Unicode wherever possible: CHARINDEX(), LEFT(), LEN(), UPPER(), LOWER(), LTRIM(), RTRIM(), PATINDEX(), REPLACE(), QUOTENAME(), REPLICATE(), REVERSE(), STUFF(), SUBSTRING(), UNICODE(). These functions accept Unicode arguments, respect the 2-byte character boundaries of Unicode strings, and use Unicode sorting rules for string comparisons when the input parameters are Unicode.

    Defining Parameters in Stored Procedures

    Defining parameters with a Unicode data type guarantees that client requests or input are implicitly converted to Unicode on the server and not corrupted in the process. If the parameter is specified as an OUTPUT parameter, a Unicode type also minimizes the chance of corruption on its way back to the client.

    In the following stored procedure, the variable is declared as a Unicode data type.

    CREATE PROCEDURE Product_Info
        @name nvarchar(40)
    AS
    SELECT p.ListPrice, v.Name
        FROM Production.Product p 
            INNER JOIN Purchasing.ProductVendor pv
                ON p.ProductID = pv.ProductID  
            INNER JOIN Purchasing.Vendor v
                ON pv.VendorID = v.VendorID
    WHERE p.Name = @name;
    Using the N Prefix

    Unicode string constants that appear in code executed on the server, such as in stored procedures and triggers, must be preceded by the capital letter N. This is true even if the column being referenced is already defined as Unicode. Without the N prefix, the string is converted to the default code page of the database. This may not recognize certain characters.

    For example, the stored procedure created in the previous example can be executed on the server in the following way:

    EXECUTE Product_Info @name = N'Chain'

    The requirement to use the N prefix applies to both string constants that originate on the server and those sent from the client.

    Understanding the WITH Clause | Jonathan Gennick
    gennick.com/?q=node/2

    Understanding the WITH Clause

    Submitted by JonathanGennick on 1 July 2003 - 12:00pm. DB2 | Oracle | SQL
    Recently I researched recursive SQL queries for an Oracle Technology Network (OTN) article on Oracle's CONNECT BY syntax. (By the way, that article is scheduled to appear on OTN sometime in July) While researching that article, someone at Oracle pointed me to the SELECT statement's WITH clause. WITH was introduced in the SQL:1999 standard, and made it's way into Oracle in Oracle9i Database Release 1. As defined in the standard, WITH enables you to do two things:
    • You can use WITH to write recursive queries

    • You can use WITH to factor subqueries out of a main query.

    I was only dimly aware of WITH, and hadn't given it much thought. That it could be used to write recursive queries was a surprise to me, probably because Oracle hasn't implemented that aspect of WITH. I decided to do some investigating to find out just what WITH was all about.

    Using WITH to Issue Recursive Queries

    Perhaps the driving force behind the introduction of WITH in the SQL standard was the need to issue recursive queries. Oracle has long (since version 2) supported such queries through its CONNECT BY syntax. For example, the following CONNECT BY query retrieves the definition of an automobile from a bill-of-mterials table:
    SQL> SELECT assembly_id, assembly_name, parent_assembly
      2  FROM bill_of_materials
      3  START WITH assembly_id=100
      4  CONNECT BY parent_assembly = PRIOR assembly_id;
    
    ASSEMBLY_ID ASSEMBLY_NAME           PARENT_ASSEMBLY
    ----------- ----------------------- ---------------
            100 Automobile
            110 Combustion Engine                   100
            111 Piston                              110
            112 Air Filter                          110
            113 Spark Plug                          110
            114 Block                               110
            115 Starter System                      110
            116 Alternator                          115
            117 Battery                             115
            118 Starter Motor                       115
            120 Body                                100
            121 Roof                                120
            122 Left Door                           120
            123 Right Door                          120
            130 Interior                            100
    
    If you look carefully at this output, you can see that an automobile is made up of a combustion engine, which in turn is made up of a piston, air filter, spark plug, block, and starter system. The starter system is in turn composed of an alternator, battery, and starter motor, and so forth. CONNECT BY gives you the ability to query a table that is recursively related to itself, and the results from a CONNECT BY query are generated in the hierarchical order shown here. When you write a CONNECT BY query, you can think of the START WITH clause as defining the set of root nodes to be returned by the query. The CONNECT BY clause then defines the "join" between parent and child rows. I explain this in more detail in my OTN article. Partly for grins, and partly to expand my horizons a bit, I decided to look at how the preceding query could be implemented using WITH instead of CONNECT BY. To do that, I had to use a database supporting recursive WITH, so I installed a copy of IBM DB2. After a good bit of head-scratching and reading of IBM's example queries, I came up with the following WITH-based solution to my bill-of-materials query:
    WITH recursiveBOM
       (assembly_id, assembly_name, parent_assembly) AS
    (SELECT parent.assembly_id,
            parent.assembly_name,
            parent.parent_assembly
    FROM bill_of_materials parent
    WHERE parent.assembly_id=100
    UNION ALL
    SELECT child.assembly_id,
           child.assembly_name,
           child.parent_assembly
    FROM recursiveBOM parent, bill_of_materials child
    WHERE child.parent_assembly = parent.assembly_id)
    SELECT assembly_id, parent_assembly, assembly_name
    FROM recursiveBOM;
    
    Wow! This is inscrutable. It took me a long time fathom this query. Let's take it a piece at a time:
    WITH recursiveBOM
       (assembly_id, assembly_name, parent_assembly) AS
    
    The WITH keyword defines the name recursiveBOM for the subquery that is to follow. Next comes a list of column aliases to use when referencing the results of the subquery. Oracle requires that you specify aliases in the subquery's SELECT, something I'll talk more about later. Next comes the first part of the named subquery:
    (SELECT parent.assembly_id,
            parent.assembly_name,
            parent.parent_assembly
    FROM bill_of_materials parent
    WHERE parent.assembly_id=100
    
    The named subquery is a UNION ALL of two queries. This, the first query, defines the starting point for the recursion. As in my CONNECT BY query, I want to know what makes up assembly 100, an automobile. Next up is the part that was most confusing to me:
    UNION ALL
    SELECT child.assembly_id,
           child.assembly_name,
           child.parent_assembly
    FROM recursiveBOM parent, bill_of_materials child
    WHERE child.parent_assembly = parent.assembly_id)
    
    This is where things get recursive, and confusing. The second query in the union joins bill_of_materials to the results of the named subquery. Notice how the WHERE clause strikingly resembles the CONNECT BY clause used in the Oracle version of this recursive query. The final part of the query is the main SELECT:
    SELECT assembly_id, parent_assembly, assembly_name
    FROM recursiveBOM;
    
    This SELECT does nothing more than to retrieve the results returned by named subquery. All the recursion happens in the subquery. The important thing to notice here is that the three column names in the main query's select-list match the three aliases specified near the beginning of the WITH clause. The aliases you specify for a named subquery are the names you must use when retrieving the results of that subquery. How does WITH's recursion work? It's important to form a clear mental-model of how a query such as this produces the results that it does.
    For more on mental models, see my article at:
    http://gennick.com/mental_models.html
    My first attempt at understanding the preceding WITH query was to think in terms of replacing the query *text* recursively. When I came across the text "recursiveBOM", I replaced it with the subquery text. The resulting mess that I envisioned in my mind looked as follows, with ellipses (...) indicating where further query text expansion would occur:
    WITH recursiveBOM
       (assembly_id, assembly_name, parent_assembly) AS
    (SELECT parent.assembly_id,
            parent.assembly_name,
            parent.parent_assembly
    FROM bill_of_materials parent
    WHERE parent.assembly_id=100
    UNION ALL
    SELECT child.assembly_id,
           child.assembly_name,
           child.parent_assembly
    FROM (...) parent, bill_of_materials child
    WHERE child.parent_assembly = parent.assembly_id)
    SELECT assembly_id, parent_assembly, assembly_name
    FROM (SELECT parent.assembly_id,
                 parent.assembly_name,
                 parent.parent_assembly
    FROM bill_of_materials parent
    WHERE parent.assembly_id=100
    UNION ALL
    SELECT child.assembly_id,
           child.assembly_name,
           child.parent_assembly
    FROM (...) parent, bill_of_materials child
    WHERE child.parent_assembly = parent.assembly_id);
    
    The above is not the way to understand a recursive WITH query. The recursion is not modeled in terms of the text. You have to think in terms of subquery *results*. Following is the mental model I'm currently using to understand this recursive WITH query: 1. The parent query executes:
       SELECT assembly_id, parent_assembly, assembly_name
       FROM recursiveBOM;
    
    This triggers execution of the named subquery. 2 The first query in the subquery's union executes, giving us a seed row with which to begin the recursion:
       SELECT parent.assembly_id,
              parent.assembly_name,
              parent.parent_assembly
       FROM bill_of_materials parent
       WHERE parent.assembly_id=100
    
    The seed row in this case will be for "Automobile". Let's refer to the seed row from here on out as the "new results", new in the sense that we haven't finished processing them yet. 3 The second query in the subquery's union executes:
       SELECT child.assembly_id,
              child.assembly_name,
              child.parent_assembly
       FROM recursiveBOM parent, bill_of_materials child
       WHERE child.parent_assembly = parent.assembly_id
    
    This is where things get interesting. Notice the recursive reference to recursiveBOM. That plays into Step 4. 4. The recursive reference to recursiveBOM is replaced by the query results. And what are the query results? They are:
    • The new results ("Automobile" the first time)

    • The results from executing this query using the new results in place of recursiveBOM. And what are the results from that? They are:

      • The new results (brought in by the join)

      • The results from executing this query using the new results in place of recursiveBOM. And what are the results from that? They are:

        • The new results (brought in by the join)

        • The results from executing this query using the new results in place of recursiveBOM ...

    Do you see the recursion here? It took me most of a day to get to the point where I felt comfortable that I understood this. Hopefully you'll catch on more quickly than I did. By the way, the recursion stops when a join fails to bring back any more new rows. Oracle doesn't support the use of WITH to write recursive queries, so if you're using only Oracle, everything I've discussed so far is somewhat academic. However, WITH is part of the ANSI/ISO SQL standard, and it's good to be familiar with what the SQL standard calls for as opposed to what Oracle implements. Who knows? Someday you may encounter a recursive WITH in code written for another database, and now you're equipped to understand it.

    Factoring Out Subqueries

    The factoring out of subqueries is one use of WITH that Oracle does support, and that use has positive implications for performance. Consider the following SQL query which uses a non-correlated subquery to retrieve all parts with inventories greater than the average:
    SELECT part_number, (SELECT AVG(current_inventory)
                         FROM part)
    FROM part
    WHERE current_inventory > (SELECT AVG(current_inventory)
                               FROM part);
    
    This query uses the same subquery twice, once to return the average inventory along with each result row, and again to determine the average inventory for use in the WHERE clause. Using WITH, you can factor out the subquery:
    WITH partavg AS (SELECT AVG(current_inventory) avg_inventory
                     FROM part)
    SELECT part_number, (SELECT avg_inventory FROM partavg)
    FROM part
    WHERE current_inventory > (SELECT avg_inventory
                               FROM partavg);
    
    The main query still contains two subqueries, but each of those subqueries is a SELECT from the named subquery partavg. This gives the optimizer enough information to determine that the partavg subquery needs to be executed only once. Notice here that I've named the column returned by the partavg query by specifying avg_inventory as a column alias within the named subqueries SELECT clause. This is different from the IBM DB2 approach you saw earlier. To see how these queries executed, I connected to my test database using SQL*Plus, issued the SQL*Plus command SET AUTOTRACE TRACEONLY, and then executed each query. Following are the two execution plans:
     Execution Plan
    ---------------------------------------------------------
      0      SELECT STATEMENT Optimizer=CHOOSE
      1    0   FILTER
      2    1     TABLE ACCESS (FULL) OF 'PART'
      3    1     SORT (AGGREGATE)
      4    3       TABLE ACCESS (FULL) OF 'PART'
    
    Execution Plan
    ---------------------------------------------------------
      0      SELECT STATEMENT Optimizer=CHOOSE
      1    2     RECURSIVE EXECUTION OF 'SYS_LE_2_0'
      2    0   TEMP TABLE TRANSFORMATION
      3    2     FILTER
      4    3       TABLE ACCESS (FULL) OF 'PART'
      5    3       VIEW
      6    5         TABLE ACCESS (FULL) OF 'SYS_TEMP_0FD9D660B_4B6F5C4'
    
    It's interesting to notice that the first execution plan does not indicate a double execution of the subquery. I'll come back to this point shortly, because the execution statistics make it rather obvious that the subquery is, in fact, being executed twice. The second execution plan is from the WITH version of the query. You can see the effects of WITH in the plan. The "RECURSIVE EXECUTION OF 'SYS_LE_2_0'" no doubt represents execution of the named subquery. The results are stored in a temporary table named SYS_TEMP_0FD9D660B_4B6F5C4, and that table feeds into the processing of the main query. How do I know that the subquery is executed twice the first time, but only once when it's factored out using WITH? The execution statistics provide a good clue. Look at the following two sets of statistics, and focus on the number of consistent gets in each set:
    Statistics
    ---------------------------------------------------------
             1  recursive calls
             2  db block gets
          9427  consistent gets
             0  physical reads
         53880  redo size
       2290609  bytes sent via SQL*Net to client
        572834  bytes received via SQL*Net from client
          4371  SQL*Net roundtrips to/from client
             0  sorts (memory)
             0  sorts (disk)
         65536  rows processed
    
    Statistics
    ---------------------------------------------------------
            69  recursive calls
            10  db block gets
          7141  consistent gets
             2  physical reads
          1264  redo size
       2290603  bytes sent via SQL*Net to client
        572834  bytes received via SQL*Net from client
          4371  SQL*Net roundtrips to/from client
             0  sorts (memory)
             0  sorts (disk)
         65536  rows processed
    
    The first set of statistics is from the first query, which specifies the same subquery redundantly. The execution of that query against my test data required 9427 consistent gets. (A "consistent get" represents the logical read of a block from the database buffer cache) The second set of statistics is from the WITH version of the query, and required only 7141 consistent gets. Clearly the WITH version of the query is doing less work, and I can only attribute that to the fact that the subquery is being executed once, not twice. It's a bit disappointing that the execution plan doesn't make that crystal clear.

    Correlated Subqueries

    The previous example was easy, because it used a non-correlated subquery, one that did not reference values from the parent query. It's trivial to factor out a non-correlated subqueries. But what if your subquery is correlated, and references one or more values from the main query. What then? It turns out you can still benefit from WITH, but you may have to think just a bit harder. The following query is a slight revision of the part query shown earlier, but this time, rather than compare each part's inventory to the average of all inventories, it compares each part's inventory to the average of all inventories of parts made by the same manufacturer. Thus, the subqueries are correlated, referencing the manufactured_by value from the parent row.
    SELECT p1.part_number,
           (SELECT AVG(p2.current_inventory)
            FROM part p2
            WHERE p2.manufactured_by
                  = p1.manufactured_by) avg_inventory
    FROM part p1
    WHERE p1.current_inventory
          > (SELECT AVG(p2.current_inventory)
             FROM part p2
             WHERE p2.manufactured_by=p1.manufactured_by);
    
    WITH clause subqueries can't be correlated. I can't factor out these subqueries by copying them as-is into a WITH clause. It isn't that simple this time. However, after thinking about things a bit, I realized that I was after the average part inventory for each manufacturer, and that I could generate those values all at once using a simple GROUP BY query:
    SELECT manufactured_by,
           AVG(current_inventory) avg_inventory
    FROM part
    GROUP BY manufactured_by;
    
    This subquery became the basis for the WITH version of my correlated part query. Rather than compute the average inventory for a given manufacturer anew for each part, I can generate all the manufacturer-specific part inventory averages just once, and then reference those results from the main query. Here's what I came up with:
    WITH partavg AS (SELECT manufactured_by,
                     AVG(current_inventory) avg_inventory
                     FROM part
                     GROUP BY manufactured_by)
    SELECT part_number,
           (SELECT avg_inventory
            FROM partavg
            WHERE part.manufactured_by
                  = partavg.manufactured_by) avg_inventory
    FROM part
    WHERE part.current_inventory
          > (SELECT avg_inventory
             FROM partavg
             WHERE part.manufactured_by
                   = partavg.manufactured_by)
    
    Notice that I didn't totally eliminate subqueries from my main query. What I did eliminate from the main query is the constant summarizing and averaging of data. The WITH subquery computes part inventory averages for each manufacturer just once, placing those averages into a temporary table. The subqueries in the main query now select from that much smaller, pre-aggregated temporary table. The results are telling. I won't show the execution plans, because they are similar to the previous two plans, but look at the statistics:
    Statistics
    ----------------------------------------------------------
              0  recursive calls
              0  db block gets
          16794  consistent gets
              0  physical reads
              0  redo size
        2934767  bytes sent via SQL*Net to client
         572834  bytes received via SQL*Net from client
           4371  SQL*Net roundtrips to/from client
              0  sorts (memory)
              0  sorts (disk)
          65536  rows processed
    
    Statistics
    ----------------------------------------------------------
              4  recursive calls
              7  db block gets
           7139  consistent gets
              1  physical reads
            520  redo size
        2934767  bytes sent via SQL*Net to client
         572834  bytes received via SQL*Net from client
           4371  SQL*Net roundtrips to/from client
              1  sorts (memory)
              0  sorts (disk)
          65536  rows processed
    
    The number of consistent gets issued by the first version of the query, the one with the redundant, correlated subqueries, is 16,794. Look at the improvement in the WITH version of the query, which issued only 7139 consistent gets. That's less than half the work! Using WITH in this case leads to a dramatic decrease in the amount of logical I/O, and decreasing logical I/O is one key to improving performance, both for the database as a whole and for the query in question. WITH can be an important tool in your query-writing arsenal. Anytime you find yourself using the same subquery twice, give some thought to using WITH to factor out that subquery, thus reducing the amount of logical I/O your query must perform. You can also use WITH to factor out non-redundant subqueries in order to make a main query more readable. Be sure to weigh the benefits of using WITH, against the lack of compatibility with older releases of Oracle, because WITH is not available prior to Oracle9i.

    Acknowledgments

    I'd like to thank Jim Melton of Oracle Corporation, who is also the editor of the ANSI/ISO SQL Standard, for his long-suffering patience when it came to answering my many, many questions about the recursive use of WITH. If my explanation of recursive WITH execution has any clarity, it's largely due to Jim's patient explanations.
    ScottGu's Blog : App_Offline.htm and working around the "IE Friendly Errors" feature
    weblogs.asp.net/scottgu/archive/2006/04/09/442332....

    App_Offline.htm and working around the "IE Friendly Errors" feature

    I posted the slides+demos from my ASP.NET 2.0 Tips and Tricks talk online last week from the ASP.NET Connections Conference in Orlando.  One of the new features I talked about was the "App_Offline.htm" feature in ASP.NET 2.0, which provides a super convenient way to bring down an ASP.NET application while you make changes to it (for example: updating a lot of content or making big changes to the site where you want to ensure that no users are accessing the application until all changes are done).

    The way app_offline.htm works is that you place this file in the root of the application.  When ASP.NET sees it, it will shut-down the app-domain for the application (and not restart it for requests) and instead send back the contents of the app_offline.htm file in response to all new dynamic requests for the application.  When you are done updating the site, just delete the file and it will come back online.

    One thing I pointed out in the talk that you want to keep an eye on is a feature of IE6 called "Show Friendly Http Errors".  This can be configured in the Tools->Internet Options->Advanced tab within IE, and is on by default with IE6.  When this is on, and a server returns a non HTTP-200 status code with less than 512 bytes of content, IE will not show the returned HTML and instead substitutes its own generic status code message (which personally I don't think is super friendly <g>).

    So if you use the app_offline.htm feature, you should make sure you have at least 512 bytes of content within it to make sure that your HTML (instead of IE's friendly status message) shows up to your users.  If you don't want to have a lot of text show-up on the page, one trick you can use is to just add an html client-side comment with some bogus content to push it over 512 bytes.  For example:

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    <html xmlns="http://www.w3.org/1999/xhtml" >

    <head>

        <title>Site Under Construction</title>

    </head>

    <body>

        <h1>Under Construction</h1>

     

    <