Thursday, September 24, 2009

Set active tab on failed validation with ajax tabcontainer

Ajax Tab Container is one of the coolest control in ASP.NET but it has one problem when you're using mulitple validation controls on different tabs and on click of your submit button you need to set focus on the tab on which validation failed. Following javascript is useful to achive this:

function RunValidationsAndSetActiveTab()
{
if (typeof (Page_Validators) == "undefined") return;
try
{
var noOfValidators = Page_Validators.length;
for (var validatorIndex = 0; validatorIndex < noOfValidators; validatorIndex++)
{
var validator = Page_Validators[validatorIndex];
if(validator.validationGroup == 'CategoryGroup')
{
ValidatorValidate(validator);
if (!validator.isvalid)
{
var tabPanel = validator.parentNode.control;
if(typeof (tabPanel) != "undefined")
{
var tabContainer = tabPanel.get_owner();
tabContainer.set_activeTabIndex(tabPanel.get_tabIndex());
break;
}
}
}
}
}
catch (Error)
{
}
}


Please notice that we're using parentNode instead of parentElment because firefox doesn't recognize parentElement.

In the button that is used to submit all the tabs at once where each tab has set of validations you can call the above method on click of button on client side such as:

OnClientClick="RunValidationsAndSetActiveTab()"

Happy Programming!!!

Wednesday, July 15, 2009

Add nodes dynamically to the SiteMap For Your ASP.NET Site

The Web.sitemap file stores only static URLs, so if you have a page like this:
http://yoursite.com/news.aspx?view=4

Your sitemap breadcrumb will not reflect the fact that you are viewing a an actual news item. If you're like me, you also reflect the breadcrumb in the title of your page. So instead of seeing:
Breadcrumb: Home > News > A headline! Title: A headline! : News : My Site
You see:
Home > News News : My Site
Read on for how to solve it.

Solution
How do you fix it? I found a
great post on how to nest the sitemap in a master-detail style and then adjust the parent node's URL, but I wanted to actually add a new node because I was viewing and listing news on the same page.
Here's how you do it:

private string _mHeadline = string.Empty;

protected void Page_Load(object sender, EventArgs e)
{

// Here you set the page title to whatever dynamic thing
// you're loading.
_mHeadline = "A test";


SiteMap.SiteMapResolve += SiteMapResolve;
}

protected void Page_Unload(object sender, System.EventArgs e)
{
// Remove this specific handler once the page is done.
// Otherwise it will get called on other pages.
SiteMap.SiteMapResolve -= SiteMapResolve;
}

protected SiteMapNode SiteMapResolve(object sender, SiteMapResolveEventArgs e)
{

SiteMapNode cn = SiteMap.CurrentNode.Clone(true);
SiteMapNode newNode = default(SiteMapNode);

// "viewnews" can be changed to whatever you want. It's just a key.
if (_mViewTitle != string.Empty) {
newNode = new SiteMapNode(SiteMap.Provider, "viewnews", Request.Url.PathAndQuery, _mHeadline);
newNode.ParentNode = cn;
}
else {
newNode = cn;
}

return newNode;
}

So I left out how I was getting the title of the news headline because I assume you know how to do that. The place that is interesting is the SiteMapResolve function. I create a new node and set its parent to the current node, which successfully has the SiteMapPath show the correct breadcrumb:
Home > News > A headline!

A couple notes:

First, I remove the handler once the page is complete because otherwise the function will get called every time the SiteMap resolves itself, which is not what we want!
Second, you may need to touch the web.config (just remove a line and undo it) and save it to have your application recycle itself, otherwise your changes might not show up.
Hope this helps somebody. Happy Programming!!!

Reference:

http://blog.lib.umn.edu/ayubx003/dividebyzero/2009/01/10/how_to_programmatically_add_no.html#more


Thursday, July 9, 2009

VS Debug Problem with IE8

How VS debugger could be crashed with IE8?

If you opened multiple instances of IE8 and you attempt to debug your project, you mostly will have the issue where VS debugger just stops and ignores your break points!

Why was that?

Well, IE 8 has a feature called Loosely-Coupled Internet Explorer (LCIE) which results in IE running across multiple processes.
http://www.microsoft.com/windows/internet-explorer/beta/readiness/developers-existing.aspx#lcie

Older versions of the Visual Studio Debugger get confused by this and cannot figure out how to attach to the correct process.

To overcome this issue, you need to disable the process growth feature of LCIE by follow the below steps:

1) Open RegEdit
2) Browse to HKEY_LOCALMACHINE -> SOFTWARE -> Microsoft -> Internet Explorer -> Main
3) Add a dword under this key called TabProcGrowth
4) Set TabProcGrowth to 0

If you run into the same problem on Vista or newer, you will also need to turn off protected mode.

And then go a head and start debugging your code :)

Reference:
http://weblogs.asp.net/abdullaabdelhaq/archive/2009/06/01/VS-Debug-Problem-with-IE8.aspx

Thursday, April 30, 2009

Tip/Trick: Handling Errors with the UpdatePanel control using ASP.NET AJAX

Luis Abreu is an ASP.NET MVP who has a great blog on the http://msmvps.com blog site. Earlier today he posted a great tutorial post that describes how to use some of the new features in the ASP.NET AJAX Beta1 release to add more robust error handling into your application. I highly recommend reading and bookmarking it for future use.

Error handling in an AJAX world can often be tricky -- especially when AJAX call-backs are taking place and a mixture of client and server code is running within an application. In its most recent release, the control that comes with ASP.NET AJAX now has much more robust error handling and reporting features for you to use.

Specifically:
1) You can now handle the "OnAsyncPostBackError" event on the control to catch and/or modify an error message that occurs during the processing of an AJAX postback callback on the server.
2) You can now set the "AllowCustomErrors" property on the control to enable the standard ASP.NET custom error support to be enabled to redirect a user automatically to an error page when an error occurs (even if it is happening during an AJAX postback).
3) You can now optionally handle client-side JavaScript events on the page to intercept any error message sent back from the server, and perform custom client-side actions as a result (for example: to output the error message to a nicely formatted section instead of performing a pop-up message).

Read all about how to take advantage of the above new features from Luis' great tutorial here.

Hope this helps,

Viresh Shah

Tracking User Activity in ASP.NET

Introduction

I like data. I go gaga over measurable metrics. Nothing makes me happier than storing information and then seeing it expressed in tables of numbers and colorful charts. Whenever I work on a web application I am always looking for interesting data to record and analyze, and the most interesting (and potentially profitable) data that every website owner should track are usage statistics. Web server log files and online tools like Google Analytics provide an array of useful metrics, including how many unique visitors accessed your site, what pages were the most popular, what days of the week and hours of the day represent peak demand, and so forth.

Many ASP.NET web applications support user accounts, enabling visitors to create an account and sign in to the site. With a little bit of effort you can track the activity of your logged on users. This can include recording activities such as what pages were visited as well as what actions were performed. Consider a page that allows a user to manage his profile. When first arriving at this page the activity log might add an entry like "Visiting the User Profile page." After updating his e-mail address, the activity log might record, "Changed e-mail address from scott@example.com to mitchell@example.com." Such usage tracking offers a deeper level of analysis than is possible with log files or online visitor statistic tools. Instead of data that report total number of visitors or how the average user is interacting with the site, user activity tracking can provide a very detailed view of how a particular individual is using the application and what actions he is performing while signed on to the site.

This article examines how to record your users' activities in a database table and display this information in a web page. A complete, working demo application that shows these techniques in action is available for download.

ASP.NET's Membership system makes it easy to create and manage user accounts. Many websites that use Membership are configured to use SqlMembershipProvider, a Membership provider that ships with the .NET Framework and stores user account information in a Microsoft SQL Server database. The demo application for this article uses SqlMembershipProvider, storing user account information along with the user activity log in a Microsoft SQL Server 2008 Express Edition database file (ASPNETDB.mdf), which you will find in the application's App_Data folder.

A Look at the Membership System's User Tracking Implementation

One of the lesser known features of ASP.NET's Membership system is that it has a built-in mechanism to track the last date and time each user has accessed the system. Each user account has a LastActivityDate property that records this information; the SqlMembershipProvider stores this value in the aspnet_Users table's LastActivityDate column in Coordinated Universal Time (UTC). This LastActivityDate value is automatically updated whenever a user signs in and whenever their user account information is accessed via the Membership.GetUser method. The LastActivityDate is used by the Membership system to determine how many users are online - the Membership.GetNumberOfUsersOnline method returns the number of users whose LastActivityDate is within a certain window of the current date and time (15 minutes, by default).

The Membership system's user tracking implementation is pretty limited as it only specifies the last date and time a user was active on the site. It does not indicate what the user was doing at that time or provide any sort of activity history. The activity logging system presented in this article overcomes these limitations.

Designing the User Activity Log Database Table

The first step in building any analysis tool is to determine what information to track. Different website usage analytic tools capture different information: web server logs typically record the filename of each requested page, the querystring, the date and time, and the HTTP status code, whereas online tools capture information of interest to the sales and marketing departments: visit durations, the geographical locations of the site's visitors, the number of unique visitors, entry and exit pages, and so on.

What information do we need to record when tracking the online activity of a website's logged on users? At a minimum we would need to log:

The activity being performed
The user performing the activity
The date and time of activity
The page being visited when the activity is performed

The ActivityLogID is of type uniqueidentifier and uniquely identifies each log entry. The UserId column identifies the user who performed the activity. (The UserId column in the aspnet_Users table is what uniquely identifies each user in the SqlMembershipProvider user store.) The Activity column describes the activity performed; PageUrl is the URL of the page where the activity was performed. Finally, ActivityDate is the date and time (in UTC) that the activity was performed.
The ActivityLog table is designed to have a record added for each activity performed by the user. Depending on the popularity of your website, this table can grow to include tens of thousands if not millions of records. You may want to consider implementing some mechanism to remove records older than a certain date, such as all activity log entries more than three months old. This could be accomplished by a SQL Job that executes nightly.

Logging User Activity Web server log files automatically record each requested page; online usage statistic tools use cookies to track the pages users visit. Both of these logging mechanisms, once setup and configured, track visits to the site automatically without needed intervention from the web developer. The activity log is more flexible as it can be used to track any "activity," which may be page visits or user-instigated actions. Therefore, logging a user activity to the database involves writing code.

To help facilitate this process I created a custom base page class named BasePage that extends the System.Web.UI.Page class. BasePage includes a method named LogActivity(activity, recordPageUrl) that writes a record to the ActivityLog table with the specified activity and, if specified, the URL of the currently visited page.
The LogActivity method's code follows:
protected void LogActivity(string activity, bool recordPageUrl)
{
if (Request.IsAuthenticated)
{
// Get information about the currently logged on user
MembershipUser currentUser = Membership.GetUser(false);
if (currentUser != null)
{
Guid userId = (Guid)currentUser.ProviderUserKey;
// Log the activity in the database
using (SqlConnection myConnection = new
SqlConnection(ConfigurationManager.
ConnectionStrings["MembershipConnectionString"].ConnectionString))
{
SqlCommand myCommand = new SqlCommand();
myCommand.CommandText = "usp_LogUserActivity";
myCommand.CommandType = CommandType.StoredProcedure;
myCommand.Connection = myConnection;
myCommand.Parameters.AddWithValue("@UserId", userId);
myCommand.Parameters.AddWithValue("@Activity", activity);
if (recordPageUrl)
myCommand.Parameters.AddWithValue("@PageUrl", Request.RawUrl);
else
myCommand.Parameters.AddWithValue("@PageUrl", DBNull.Value);
myConnection.Open();
myCommand.ExecuteNonQuery();
myConnection.Close();
}
}
}
}

The method starts by determining if the user visiting the page is authenticated. If so, it gets the user's information via the Membership class's GetUser method. If a user is returned, the stored procedure usp_LogUserActivity is called, passing in values for the @UserId, @Activity, and @PageUrl parameters. Note that if recordPageUrl is false, the @PageUrl parameter is set to a database NULL value; if it is true, the @PageUrl parameter is assigned the raw URL of the currently requested page, which includes the directories, filename, and querystring of the requested web page (i.e., /MyApp/Users/Default.aspx?ID=2).

The usp_LogUserActivity stored procedure starts by updating the LastActivityDate column in the aspnet_Users table. As a result, adding an entry to the user activity log is tantamount to retrieving the user's information through the Membership system. Following the update to the LastActivityDate, the usp_LogUserActivity stored procedure inserts a record into the ActivityLog table. This update and insert are atomic as they are performed under the umbrella of a transaction. For background on transactions and using SQL Server's TRY...CATCH blocks see Maintaining Database Consistency with Transactions and TRY...CATCH in SQL Server.
ALTER PROCEDURE dbo.usp_LogUserActivity
(
@UserId uniqueidentifier,
@Activity nvarchar(100),
@PageUrl nvarchar(100)
)
AS
BEGIN TRY
BEGIN TRANSACTION -- Start the transaction
DECLARE @CurrentTimeUtc datetime
SET @CurrentTimeUtc = getutcdate()
-- Update the LastActivityDate in aspnet_Users
UPDATE dbo.aspnet_Users
SET LastActivityDate = @CurrentTimeUtc
WHERE @UserId = UserId
-- Insert activity record for user
INSERT INTO ActivityLog(UserId, Activity, PageUrl, ActivityDate)
VALUES(@UserId, @Activity, @PageUrl, @CurrentTimeUtc)
-- If we reach here, success!
COMMIT
END TRY
BEGIN CATCH
-- Whoops, there was an error
IF @@TRANCOUNT > 0
ROLLBACK
-- Raise an error with the details of the exception
DECLARE @ErrMsg nvarchar(4000), @ErrSeverity int
SELECT @ErrMsg = ERROR_MESSAGE(),
@ErrSeverity = ERROR_SEVERITY()
RAISERROR(@ErrMsg, @ErrSeverity, 1)
END CATCH

With the BasePage class complete, the final step is to have the ASP.NET pages in the site derive from BasePage (rather than from System.Web.UI.Page). Once this has been done you can call the LogActivity method from any page. For example, the homepage (~/Default.aspx) has the following code for its Page_Load event handler:
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
base.LogActivity("Visiting the homepage...", true);
}
}

The LogActivity method can be called from any event handler in those ASP.NET pages that derive from BasePage. Call LogActivity from the Page_Load event handler to log information when a page is first visited. You can additionally call LogActivity when the user clicks a button to log that they've performed a particular action (such as editing or deleting a record from some database table). All of the ASP.NET pages in the demo application derive from the BasePage class, and most include at least one call to the LogActivity method.

Displaying a Particular User's Activity History The activity log provides a complete history of each user's activity on the site. The demo application includes a page named ActivityHistoryByUser.aspx that displays the complete history in a paged grid for a particular user.

The GridView controls used in the web pages in this tutorial use default paging, which is easy to implement but inefficient when paging through a large number of records. Because the activity log may contain thousands of records for each user account, the GridView controls should be retooled to use custom paging unless some mechanism is put into place to periodically cull old activity log entries from the table or if only recent activity log records are displayed in the grid. For more information on custom paging see Custom Paging in ASP.NET.
Each activity in the left column is displayed as a hyperlink that links to the activity's PageUrl value (if such a value exists). The Last Updated column shows the date the activity was performed. For activities older than a week, the date the activity was performed is displayed. If the activity occurred more recently then a human-friendly message, such as "2 days ago" or "6 minutes ago," is displayed in place of the date and time. (This display formatting is handled by the FormatLastUpdatedDate method in the BasePage class.)

The grid is populated by the records returned from the usp_GetUserActivityLog stored procedure. As the following markup shows, this stored procedure returns the ActivityLog entries for a particular user ordered from the most recent entries to the oldest.
ALTER PROCEDURE dbo.usp_GetUserActivityLog
(
@UserID uniqueidentifier
)
AS
DECLARE @CurrentDateUtc datetime
SET @CurrentDateUtc = getutcdate()
SELECT ActivityLogID, Activity, PageUrl, ActivityDate, @CurrentDateUtc as CurrentDate
FROM ActivityLog
WHERE UserID = @UserID
ORDER BY ActivityDate DESC

Viewing the Online Users and their Last Performed Activity Many websites that support user accounts have a page that lists what users are currently online and what activity they last performed. As noted earlier, the Membership system automatically records each user's last active date and time and provides a method for determining how many users have been active within a specified time window. The Membership's built-in system does not include what activity the user last performed, but this information is captured by the ActivityLog table.

I added a stored procedure to the database named usp_GetCurrentActivityForOnlineUsers that returns the list of users who are currently online along with their most recent activity. This stored procedure takes in a single input parameter, @MinutesSinceLastInactive, which is the number of minutes that has elapsed since a user has been active in the system and is still considered "online." For example, a value of 15 means that any user whose LastActivityDate is within 15 minutes of the current date and time is considered "online," whereas those users whose LastActivityDate is outside of this window are considered "offline."

The usp_GetCurrentActivityForOnlineUsers stored procedure starts by determining what time is the cutoff for a user to be considered "online." It then queries the aspnet_Users and ActivityLog tables to retrieve the username of online users along with information about their last activity.
ALTER PROCEDURE
dbo.usp_GetCurrentActivityForOnlineUsers
(
@MinutesSinceLastInactive int
)
AS
DECLARE @CurrentTimeUtc datetime
SET @CurrentTimeUtc = getutcdate()
DECLARE @DateActive datetime
SELECT @DateActive = DATEADD(minute, -(@MinutesSinceLastInactive), @CurrentTimeUtc)
SELECT act.UserId,
u.UserName,
act.Activity,
act.PageUrl,
act.ActivityDate,
@CurrentTimeUtc as CurrentDate
FROM dbo.aspnet_Users u(NOLOCK)
INNER JOIN dbo.ActivityLog act(NOLOCK) ON
act.UserId = u.UserId
WHERE u.LastActivityDate > @DateActive AND
act.ActivityDate = u.LastActivityDate
ORDER BY act.ActivityDate DESC

Conclusion

Web server log files and online usage analysis tools are helpful in determining and evaluating macroscopic usage patterns. Unfortunately, these tools cannot provide a detailed view of how a particular user is interacting with your site. Nor can they provide live, up to the second activity information that can be used in your application to show who is currently online and what they are doing. Such deep usage analysis and real-time statistics are possible on websites that support user accounts.

ASP.NET's Membership system greatly simplifies the process of setting up, creating, and managing user accounts. However, the Membership system only tracks when each user's last activity was on the site; it does not log the activity performed or maintain an activity history. As examined in this article, it is possible to build your own user activity log with a little bit of elbow grease.

Happy Programming!
Reference:

Saturday, February 7, 2009

Copy/Insert row from one sheet to another sheet in Excel using Interop.Excel in .NET

I was just trying to copy row in one excel file to another excel using Interop.Excel from .NET. I tried a lot but couldn't make it. It looks very simple at first view but normal Rows.Insert doesn't work in all cases. I googled a lot and then after a long time I found one solution to achieve this.

Have a look at following code to achieve this(if you're using interop.excel).

Dim xlApplication As New Excel.Application
xlApplication.Visible = False
Dim xlBook As Workbook
Dim xlBook_New As Workbook
Try
xlBook = xlApplication.Workbooks.Open("C:\Source.xls")
xlBook_New = xlApplication.Workbooks.Open("C:\Destination.xls")

xlBook.Worksheets("" & "Sheet1" & "").activate()
Dim xlSheet1 As Worksheet = xlBook.ActiveSheet
xlSheet1.Range("A1", "CP5").Select()
xlSheet1.Range("A1", "CP5").Copy()
xlApplication.Selection.copy()
xlBook_New.Worksheets("Sheet1").activate()
Dim xlSheet1_new As Worksheet = xlBook_New.ActiveSheet
xlSheet1_new.Activate()
Dim rng As Excel.Range = xlSheet1_new.Range("A2")
rng.EntireRow.Insert(XlInsertShiftDirection.xlShiftDown, XlInsertFormatOrigin.xlFormatFromRightOrBelow)

'Remove 3rd row
Dim rng1 As Excel.Range = xlSheet1_new.Range("A1").Offset(3, 0)
rng1.EntireRow.Delete(XlDeleteShiftDirection.xlShiftUp)

xlSheet1_new.Name = "Sheet1"
xlSheet1_new.Columns.AutoFit()

Catch ex As Exception
MsgBox(ex.Message)
Finally
Dim o As New Object
Clipboard.SetDataObject(0)
xlBook.Save()
xlBook.Close(False, Nothing, False)
xlBook = Nothing
xlBook_New.Save()
' Need all following code to clean up and remove all references!!!
xlBook_New.Close(Nothing, Nothing, Nothing)
xlBook_New = Nothing
xlApplication.Workbooks.Close()
xlApplication.Quit()
xlApplication = Nothing
End Try

Happy Programming!!!

Saturday, January 31, 2009

All about Application Root in ASP.NET

It still surprises me how many ASP.NET developers I run into don't know about the different ways to construct path references in ASP.NET. Let's say we want to include an image in our website. This image is hosted on our website, in an "img" subfolder off of the application root. So how do we create the image HTML, and what do we use as the URL? The wrong answer can lead to big-time maintenance headaches later.

There are three kinds of paths we can use:

Absolute
Relative
Application root (ASP.NET only)

Additionally, we have a few choices on how we chose to create the image in our ASP.NET page:

Plain ol' HTML
HTML server control
Web server control
Each kind of path can be used for each rendering object type (HTML, server control). It turns out that the path is much more important than the rendering object, as different forces might lend me to use controls over HTML. For posterity, I'll just pick plain ol' HTML as an example.

Absolute
Absolute paths are fully qualified URL paths that include the domain name in the URL:

Absolute paths work great for external resources outside of my website, but are poor choices for internal resources. Typically ASP.NET development is done on a development machine, and deployed to a different machine, which means the URLs will most likely change.

For example, the URL above works on my local machine, but breaks when deployed to the server because the "EcommApp" now resides at the root, so I need a URL like "http://ecommapp.com/img/blarg.jpg". Since this absolute path is different, my link breaks, and I have to make lots of changes going back and forth between production and development. For internal resources, absolute paths won't work.

Relative
Relative paths don't specify the domain name, and come in a few flavors:

Site-root relative
Current page relative
Peer relative

These URL path notations are similar to file path notations. Each is slightly different and carries its own issues.

Site-root relative
Here's the same img tag used before, now with a site-root relative path:

Note the lack of the domain name and the leading slash, that's what makes this a site-root relative path. These paths are resolved against the current domain, which means I can go from "localhost" to "ecommapp.com" with ease.

Again, the problem I run into is that locally, my app is deployed off of an "EcommApp" folder, but on the server, it's deployed at the root. My image breaks again, so site-root relative paths aren't a great choice, either.

Current page relative
Now the img tag using a current page relative path:

This time, I don't have the leading slash, nor do I include the "EcommApp" folder. This is because current page relative paths are constructed off the URL being requested, which in this case is the "default.aspx" page at the root of the application. The request goes from the "default.aspx" path, wherever that might be. Now my URL does not have to change when I deploy to production, it works in both places.

But I have two problems now:

Moving the page means I have to change all of the resource URLs
Creating a page in a subfolder means all URLs to the same resource could be different
This leads me to the last kind of relative path.

Peer relative
Suppose I want to create the img tag in a site with the following structure:

\Root
\img
blarg.jpg
\products
default.aspx
Note that default.aspx has to go up one node, then down one node to reference the file in the above tree. Here's the img tag to do just that:

Similar to folder paths, I use the ".." operator to climb up one node in the path, then specify the rest of the path. This path works just fine in production and development, but I still have two main problems:

URLs to the same resource are different depending on depth of the source file in the tree
Moving a resource forces me to manually fix the relative paths
If I decide to move the "default.aspx" page up one level, all of the relative paths must be manually fixed.

But there's one more major issue.

User controls
Now let's suppose I have the following setup:

\Root
\img
blarg.jpg
\products
default.aspx
\user
login.aspx
\support
help.aspx
\usercontrols
header.ascx
default.aspx

All of the ASPX files use the same "header.ascx" control (I'm not using master pages on this site). The "header.ascx" control needs to reference the img, but note that the relative path is calculated based on the page requested, not the user control requested. This means that the relative URL will only work if the user control just happens to be included in a page at the correct depth. All other times it will break, and this is a huge problem.

Luckily, ASP.NET includes a handy way to fix all of these problems, deployment and otherwise.

Application root
A path built with an application root is prefixed with a tilde (~). For example, here's a raw application path to the image:

~/img/blarg.jpgNote the "~/" at the front, that's what signifies it as an application root path. Application root paths are constructed starting at the root of the application. For example, both "http://ecommapp.com" and "http://localhost/EcommApp" are application roots, so I don't have to worry about changing paths at deployment.

Additionally, I don't have to worry about problems with node depth in the hierarchy, as paths are formed from the root and not relative to a leaf node, so my user control problem disappears.

One issue with application root paths is only ASP.NET knows about them. Not browsers. If I do this:

The image breaks, as browsers don't know what IIS applications are, they just know URLs. ASP.NET, however, will take this URL and generate the correct relative path for you, as long as I use ASP.NET to generate the path. Server controls, like "asp:hyperlink" can handle the application root path.

To use the application root path in raw HTML, I just need to use the ResolveUrl method, which is included in the Control class, and therefore available in both my Page and UserControl classes. Combining raw HTML and the ResolveUrl method, I get:

" />The "<%= %>" construct is basically a "Response.Write", and allows me to call the ResolveUrl method directly.

Using application root paths allows me to:

Develop locally and deploy to production seamlessly
Have consistent URL per resource
Use raw HTML without the problems of absolute and relative paths
Some caveats
No matter what I do, I have to change code if either the resource or the page moves. I can minimize the number of changes by externalizing the specific path (the "~/img/blarg.jpg" part) to a resource file, constant, or static global variable. This applies for all types of paths, so I like to eliminate as much duplication as possible.

It's dangerous to assume that the structure or names of resources and pages won't change at some point. As a web site grows, it can become necessary to move resources around and reorganize your site structure. To minimize the impact of deployment and change, use application paths as much as possible, you'll save on Excedrin later.

Reference URL:
http://www.lostechies.com/blogs/jimmy_bogard/archive/2008/01/03/application-root-is-your-friend.aspx

Tuesday, January 27, 2009

ViewState Compression

Recently, I developed a huge ASP.NET page, with more than 30 controls. As we all know, it's a good idea to disable the ViewState for the controls that don't actually need it, say Literals or Labels. After doing that, I noticed that the hidden ViewState field was still a few KBs big. This is obviously a big problem for the users that still don't have a broadband connection, because uploading 40 KB to the server is really a bad issue, especially when they begin to click the "Submit" button again and again because they don't notice any response. So, after a few searches through the Internet, I got a code which compresses viewstate and therefore save a rough 50% of the bandwidth.

Performance and Conclusion:

I noticed that the ViewState has been reduced from 38 KB to 17 KB, saving 44%. Supposing you have an average of 1 postback per minute per user, you could save more than 885 MB of bandwidth per month on every single user. That's an excellent result: you save bandwidth (and therefore money), and the user notices a shorter server response time.

I wanted to point out that this solution has a performance hit on the server's hardware. Compressing, decompressing, encoding, and decoding data is quite a heavy work for the server, so you have to balance the number of users with your CPU power and RAM.

Reference URL: http://www.codeproject.com/KB/viewstate/ViewStateCompression.aspx