Enhancing the Store Locator ASP.NET MVC Application to Include Directions
27 August 10 10:44 PM | Scott Mitchell | 2 comment(s)

The info popup includes a Directions link.Earlier this year I wrote an article on 4GuysFromRolla.com showing how to build an ASP.NET WebForms store locator application using Google Maps. Recently, I ported the store locator application to ASP.NET MVC and detailed building the application in Implementing the Store Locator Application Using ASP.NET MVC.

Recently, a reader wrote in and asked what steps would be necessary to include a “Directions” link with each marker in the map so that, when clicked, the user would see the driving directions from the address they entered and the store of interest. I decided to update the ASP.NET MVC application to include this new feature request. Now, the results page shows a “Directions” link in both the grid of nearby stores and in the info window that pops up when you click a map marker. Clicking the “Directions” link opens a new browser window and loads Google Maps, showing the directions from the user-entered address to the selected store’s address.

To show the driving directions I send the user to the following URL: http://maps.google.com/maps?f=d&source=s_d&saddr=startingAddress&daddr=destinationAddress.

When the user is sent to the store locator page, the user-entered address (a/k/a, the starting address) is passed through the querystring via a field named Address, so we already know the starting address. But how do we get our hands on the destination address? Recall that view is passed a model that is a collection of NearbyStoreLocation objects; the NearbyStoreLocation class has properties like Address (the street address), City, Region, PostalCode, and so forth. We can build up the address by concatenating these various address parts.

Rather than requiring the view to build up the address, I added a new read-only property to the NearbyStoreLocation class named FormattedAddress, which returns an address Google Maps can parse by piecing together the address-related properties into a string.

public string FormattedAddress
{
    get
    {
        var addrPieces = new List<string>(5);
        if (!string.IsNullOrEmpty(this.Address))
            addrPieces.Add(this.Address);
        if (!string.IsNullOrEmpty(this.City))
            addrPieces.Add(this.City);
        if (!string.IsNullOrEmpty(this.Region))
            addrPieces.Add(this.Region);
        if (!string.IsNullOrEmpty(this.CountryCode))
            addrPieces.Add(this.CountryCode);
        if (!string.IsNullOrEmpty(this.PostalCode))
            addrPieces.Add(this.PostalCode);

        return string.Join(", ", addrPieces.ToArray());
    }
}

In the view, the link to the directions can be build like so:

<a target="_blank" href="http://maps.google.com/maps?f=d&source=s_d&saddr=<%=Server.UrlEncode(Request.QueryString["Address"]) %>&daddr=<%=Server.UrlEncode(store.FormattedAddress) %>">Directions</a>

And that’s it! Adding the Directions link to the info popup window is a tad more involved because the quotation marks must be escaped using \”.

To get your hands on the complete, working code, go to: http://www.4guysfromrolla.com/code/GoogleMapsMVC.zip

Happy Programming!

I’m Teaching Two Upcoming Training Classes in San Diego
19 August 10 09:45 PM | Scott Mitchell

Once a year (or so) I teach a handful of one-day, four hour classes in San Diego on various ASP.NET topics. I’ve got two such classes lined up for Saturday, September 11:

  • Get Started with ASP.NET MVC8 AM to Noon
    Traditionally, ASP.NET websites have been designed using the Web Forms model; ASP.NET MVC is an alternative model that uses the Model-View-Controller pattern. In a nutshell, ASP.NET MVC gives developers much finer control over the markup rendered by their web pages, a greater and clearer separation of concerns, better testability, and cleaner, more SEO-friendly URLs. This four hour class introduces ASP.NET MVC, explores how to build ASP.NET MVC applications, covers key differences between Web Forms and ASP.NET MVC, and highlights the benefits of ASP.NET MVC. During the class, we will build a real-world ASP.NET MVC application from start to finish.
  • Build Responsive Web Applications with Ajax, jQuery, and ASP.NET1 to 5 PM
    Ajax-enabled web applications use JavaScript and streamlined client/server communications to offer a highly interactive and responsive user experience. jQuery is a free, popular and powerful JavaScript library that ships with Visual Studio 2010 and greatly simplifies building Ajax-enabled web applications. This four hour class examines key Ajax concepts and techniques and walks through building a real-world Ajax-enabled web application using jQuery and ASP.NET.

Each class is $249 per student, but there is an ongoing early bird special price of $199 (available until August 28th). Students receive electronic access and printed copies of the training materials. Coffee, snacks and lunch are provided. The classes are held at the Sorrento Mesa Center (directions), which is located in Sorrento Valley and has free parking.

If you are interested in signing up or would like detailed course outlines, visit www.fuzzylogicinc.net/InDepth. If you have any questions about the class, material, or logistics, shoot me an email at mitchell@4guysfromrolla.com.

Hope to see you there!

Signup: www.fuzzylogicinc.net/InDepth

Hot Off the Presses – Teach Yourself ASP.NET 4 in 24 Hours
18 August 10 08:27 PM | Scott Mitchell

Teach Yourself ASP.NET 4 in 24 Hours Yesterday I received the author copies of my latest book, Teach Yourself ASP.NET 4 in 24 Hours. When signing the author agreement many, many months ago, there is typically a clause that promises the author X copies of the finished book. Getting these author copies is the final chapter in the book writing process – there is literally no more work to be done. No more writing, no more editing, no more author reviews. It signifies the true completion of the work and results in a physical product you can put your hands on and say, “Here it is. Thank goodness that’s over. Someone please slap me if I ever decide to do this again.”

As with my previous installments of Teach Yourself ASP.NET in 24 Hours, this book is intended for beginner to intermediate developers who are interested in learning ASP.NET 4. The book presumes no prior experience with ASP.NET, databases, or even HTML, although familiarity in those technologies in a plus. Over the course of 24 “hours” (or chapters, as I’m wont to call them), the reader is:

  • Introduced to ASP.NET and the client/server model inherent in every web application,
  • Given a crash course in using Visual Web Developer to create, design, and test ASP.NET websites,
  • Presented an overview of the Visual Basic programming language (for those new to programming),
  • Introduced to ASP.NET’s WebForms model and the most common and germane Web controls,
  • Taught the basics of working with relational databases, including a crash course in SQL,
  • Given an overview of working with databases from ASP.NET pages, including how to display data and how to insert, update, and delete data,
  • Shown how to use ASP.NET features like Membership, the site map, and master pages,
  • Introduced to the ASP.NET Ajax Library and how to create Ajax-enabled web applications using the server-centric model, and
  • Walked through deploying a website to a web hosting company.

If you are a seasoned ASP.NET developer, please refrain from buying this book for yourself – it is not written for you. This book is written for the beginner to intermediate developer who is new to or getting started with ASP.NET. So, if you’re getting started with ASP.NET, consider yourself a beginner-level ASP.NET developer, or if you have a colleague who’s learning ASP.NET, I hope you’ll pick up a copy of Teach Yourself ASP.NET 4 in 24 Hours.

Happy Programming!

Using the GeneratedImage Control in ASP.NET MVC
14 August 10 03:44 AM | Scott Mitchell | 6 comment(s)

One of the free, open-source controls on the ASP.NET team’s CodePlex page is the GeneratedImage control. In a nutshell, the GeneratedImage control is a combination of an ASP.NET Web Control and a set of classes that facilitate programmatically creating, serving, caching, and transforming images. If you store images in the database that need to be served from a web page, if you need to create images on the fly, or if you need to resize, add watermarks, or perform some other image transform, then the GeneratedImage control can help. For more information on the GeneratedImage control and its capabilities, see my articles Dynamically Generating and Caching Images in ASP.NET with the GeneratedImage Control and Image Transforms with the ASP.NET Generated Image Control.

GoogleMaps10Previously, my experience with the GeneratedImage control was in WebForms applications. However, I recently ported my Store Locator ASP.NET application from WinForms to ASP.NET MVC 2 (a two-part article series on porting this application from WinForms to ASP.NET MVC is forthcoming). As the screen shot to the right shows, the store locator results page uses the GeneratedImage control to dynamically create markers for the store locator map, namely a navy circle displaying a white number - 1, 2, 3, and so - to illustrate where on the map a particular store resides.

This image is dynamically generated from an HTTP Handler named NumberToImageHandler.ashx. Specifically, the text to display is passed through the querystring and this HTTP Handler generated and returns the image content. For example, pointing your browser to NumberToImageHandler.ashx?number=1 would create and return the binary content for the 1 image, whereas NumberToImageHandler.ashx?number=2 would create and return the binary content for the 2 image, and so on. This HTTP Handler is a class that extends the Microsoft.Web.ImageHandler class, which is part of the GeneratedImage control library.

For my ASP.NET MVC store locator application I wanted to utilize this existing code, but use a more natural route for accessing the content. Having a .ashx extension just seems so passé. What I wanted instead was to reference these images using routes like:

  • Images/1
  • Images/2
  • ...

Here’s what I ended up doing:

  1. Added a new route in the RegisterRoutes method in Global.asax for the URL pattern Images/{number}.
    routes.Add(
        "ImageRoute",
        new Route(
            "Images/{number}",
            new RouteValueDictionary(),
            new RouteValueDictionary {{ "number", "\\d+" }},
            new NumberRouteHandler()
        )
    );
    Note the constraint that only matches this route if the number parameter is one or more digits. Also note the RouteHandler class that is responsible for handling this route, NumberRouteHandler.
  2. Created the NumberRouteHandler class. This class’s sole responsibility is to return an instance of the HTTP Handler that will handle this request. The code for this class is simple, it just returns an instance of the NumberToImageHandler class:
    public class NumberRouteHandler : IRouteHandler
    {
        public IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
            return new NumberToImageHandler();
        }
    }
  3. Created the NumberToImageHandler class. This class contains virtually the same code as the NumberToImageHandler.ashx file in the WebForms application. The only difference was instead of reading the number to display from the querystring I read it from the Request object’s RouteData collection. Here’s the line of code that determines which number to display in the image:
    string numberText = HttpContext.Current.Request.RequestContext.RouteData.Values["number"] as string;

With this code in place, anytime a request comes in for Images/1, say, the ASP.NET Routing system dispatches the request to the NumberRouteHandler class, which returns an instance of the NumberToImageHandler class, which reads in the number (1, in this case), dynamically creates the image, and returns it back to the requestor. Pretty slick, eh?

For more information on ASP.NET Routing see URL Routing in ASP.NET 4 and ASP.NET Routing (the technical docs). You can download the ASP.NET MVC version of the store locator from http://www.4guysfromrolla.com/code/GoogleMapsMVC.zip. As noted earlier, there is a two-part article series in the works that walks through porting the store locator from WebForms to ASP.NET MVC and will be published on 4GuysFromRolla.com later this month.

I Am Now Tweeting (Or is it Twittering?)
10 August 10 07:12 AM | Scott Mitchell | 3 comment(s)

Last week I received an email from Srikanth Pragada, a .NET trainer in southern India, asking why I did not have a Twitter account. I had two excuses. First, I am somewhat of a Luddite when it comes to social technologies like cell phones and web apps like LinkedIn, Facebook, etc. Also, I have the knack of verbosity, in that I can - and often do! - take 100 words to describe a point that could have been made in 50. Consequently, Twitter's 140 character limit sort of grates against my natural tendency to write as if I was being paid by the word.

But excuses be damned, for I have started tweeting! (Or is it, "I have started Twittering?" I need a teenager so that I can have the answers to questions like these.) You can follow me @ScottOnWriting. Like with this blog, my intent it to use this Twitter account exclusively for discussing and sharing information about ASP.NET and related technologies/topics. No tweets about what I had for breakfast or that my flight is delayed, promise!

Filed under:
Two Upcoming ASP.NET Local User Group Talks in August
05 August 10 01:00 AM | Scott Mitchell

This month I will be giving two talks to .NET user groups here in San Diego.

  • Take Control of Your Website's URLs with ASP.NET Routing - Tuesday, August 17th at 6:00 PM at the ASP.NET Special Interest Group at the Microsoft Office in UTC
    Did you know that ASP.NET has a powerful URL routing framework built into it? This talk explores the ASP.NET Routing system and how to use it to create SEO-friendly, human-readable URLs in both MVC and Web Form applications.

  • ASP.NET Tips, Tricks, Traps, and Tools - Tuesday, August 24th at 6:00 PM at the San Diego .NET User Group at the Intuit Campus
    Explore a variety of ASP.NET tips, tricks, traps, and tools with author, consultant, and trainer Scott Mitchell.

Hope to see you there!
Filed under:
Links to Scott Guthrie's Using LINQ to SQL Tutorials
27 July 10 11:56 PM | Scott Mitchell | 5 comment(s)

Back when Visual Studio 2008 came out, Scott Guthrie wrote an excellent series of blog posts on using LINQ to SQL. Unfortunately, the various installments are not easy to follow when starting from Part 1 and going onward, so I've decided to gather and organize the links here for myself and for anyone else interested in his tutorial.

Using LINQ to SQL

Also, there was (at one time) a single PDF containing all of these tutorials, but the website where it was hosted appears to be offline. Does anyone have a copy of that PDF they'd care to send me to host?

UPDATE: Some time ago a developer by the name of Kadir Pekel compiled all nine tutorials into a single PDF and hosted them on his website, http://it-box.blogturk.net, but that website no longer is around. Helpful reader Milan had a copy of said PDF and sent it to me (thank you!), which you can download here, if interested: http://www.datawebcontrols.com/classes/LINQToSql.zip

Happy Programming!

A Review of Desktop / Screen Sharing Software
18 July 10 12:32 AM | Scott Mitchell | 15 comment(s)
I work as an independent consultant and trainer. Most of my business involves me meeting with clients (in person or online), discussing their needs, and then building a new application or enhancing an existing one from my own desktop. However, a growing part of my business has been what I call "online consulting and training," which involves me using screen sharing software to remote into the client's computer. The client is seated at his computer and we are on the phone and during these meetings we'll either practice pair programming, hash out a particularly difficult block of code, or use the time as a one-on-one tutoring session. Some clients I meet with regularly (like 3 hours every week, say), while for others we meet when something comes up and they need a helping hand.

If you're interested in such online consulting or training, please drop me a line at mitchell@4guysfromrolla.com. You can find out more about my experience and rates at http://www.4guysfromrolla.com/ScottMitchell.

In any event, I spend a fair amount of time - 5-15 hours a week, usually - sharing my desktop or working from the client's desktop. During these sessions there's the same sort of activities you perform when developing ASP.NET applications at your workstation: typing in code, switching between Visual Studio, the browser, running queries from SQL Server Management Studio, and so forth. I've evaluated four different screen sharing programs and thought I'd share what I liked best about each and what I liked least. Keep in mind that my rankings are based on my typical usage when sharing screens. If you are doing something different - showing a PowerPoint presentation, for instance - your mileage may vary.

Here they are, from worst to first:

#4.) FogCreek Copilot
Copilot has a couple of really nice selling points when compared to the other three competitors:

  • It's the most affordable - starting at $244/year, or $24/month, Copilot is about half of what the other competitors charge. Plus you can buy a one-day pass for $5 during the week - on the weekends, it's free!
  • It's especially easy to start the meeting - unlike some of the other options, which involve first time users to download and install software prior to joining the meeting, Copilot is run from an executable that doesn't require any installation steps. Just download the file, double-click it, and you're off and running. This can be helpful when assisting a computer "challenged" friend or when working with someone in a corporate environment that prohibits them from installing software on their machine.

The big problem with Copilot is that it is slooooow. Switching from one full screen window to another can take a could one to three seconds. What's more, if you're not a slow typer you'll find that there is a noticeable lag when viewing a remote user's screen and typing into it. Type - wait a half-second - the letter appears. Type - wait a half-second - the letter appears. Needless to say, it can be quite frustrating to speedily type out a long line of code. I don't have the patience to type one character at a time, continuing only when the letter shows up. My clients, who are paying by the hour, wouldn't be satisfied with this approach either. So I end up typing a line of code and don't see the whole line complete until a couple of seconds after I finished typing. And if you transposed two letters or forgot a parenthesis you have to carefully (and slowly) move the cursor back to the right spot and then fix the mistake.

Another problem with Copilot is that you can't seamlessly switch control from one desktop to another. Say that I am viewing the client's remote computer and want to show them a diagram on my computer. It would be nice to be able to just click a button and have them see my screen, but that's not possible with Copilot. Also, Copilot only allows one-on-one screen sharing. You can't have a meeting with three (or more) attendees.

Copilot's ease of setup and low price point may make it attractive to people doing limited screen sharing or for those giving presentations or assisting a family member with a computer problem, but it is not a viable option for online consulting and training due, primarily, to its speed issues.

#3.) Adobe Connect Pro
Of all the desktop sharing products I evaluated, Adobe Connect Pro is certainly the easiest to get a meeting started. Adobe Connect Pro meetings are powered by Flash, which means if the participants have Flash installed on their computers (and, honestly, who doesn't these days?) then they can join the meeting. There's no software to download, nothing to install.

It seems that Adobe Connect Pro is more targeted to online meetings and eLearning scenarios than desktop sharing. While Adobe Connect Pro certainly allows the host (or a participant) to share their desktop with everyone else, it also has features like online quizzes, registration forms, and other features that would be useful for teaching an online class, but not at all needed for doing the type of online consulting and training that I do. And while I'm sure their online quizzes and other eLearning features are all top notch, I regret to report that their screen sharing is not - it suffers from the same speed issues found in Copilot. In short, there is a perceptible delay when typing on the remote user's computer; likewise with switching between windows on the remote computer.

Adobe Connect Pro is also the most expensive option out of the bunch. An annual plan will run you $540. By the month it's $55. Adobe offers a pay-per-use option, but that's $0.32 per minute per user, which can add up fast. While the two share similar speed problems, I decided to rank Adobe Connect Pro ahead of Copilot because it does offer more screen sharing features, like the ability to toggle between the host and participant's screens, and the ability to choose which monitor to share (if you have multiple monitors), and audio conferencing options. But for some these extra features might not be worth the added cost.

#2.) GoToMeeting
After using Copilot and Adobe Connect Pro, the first thing you notice about GoToMeeting is that there is no longer a delay when typing. Sure, there may be a hiccup every now and then when the network gets congested, but for the most part every time you type a letter from your keyboard, wham, it appears on the remote computer instantly. Also, switching between windows on the remote computer is noticeably faster than with Copilot or Adobe Connect Pro.

Unlike the previous two products, GoToMeeting requires a bit more of an installation process to join (or host) a meeting. It's all very nicely automated - you only need to click a few "I Agree"s and "Next" buttons - but these extra steps might trip up a computer novice. And the need to install software may be a deal breaker in certain corporate environments.

My biggest gripe with GoToMeeting is that the client swallows (or outright disallows) certain keyboard shortcuts when typing from the host computer into a remote computer. And it's not just any keyboard shortcuts that are not permitted, but two of the most popular ones: Alt+Tab and Ctrl+C. Say you're writing code and want to copy some block of text and paste it elsewhere. If you're at all like me, you instinctively use the keyboard to highlight the selected text and then use Ctrl+C to copy it to the clipboard. However, this combination does not copy anything to the clipboard. Consequently, when you later hit Ctrl+V, what was previously in the clipboard gets emitted! In order to copy to the clipboard you need to select the text and then, using your mouse, right-click it and choose Copy from the context menu. Similarly, Alt+Tab doesn't work. Alt+Tab, when typed from the host computer, tabs out of the GoToMeeting window and to a different window on your desktop. If you want to switch between windows on the remote computer you have to use your mouse and click the appropriate window from the taskbar.

GoToMeeting has all of the screen sharing bells and whistles. You can have up to 15 participants. Sharing one participant's screen with the group or sharing the host's screen is quite simple, too. There's a phone number participants can optionally call to join an audio conference, or the conferencing can be done over the Internet using the computer's speakers and microphone.

Price-wise, GoToMeeting falls in between CoPilot and Adobe Connect Pro. You can sign up for an annual plan for $468 or go monthly for $49.

#1.) Webex
Webex is a lot like GoToMeeting. Both have a similar download and install story. Both cost the same. Both have comparable performance (although Webex does seem a tad bit faster to me). And both offer pretty much the same feature set.

The differences between the two are, by most accounts, minor ones, although as one who prefers using the keyboard over the mouse, these differences are important. Namely, the Webex client doesn't swallow keyboard shortcuts. Alt+C and Alt+Tab work as expected when typing from my keyboard to the remote client's computer. That's not to say the Webex client is not without its own annoyances. In fact, there are two that irk me. First, when the remote user is made the presenter and shares his screen with me, a little command window with various options appears on the remote computer's desktop in the lower right corner. These options are only displayed on the remote computer's screen and not on my screen. However, if I have control of the remote client's computer and move my mouse to the lower right corner and click it, my clicks are intercepted by that command window (even though I don't see it). The problem goes away if the remote client remembers to minimize that command window. It would be nice to have an option that would hide that command window right away (or not have it intercept my clicks or show it to me, as well).

The second issue is that when I have control of the remote client's computer a little bubble follows my mouse cursor that says to the remote client and me, "Scott has control of your computer. Click here to take control back." This bubble is supposed to be beneath the mouse cursor and offset to the right, but every now and then the mouse cursor gets positioned over it and, wouldn't you know, when that happens any mouse click is intercepted by that bubble. It doesn't revert control back to the remote client, but it does "swallow" my mouse click. I have to "jiggle the mouse" to get it off that bubble so that I can click what I meant to click in the first place.


In Conclusion...
So there you have it, Webex is the winner. You don't have to take my word for it, though - each of these products offers a free trial so you can experiment with each and find which one best fits your workload.

For background, when I first started offering online consulting and training I used Copilot for several months, suffering through the speed issues. I incorrectly had presumed that all screen sharing products were going to have a little bit of lag. After one particularly frustrating day I decided to try GoToMeeting for giggles as they had a free 30 day trial. I was blown away by the difference in speed and immediately switched. The $25 extra each month was definitely worth it. Earlier this year, GoToMeeting updated their software to a new version where the Ctrl+C shortcut no longer worked. Their telephone support was helpful and was able to roll me back to the previous version so that I could continue to use the keyboard to copy and paste. A month ago (or so), they stopped supporting the old version of the software and upgraded me to the new version (which still did not support Ctrl+C). At that point I evaluated Adobe Connect Pro and then Webex, which is what I use today.

Happy (Remote) Programming!

In addition to evaluating these four products, I also tried Microsoft Live Meeting. Unfortunately, the client I attempted to evaluate this product with received error messages when attempting to download the software used by the meeting. This happened on two separate occasions, so I threw in the towel.

Filed under:
HOWTO: Update Records in a Database Table With Data From Another Table (MS SQL Server)
13 July 10 08:01 AM | Scott Mitchell | 6 comment(s)

SQL's UPDATE statement makes it easy to update one or more records in a database table. The most common UPDATE statement pattern assigns static or parameterized values to one or more columns:

UPDATE MyTable SET
    Column1 = Value1,
    Column2 = Value2,
    ...
WHERE PrimaryKeyColumn = SomeValue

But what if you need to update values in one database table from values in another database table? There are a couple ways this can be done. One way is to use a subquery:

UPDATE MyTable SET
    Column1 = (SELECT SomeColumn FROM SomeOtherTable WHERE SuchAndSuch = ThisAndThat),
    Column2 = (SELECT SomeOtherColumn FROM SomeOtherTable WHERE SuchAndSuch = ThisAndThat),
    ...
WHERE PrimaryKeyColumn = SomeValue

Alternatively, you can use a JOIN statement in your UPDATE clause. The precise syntax seems to vary a bit based on the database system. For Microsoft SQL Server, use the following syntax:

UPDATE MyTable SET
    Column1 = SomeOtherTable.SomeColumn,   
    Column2 = SomeOtherTable.SomeOtherColumn,
    ...
FROM MyTable
    INNER JOIN SomeOtherTable ON
        MyTable.SuchAndSuch = SomeOtherTable.ThisAndThat
WHERE PrimaryKeyColumn = SomeValue

Bear in mind that updating data in one table with data from another table may be a sign of bad database design. I've seen developers use these approaches to duplicate data across multiple tables so that it's "easier" or "quicker" to write a query to read the data, but such actions are (usually) folly. Data duplication may make your SELECT queries easier to write, but you can very easily end up with data integrity issues.

So when might you need to copy data from one table to another? Consider a help desk application that allows support staff to create support tickets, where each ticket in the system exists as a record in the Tickets table. A ticket may have dozens of data points. To ease data entry, let's say that administrative users can create "ticket templates," which pre-define the values for a number of ticket data points that are stored in a table named TicketTemplates. Applying a template to an existing ticket would require that the existing record in the Tickets table have its non-set data points assigned to the corresponding default values of the selected template. Presume that the Tickets table has a TicketTemplateId field that indicates what template is to be applied to the ticket. Once this value has been assigned, the following UPDATE statement could be executed to copy the template's pre-defined values to the non-assigned data points in the Tickets table:

UPDATE Tickets SET
    PriorityId = ISNULL(Tickets.PriorityId, TicketTemplates.PriorityId),
    CategoryId = ISNULL(Tickets.CategoryId, TicketTemplates.CategoryId),
    IsPublic = ISNULL(Tickets.IsPublic, TicketTemplates.IsPublic),
    ...
FROM Tickets
    INNER JOIN TicketTemplates ON
        Tickets.TicketTemplateId = TicketTemplates.TicketTemplateId
WHERE Tickets.TicketId = @TicketId

Happy Programming!

Filed under:
Determining an ASP.NET Page's View State Footprint
06 July 10 11:35 PM | Scott Mitchell | 6 comment(s)

ASP.NET view state is the technique used by an ASP.NET Web page to persist changes to the state of a Web Form across postbacks. The view state of a page is, by default, placed in a hidden form field named __VIEWSTATE, and can easily get very large. Not only does the __VIEWSTATE form field cause slower downloads, but, whenever the user posts back the Web page, the contents of this hidden form field must be posted back in the HTTP request, thereby lengthening the request time, as well. Because view state is buried in a hidden form field and because as developer we typically test locally, the affect of view state bloat on the user experience requires preemptive action. Steps must be taken to measure view state's size and to take steps in trimming down view state if it begins to negatively affect the user experience.

There are a variety of techniques for measuring the view state size on a given page. Perhaps the simplest is to turn on page tracing. With page tracing enabled you can see the estimated view state of each control in the control hierarchy. For instance, the screen show below shows that two Labels on my page consume (roughly) 120 bytes of view state each.

There's also a free add-on for Firefox that reports view state size directly in your browser window: Viewstate Size. When installed, this add-on will show a little icon in your browser's lower right corner reporting the view state size for a given page.

 

It's also possible to determine the view state size programmatically. Today I was working on a demo for an article and wanted to have the view state size displayed on every page. After reading an article I wrote back in 2004 - Understanding ASP.NET View State - I came up with the following code snippet to compute and display the view state size for a given page. This technique uses the "old school" way of doing it, relying on objects that have been around since ASP.NET's inception. Consequently, the below code will work in any ASP.NET application.

Visual Basic Version:

Protected Overrides Sub SavePageStateToPersistenceMedium(ByVal state As Object)
    MyBase.SavePageStateToPersistenceMedium(state)

    Dim formatter As New LosFormatter()
    Dim viewStateLength = -1

    Using writer As New StringWriter()
        formatter.Serialize(writer, state)
        viewStateLength = writer.ToString().Length
    End Using

    Response.Write(String.Format("<h1 class="" viewstate-summary="">Total ViewState Size For This Page: {0:N0} bytes</h1>", viewStateLength))
End Sub

C# Version:

protected override void SavePageStateToPersistenceMedium(object state)
{
    base.SavePageStateToPersistenceMedium(state);

    var formatter = new LosFormatter();
    var viewStateLength = -1;

    using (var writer = new StringWriter())
    {
        formatter.Serialize(writer, state);
        viewStateLength = writer.ToString().Length;
    }

    Response.Write(String.Format(@"<h1 class="" viewstate-summary="">Total ViewState Size For This Page: {0:N0} bytes</h1>", viewStateLength));
}

The approach overrides the SavePageStateToPersistenceMedium method, which is what is invoked when the page is ready to persist its view state. In ASP.NET 1.x you would override this method to store view state to an alternative store, such as to a file on the web server, rather than in a hidden input field on the form. Starting with ASP.NET 2.0 you could use the PageStatePersister class. However, here I just want to compute the size of the serialized view state string. Consequently, I start by calling the base class's SavePageStateToPersistenceMedium method. Next, I replicate the default logic that the ASP.NET Page class uses to serialize view state - namely, I create a LosFormatter object and serialize the content to a StringWriter. Once I've done this I can determine the size of the persisted view state by getting the StringWriter's contents and determining how many characters are included. The above code can be placed directly in a code-behind class or, better yet, in a custom base Page class.

The above programmatic approach is relatively easy to implement and works in all versions of ASP.NET. However, it won't produce the precise view state size for ASP.NET 2.0 applications and beyond. This is because the LosFormatter class was replaced with a new formatting type, ObjectStateFormatter, starting in ASP.NET 2.0. (You can certainly use the LosFormatter code above in ASP.NET 2.0 and beyond applications, as the class remains in the framework for backwards compatibility. Granted, the reported view state size and actual size will differ slightly, but in analyzing whether a page contains too much view state or not, precise numbers aren't required - a close estimate will usually suffice.)

To get precise measurements we can use the ASP.NET 2.0+ by doing the following:

  1. Create a class that extends the HiddenFieldPageStatePersister class. This is the class that is used by ASP.NET (by default) to persist view state and other information to hidden form fields. We want to extend this class to include a property that reports the size of the persisted state, and
  2. Override the Page class's PageStatePersister property and return the custom persister class created in step 1.
  3. Override the Page class's SavePageStateToPersistenceMedium method and read the value of the view state size from the class created in step 1.

Here's the code for the class that extends HiddenFieldPageStatePersister, which I've named MyHiddenFieldPageStatePersister (please don't mock me for my lack of naming creativity). This class is pretty simple: it defines a property named StateSize, overrides the Save method, and then sets the StateSize property from this method using the same code/logic as used internally by the HiddenFieldPageStatePersister class. The code snippet below takes advantage of some language features that you might not have at your disposal if you're using an older version of C# or VB (for example, the use of automatic properties). But the concept is sound and should work (with a little tweaking) in earlier versions of VB/C#. (I tested this using ASP.NET 4. You can download the complete source code here.)

Visual Basic Version:

Public Class MyHiddenFieldPageStatePersister
    Inherits HiddenFieldPageStatePersister

    Public Sub New(ByVal page As Page)
        MyBase.New(page)
    End Sub

    Public Overrides Sub Save()
        If MyBase.ViewState IsNot Nothing OrElse MyBase.ControlState IsNot Nothing Then
            Me.StateSize = MyBase.StateFormatter.Serialize(New Pair(MyBase.ViewState, MyBase.ControlState)).Length
        End If
        
        MyBase.Save()
    End Sub

    Public Property StateSize As Integer
End Class

C# Version:

public class MyHiddenFieldPageStatePersisterCS : HiddenFieldPageStatePersister
{
    public MyHiddenFieldPageStatePersisterCS(Page page) : base(page) { }

    public override void Save()
    {
        if (base.ViewState != null || base.ControlState != null)
            this.StateSize = base.StateFormatter.Serialize(new Pair(base.ViewState, base.ControlState)).Length;

        base.Save();
    }

    public int StateSize
    {
        get;
        private set;
    }
}

The last step, then, is to override the Page class's PageStatePersister property and return an instance of our persister class, MyHiddenFieldPageStatePersister. Again, this can be done in the code-behind class of an ASP.NET page, but I recommend putting it in a custom base Page class.

Visual Basic Version:

Private _PageStatePersister As PageStatePersister = Nothing

Protected Overrides ReadOnly Property PageStatePersister As System.Web.UI.PageStatePersister
    Get
        If _PageStatePersister Is Nothing Then
            _PageStatePersister = New MyHiddenFieldPageStatePersister(Me)
        End If

        Return _PageStatePersister
    End Get
End Property

C# Version:

private PageStatePersister _PageStatePersister = null;

protected override PageStatePersister PageStatePersister
{
    get
    {
        if (_PageStatePersister == null)
            _PageStatePersister = new MyHiddenFieldPageStatePersisterCS(this);

        return _PageStatePersister;
    }
}

EDIT [2010-07-07]: I forget an important final piece of the puzzle for using the MyHiddenFieldPageStatePersister class! In addition to overriding the PageStatePersister property you also need to override the SavePageStateToPersistenceMedium method like in the LosFormatter example, but instead of using LosFormatter you need to read the value of the StateSize property from the MyHiddenFieldPageStatePersister class. The code for this overridden method follows:

Visual Basic Version:

Protected Overrides Sub SavePageStateToPersistenceMedium(ByVal state As Object)
    MyBase.SavePageStateToPersistenceMedium(state)

    Dim myPersister As MyHiddenFieldPageStatePersister = CType(Me.PageStatePersister, MyHiddenFieldPageStatePersister)

    Response.Write(String.Format("

Total ViewState Size For This Page: {0:N0} bytes

", myPersister.StateSize)) End Sub

C# Version:

protected override void SavePageStateToPersistenceMedium(object state)
{
    base.SavePageStateToPersistenceMedium(state);

    var myPersister = this.PageStatePersister as MyHiddenFieldPageStatePersisterCS;

    if (myPersister != null)
        Response.Write(String.Format(@"

Total ViewState Size For This Page: {0:N0} bytes

", myPersister.StateSize)); }

Happy Programming!

I'll be speaking at the SoCal Code Camp this Weekend
23 June 10 03:06 AM | Scott Mitchell

The SoCal Code Camp is coming to San Diego, California this weekend, June 26th and 27th. This free event boasts nearly 100 talks given by dozens of speakers from around the area covering a wide swath of interesting technology-related topics. This year the Code Camp is hosted at the beautiful UC-San Diego campus.

I will be presenting two talks:

  • Take Control of Your Website's URLs with ASP.NET Routing -  Sunday, June 27th, 9:00 AM, Rm 101
    Did you know that ASP.NET has a powerful URL routing framework built into it? This talk explores the ASP.NET Routing system and how to use it to create SEO-friendly, human-readble URLs in both MVC and Web Form applications.

  • Getting Started with ASP.NET MVC - Sunday, June 27th, 10:15 AM, Rm 101
    ASP.NET MVC brings the Model-View-Controller design pattern to ASP.NET. In this talk, Scott shows how to get started with ASP.NET MVC, explores how to create and use Models, Views, and Controllers, and walks through a creating a simple, end-to-end ASP.NET MVC application.

If you plan on attending the Getting Started with ASP.NET MVC talk I encourage you to also attend Take Control of Your Website's URLs with ASP.NET Routing, as routing is an important aspect of ASP.NET MVC.

For a full list of talks being presented this year, see the schedule posted on the SoCal Code Camp website.

Hope to see you there!

New Software Running ScottOnWriting.NET
27 May 10 10:50 PM | Scott Mitchell | 6 comment(s)

When I started this blog in July 2003 there weren't many available blog engines build atop ASP.NET. One of the more interesting ones at the time was Scott Watermasysk's .Text blog engine (which eventually became part of Community Server. Over the past nearly seven years ScottOnWriting.NET has continued to be powered by .Text, even though the code base was discontinued circa 2004. There were two primary reasons I stuck with .Text for so long:

  1. I could find no easy way to migrate my existing content from .Text into Community Server. I believe there was such a tool created in the early days of Community Server, but I had no luck with it, as I recall. Eventually, this product disappeared and the only migration tools I could find were from older versions of Community Server to newer ones, but none for .Text.
  2. There were URL changes between .Text and Community Server, so switching over would immediately result in a slew of broken URLs. I am a strong believer in URLs as public interfaces and view broken URLs and believe each broken URL makes the Internet that less useful. Plus it makes the Internet Founding Fathers - Tim Berners-Lee, Vint Cerf, and Al Gore - cry. Smile

Well, this week I decided to take the plunge and move ScottOnWriting.NET and its hundreds of posts over to a new blog engine, namely Community Server (albeit an older version of Community Server - when will I learn?). I wrote some custom SQL scripts to (the best of my ability) move over all blog posts and categories (or tags, as they're called now-a-days). I also spent a good deal of time writing some regular expressions and very big switch statements to properly reroute URLs. For example, if you try to visit an old ScottOnWriting.NET URL, like http://scottonwriting.net/sowBlog/posts/145.aspx, you should be automatically redirected to the new URL, http://scottonwriting.net/sowBlog/archive/0000/00/00/162659.aspx. Likewise, if you visit an old category URL or an old moth/year archive URL you should be redirected to the new URLs. If this is not the case, if you find a broken URL or other problem on this site, please let me know.

Note that I did not mention bringing over the past blog comments. I'm still on the fence as to whether I want to spend the time to do this. There were hundreds of comments left over the years, many of them quite helpful and many that added substantially to the discussion. However, I've already sunk a number of hours into this migration and am hesitant to burn too much more time. Plus I still have a number of aesthetic and cosmetic things to do, like update the site's CSS, add in some of the widgets from the original site that aren't part of the default Community Server setup, and so forth. My goal is to eventually import these past comments, but I wouldn't be at all surprised if this is still on my TODO list come 2011.

In closing, please do let me know if you run into any problems with the new blog engine.

Thanks!

Building a Store Locator ASP.NET Application Using Google Maps API
25 May 10 11:39 AM | Scott Mitchell | 6 comment(s)

The past couple of projects I've been working on have included the use of the Google Maps API and geocoding service in websites for various reasons. I decided to tie together some of the lessons learned, build an ASP.NET store locator demo, and write about it on 4Guys. Last week I published the first article in what I think will be a three-part series: Building a Store Locator ASP.NET Application Using Google Maps (Part 1). Part 1 walks through creating a demo where a user can type in an address and any stores within a (roughly) 15 mile area will be displayed in a grid. The article begins with a look at the database used to power the store locator (namely, a single table that contains one row for every location, with each location storing its store number, address, and, most important, latitude and longitude coordinates) and then turns to using Google's geocoding service to translate a user-entered address into latitude and longitude coordinates. The latitude and longitude coordinates are used to find nearby stores, which are then displayed in a grid.

Part 2 looks at enhancing the search results to include a map with markers indicating the position of each nearby store location. The Google Maps API, along with a bit of client-side script and server-side logic, make this actually pretty straightforward and easy to implement. Here's a screen shot of the improved store locator results.

Part 3, which I plan on publishing next week, looks at how to enhance the map by using information windows to display address information when clicking a marker. Additionally, I'll show how to use custom icons for the markers so that instead of having the same marker for each nearby location the markers will be images numbered 1, 2, 3, and so on, which will correspond to a number assigned to each search result in the grid. The idea here is that by numbering the search results in the grid and the markers on the map visitors will quickly be able to see what marker corresponds to what search result.

This article and demo has been a lot of fun to write and create, and I hope you enjoy reading it, too.

Happy Programming!

Filed under:
Resolve a URL from a Partial View (ASP.NET MVC)
11 May 10 12:05 AM | Scott Mitchell

Working on an ASP.NET MVC application and needed the ability to resolve a URL from a partial view. For example, I have an image I want to display, but I need to resolve the virtual path (say, ~/Content/Images/New.png) into a relative path that the browser can use, such as ../../Content/Images/New.png or /MyAppName/Content/Images/New.png.

A standard view derives from the System.Web.UI.Page class, meaning you have access to the ResolveUrl and ResolveClientUrl methods. Consequently, you can write markup/code like the following:

<img src='=<%=Page.ResolveClientUrl("~/Content/Images/New.png")%>' />

The problem is that the above code does not work as expected in a partial view. What's a little confusing is that while the above code compiles and the page, when visited through a browser, renders, the call to Page.ResolveClientUrl returns precisely what you pass in, ~/Content/Images/New.png, in this instance. The browser doesn't know what to do with ~, it presumes it's part of the URL, so it sends the request to the server for the image with the ~ in the URL, which results in a broken image.

I did a bit of searching online and found this handy tip from Stephen Walther - Using ResolveUrl in an HTML Helper. In a nutshell, Stephen shows how to create an extension method for the HtmlHelper class that uses the UrlHelper class to resolve a URL. Specifically, Stephen shows how to add an Image extension method to HtmlHelper. I incorporated Stephen's code into my codebase and also created a more generic extension method, which I named ResolveUrl.

public static MvcHtmlString ResolveUrl(this HtmlHelper htmlHelper, string url)
{
    var urlHelper = new UrlHelper(htmlHelper.ViewContext.RequestContext);
    return MvcHtmlString.Create(urlHelper.Content(url));
}

With this method in place you can resolve a URL in a partial view like so:

<img src='<%=Html.ResolveUrl("~/Content/Images/New.png")%>' />

Or you could use Stephen's Html.Image extension method (although my more generic Html.ResolveUrl method could be used in non-image related scenarios where you needed to get a relative URL from a virtual one in a partial view). Thanks for the helpful tip, Stephen!

Happy Programming!

Filed under:
Authorize.Net, Silent Posts, and URL Rewriting Don't Mix
19 April 10 06:57 PM | Scott Mitchell

The too long, didn't read synopsis: If you use Authorize.Net and its silent post feature and it stops working, make sure that if your website uses URL rewriting to strip or add a www to the domain name that the URL you specify for the silent post matches the URL rewriting rule because Authorize.Net's silent post feature won't resubmit the post request to URL specified via the redirect response.

I have a client that uses Authorize.Net to manage and bill customers. Like many payment gateways, Authorize.Net supports recurring payments. For example, a website may charge members a monthly fee to access their services. With Authorize.Net you can provide the billing amount and schedule and at each interval Authorize.Net will automatically charge the customer's credit card and deposit the funds to your account.

You may want to do something whenever Authorize.Net performs a recurring payment. For instance, if the recurring payment charge was a success you would extend the customer's service; if the transaction was denied then you would cancel their service (or whatever). To accomodate this, Authorize.Net offers a silent post feature. Properly configured, Authorize.Net will send an HTTP request that contains details of the recurring payment transaction to a URL that you specify. This URL could be an ASP.NET page on your server that then parses the data from Authorize.Net and updates the specified customer's account accordingly. (Of course, you can always view the history of recurring payments through the reporting interface on Authorize.Net's website; the silent post feature gives you a way to programmatically respond to a recurring payment.)

Recently, this client of mine that uses Authorize.Net informed me that several paying customers were telling him that their access to the site had been cut off even though their credit cards had been recently billed. Looking through our logs, I noticed that we had not shown any recurring payment log activity for over a month. I figured one of two things must be going on: either Authorize.Net wasn't sending us the silent post requests anymore or the page that was processing them wasn't doing so correctly.

I started by verifying that our Authorize.Net account was properly setup to use the silent post feature and that it was pointing to the correct URL. Authorize.Net's site indicated the silent post was configured and that recurring payment transaction details were being sent to http://example.com/AuthorizeNetProcessingPage.aspx.

Next, I wanted to determine what information was getting sent to that URL. The application was setup to log the parsed results of the Authorize.Net request, such as what customer the recurring payment applied to; however, we were not logging the actual HTTP request coming from Authorize.Net. I contacted Authorize.Net's support to inquire if they logged the HTTP request send via the silent post feature and was told that they did not.

I decided to add a bit of code to log the incoming HTTP request, which you can do by using the Request object's SaveAs method. This allowed me to save every incoming HTTP request to the silent post page to a text file on the server. Upon the next recurring payment, I was able to see the HTTP request being received by the page:

GET /AuthorizeNetProcessingPage.aspx HTTP/1.1
Connection: Close
Accept: */*
Host:
www.example.com

That was it. Two things alarmed me: first, the request was obviously a GET and not a POST; second, there was no POST body (obviously), which is where Authorize.Net passes along the details of the recurring payment transaction. What stuck out was the Host header, which differed slightly from the silent post URL configured in Authorize.Net. Specifically, the Host header in the above logged request pointed to www.example.com, whereas the Authorize.Net configuration used example.com (no www). About a month ago - the same time these recurring payment transaction details were no longer being processed by our ASP.NET page - we had implemented IIS 7's URL rewriting feature to permanently redirect all traffic to example.com to www.example.com.

Could that be the problem? I contacted Authorize.Net's support again and asked them if their silent post algorithm would follow the 301 HTTP response and repost the recurring payment transaction details. They said, Yes, the silent post would follow redirects. Their reports didn't jive with my observations, so I went ahead and updated our Authorize.Net configuration to point to http://www.example.com/AuthorizeNetProcessingPage.aspx instead of http://example.com/AuthorizeNetProcessingPage.aspx. And, I'm happy to report, recurring payments and correctly being processed again!

If you use Authorize.Net and the silent post feature, and you notice that your processing page is not longer working, make sure you are not using any URL rewriting rules that may conflict with the silent post URL configuration. Hope this saves someone the time it took me to get to the bottom of this.

Happy Programming!

Filed under:
More Posts « Previous page - Next page »

Archives

My Books

  • Teach Yourself ASP.NET 4 in 24 Hours
  • Teach Yourself ASP.NET 3.5 in 24 Hours
  • Teach Yourself ASP.NET 2.0 in 24 Hours
  • ASP.NET Data Web Controls Kick Start
  • ASP.NET: Tips, Tutorials, and Code
  • Designing Active Server Pages
  • Teach Yourself Active Server Pages 3.0 in 21 Days

I am a Microsoft MVP for ASP.NET.

I am an ASPInsider.