Monday, June 09, 2008
Posted on Monday, June 09, 2008 9:06:12 PM (Mountain Daylight Time, UTC-06:00)  Comments [3] | 
Categories: ASP.NET | Dojo

So I've been using the Dojo toolkit for a while now, but mainly for the Dijit and DojoX UI components.  For the actual Ajax communication layer,  I've been relying on the Microsoft Ajax framework because of it's seamless integration with the ASP.NET Web Service model. Since I've got the whole mess of Dojo already being loaded into the browser, I figured I should drop the crutches, and work out how to work directly with the Dojo ajax framework.

The one downside of this is that you no longer get the client side proxies for your ASP.NET Web Services. Thus today's article - how to directly access an ASP.NET JSON Web Service using dojo.xhrGet.

Part 1: Create a Web Service

This is pretty simple - just add a web service to your web site. I tend to put the code right in the asmx file, rather than in the code-behind in app_code, but that's just personal preference.

<%@ WebService Language="C#" Class="ASPWebService" %>
using System;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class ASPWebService  : System.Web.Services.WebService {

    [WebMethod]
    public string SayHello() {
        return "Hello to Dojo " + DateTime.Now.ToLongDateString();
    }   
}

 

By default your web service will be configured to "speak" SOAP, which is fine if you are consuming it from another application, but for Ajax based browser apps, it's much easier to work with JSON.

Part 2: JSON-ify the Web Service

This is actually really simple - just include System.Web.Script.Services, and the [ScriptService] attribute to the web service as shown below.

<%@ WebService Language="C#" Class="ASPWebService" %>
using System;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Web.Script.Services;

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ScriptService]
public class ASPWebService  : System.Web.Services.WebService {

    [WebMethod]
    [ScriptMethod(ResponseFormat = ResponseFormat.Json, UseHttpGet=true)] 
    public string SayHello() {
        return "Hello to Dojo " + DateTime.Now.ToLongDateString();
    }   
}

We also add a [ScriptMethod] attribute to the WebMethod. We do this for two reasons - first, we want to specify that the response  format should be JSON, and we also want to allow access to this method via http Get's (this is disabled by default).

At this point you can hit the web service in your web browser, and get the standard Web Service browser. But if you also stick /js on the end of the url, you will get the client side Javascript that integrates with the MS Ajax Framework. But we're trying to work with Dojo, so we'll move on...

Part 3: Testing the Service in a Browser

Our simple "SayHello" web method does not take any arguments, so we should be able to just "GET" it via a browser.

The tricky bit that took me some time to sleuth down was that you also need to edit web.config to tell the web service to respond to all Gets and Posts. Add this section (just copy out the parts you are missing - do not paste this whole thing into your web.config or you will get an error!):

 <system.web>
  <webServices>
    <protocols>
      <add name="HttpGet"/>
      <add name="HttpPost"/>
    </protocols>
  </webServices>
</system.web>

Once you do this, you will get the response back. Despite the fact that we have used the [ScriptMethod] attribute to specify that the SayHello web method should return Json, this will only occur if you sent the request content-type to "application/json; charset=utf-8". Without that, you'll still get xml.

Part 4: Create a Simple Page

For this, I created about the simplest page I could - just a button and a div that will be updated. Here's the whole page...

<!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>Dojo to ASP.NET WebServices</title>
    <script type="text/javascript" src="http://o.aolcdn.com/dojo/1.1.1/dojo/dojo.xd.js" 
        djConfig="parseOnLoad:true, isDebug:true"></script>
    <script type="text/javascript">
    function updateSampleOne(){    
        var contentNode = dojo.byId("sampleone");
        dojo.xhrGet({
            url: "./ASPWebService.asmx/SayHello",
            handleAs: "json",
            contentType: "application/json; charset=utf-8",
            load: function(data,args){contentNode.innerHTML = data.d;},
            error: function(error,args){console.warn("error!",error);}
        });
    }; 
    </script>    
</head>
<body>
<h1>Using Dojo to Consume ASP.NET JSON WebServices</h1>
<input id="btGetString" type="button" value="Update" onclick="updateSampleOne();" />
<div id="sampleone">This will update</div>
</body>
</html>

Breaking down the dojo code...

var contentNode = dojo.byId("sampleone");

This simply get's a the div that we'll be updating.

dojo.xhrGet({
  url: "./ASPWebService.asmx/SayHello",
  handleAs: "json",
  contentType: "application/json; charset=utf-8",
  load: function(data,args){contentNode.innerHTML = data.d;},
  error: function(error,args){console.warn("error!",error);}
});

This is the actual ajax xhr request. Since we are using a GET, we are using dojo.xhrGet.

The Url of our web service is the asmx file followed by the name of the web method. Since it's located in the same folder as this html file, we reference it as ./ASPWebService.asmx/SayHello.

The response will be json, so we set handleAs to json

We set the contentType to "application/json; charset=utf-8" as per the ASP.NET security requirements.

When the response returns, we need to do something with it, this is where load comes in. Dojo uses anonymous functions a lot, and for a simple situation like this, it's pretty easy to follow. The returned JSON is:

{"d":"Hello to Dojo Tuesday, June 03, 2008"}.

So we set the contentNode.innerHTML is set to data.d

Should an error occur, the error function is called.

Summary:

So - while this is a little more convoluted than just using the MS Ajax client library and the Script Manager, it does save us from loading multiple client-side frameworks. Since I'm using Dojo for many other UI components, it seemed a waste to also drag along MS Ajax when this is a viable alternative.

One may ask why use the ASP.NET Web Services model? Well, from the server side it's pretty easy to work with. Once a service is created you can access it via Json or SOAP. They are also easy to unit test.

I'll likely extend this with some more complex examples - like submitting a form, or sending / receiving complex types, but I'll leave it here for now.

Download the sample code

Friday, May 30, 2008
Posted on Friday, May 30, 2008 11:26:39 AM (Mountain Daylight Time, UTC-06:00)  Comments [0] | 
Categories: ASP.NET | DNN | Dojo | Virtual Earth

Starting in February we have been working with the Wildlife Conservation Society to create a map viewer for their Global Avian Influenza site (www.gains.org). This updated viewer launched a few weeks ago, and I thought I'd share a little bit about the project.

About the Project

The project is pretty straightforward, with only a few top level user stories:

- Map the scientific data that has been collected as part of the GAINS program.

- Allow users to filter the displayed this data by :

country,date range,species, and influenza subtype.

Also facilitate this functionality via Url parameters.

- Functionality to be delivered as a DotNetNuke module

- It has to be fast.

Technologies:

We used Virtual Earth for the mapping canvas since WCS needed a high-quality global data set, and this platform is free for non-profits.

For the UI, we dove into the Dojo Toolkit. This ended up working really well, with the exception of the tree view. It was just a huge pain to get the Dojo Tree to work the way we wanted it to, and dropping in the YahooUI TreeView solved these issues in minutes.

On the back end, it's we used ASP.NET. Since this was going to be a heavy Ajax site, we also leveraged the ASP.NET web services which are marked up so that they "speak" json (anyone know if there is a more graceful / concise name for this??? ASP.NET Script Services?). Behind that there is a SQL 2005 tabular database, ArcGIS Server and ArcSDE.

The main UI for the map can be seen below. The data points are clustered, and symbolized based on the match to the filter criteria.

 wme

Data Filtering

The points displayed on the map meet a set of criteria, which can be chosen on this UI. Not that the design is overly beautiful, but it's a mix of Dojo and Microsoft Ajax controls - using the best parts of both. Actually applying this criteria against the database and retrieving the points was non-trivial, so I'm going to write up another post about how we got all that working.

wme-criteria

External Data: Dynamic WMS Tile Caching

The module can be configured to pull in tile services from different WMS servers. This shows my favorite - the global poultry density map. If you are on the site, you can turn these layers on / off this from Map Data --> Manage Atlas Layers. Administrators have a UI to setup the WMS Services. Since mass poultry migrations are rare, most of this data is pretty static. Thus we dynamically cache the tiles on the WCS server so we get improved performance. We do see some offsets in the tiles at the lower zoom levels due to the variations between WGS84 and the Web Mercator projection of VE.

wms-atlas-layers

External Data: GeoJSON Features from ArcGIS Server 9.2

We also have the ArcDeveloper 9.2 REST API for ArcGIS Server 9.2 in the mix, pulling in flyway geometries. For species that have flyways, an extra item shows up in the identify tree. Here's a link to a map showing Calidris alpina, which has flyways. Mouse over a feature, click "View Details" in the pop-up and then expand the tree until you see "Show Flyway". This makes a JSONP call to the REST service running on a different server, grabs the geometries and drops them into the map. Since this species as multiple flyways, multiple polygons are drawn with various colors.

 flyways

It's been a great project to work on, and we are continuing to work with the Wildlife Conservation Society to add additional functionality into this application.

Wednesday, April 30, 2008
Posted on Wednesday, April 30, 2008 9:02:19 AM (Mountain Daylight Time, UTC-06:00)  Comments [1] | 
Categories: Ajax | ASP.NET

Recently I needed to parse a complex query string from a Url, and configure a map based on those settings. This parsing occurs in the server side Page_Load event, and the general process of parsing the Querystring NameValueCollection is straight forward.

The complex part comes from the fact that my application is heavily Ajax based - other than the initial markup, everything else is fetched from the server via HTTP GET's to ASP.NET JSON web services. Still sounds good. But - here's the trick. The query string is passed to the page, not the web service. I guess I could have tried to write all the parsing in javascript and handled it on the client side but there are some pretty complex options that involve database lookups, and I like me some MBUnit Row Tests, so server side was a better option.

Just use Session State...

The first though is - ok - I'll parse the query string, and stuff the resultant "map settings" object into session, and have the client fetch that from the web service. Nice, but no cigar, since the web service can not access the HttpContext that the page is running in. So much for simple solutions.

ASP.NET Page Methods To the Rescue (or not!)

Next contender was ASP.NET "page methods" (aka web methods). These are static methods They should work, as they can access the HttpContext of the page. After parsing and stuffing the "map settings" into the session state, the client side code could call back to the "Page Method", which would grab the map settings from the session and send back to the client. Sweet. After some head scratching and reviewing of sample code that does this, and a bunch of Googling, I found that Page Methods do not work on User Controls (ascx). Since I was creating a module for use in DotNetNuke, there was no way around using a user control. Time to get ugly...

 

Serialize to Json and Cram into the Page...

Yep - the old inject-it-as-it-renders trick. Ugly, but it works. So, in page load, I parse query string, create and populate the map settings class and then use the System.Web.Script.Serialization.JavascriptSerializer  to convert my object to JSON. Some people have reported issues with this serializer, but it's been working smoothly for us so far. If you have problems, you can roll your own or use an open source option like JRock or JSON.Net. From there, I just emit it into the page as part of a script block:

//Serialize out to the browser as JSON

JavaScriptSerializer jss = new JavaScriptSerializer();

//Use this Replace operation to escape out the dates

//see: http://forums.asp.net/t/1070058.aspx post by dotnetsamurai from 5/4/2007

string json = jss.Serialize(initialMapSettings).Replace(@"\", @"\\");

string script = "<script>mapSettingsJson = '" + json + "';</script>";

//Now send the json out

Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "mapsettings", script);

 

As alluded to the in the comments, I ran into all kinds of trouble with dates. Read the referenced forum article for the "why" behind the string replacement bit.

Then in the client side Page_Load (I'm using the MSAjax stuff, so we get a Page_Load that automatically linked into the client page lifecycle), we use the client side deserializer to convert our JSON string back into a handy dandy javascript object.

//Deserialize back into an object
mapSettings = Sys.Serialization.JavaScriptSerializer.deserialize(mapSettingsJson);

From here we just proceed with the rest of the page startup, and we get the correct map.

 

Note for .NET 3.5:

If you have .NET 3.5 installed, then JavaScriptSerializer is marked as deprecated, and suggests you use the DataContractJsonSerializer. And if you are using Complex types (like I am) you will need to add the [DataContract] and [DataMember] attributes to the classes being serialized. I tried both ways and for me they both worked.

Thursday, April 17, 2008
Posted on Thursday, April 17, 2008 9:48:53 AM (Mountain Daylight Time, UTC-06:00)  Comments [4] | 
Categories: ASP.NET

I was doing some work with a database driven version of the Virtual Earth Tile Service HTTPHandler, and needed to do some debugging. I ran into some odd behavior which took some time to track down, so hopefully this saves someone else some time.

Background

To use a HttpHandler, you need to register it in your web.config. Here's what I had :

<system.web>
      <httpHandlers>
        <add verb="*" path="*.ve" type="DBTileHandler" />
      </httpHandlers>
    </system.web>

This just simply routes requests for anyoldthing.ve to the DBTileHandler class, which must implement IHTTPHandler. Very simple.

Debugging Handlers

Typically I open websites "from file" in Visual Studio as this seems to work a little more consistently with VisualSVN (I think this is some issue with my box, as others on my team have no problems opening via IIS). As a result, when I debug the site, it spins up the ASP.NET Development Webserver (aka "Cassini"), and binds to a random port (e.g 2323). Then the site launches on http://localhost:2323/site. All is well and you can step through your aspx pages.

However, you can't debug http handlers by just starting up the site from Visual Studio. You need to change the Start Action of the project to "Don't open a page. Wait for a request from an external application" as shown below.

asp-start-action

Then once you start the web site, and it builds, you need to use Debug --> Attache to Process, and select aspnet_wp.exe.

attach

At this point you can make requests to your handler, and breakpoints will be hit - curiously you attach to the aspnet_wp.exe process even if you are running the site through Cassini.

So, I was happily debugging my tile handler, and got everything worked out, tile were showing up in my VE Map and all was well in the world. Then I went to try it through IIS. Doh! No tiles!

Firebug showed me that the requests were correct, but they were returning with 404's

firebug

I went back and fired it up in Cassini, and got tiles. IIS - no tiles. Cassini tiles. IIS no tiles.

This was quite odd - why would my handler work in one case and not the other? Clearly there is something different between the web servers but what?

To make a long dull story involving much Googling shorter, the answer is that Cassini automatically maps file extensions specified in the Http Handlers section of the web.config, but IIS requires that the file extension mapping be applied explicitly in IIS. Obviously.

Solution

There are two ways to solve this issue:

1) Add the .ve extension to IIS and map it to the aspnet_isapi.dll

or

2) change the extension in your configuration to a type already handled by ASP.NET - such as ashx or aspx.

I chose the latter because it will simplify the deployment of the site. All I did was tack ".aspx" on the end of the path attribute in my web.config and all was working again.

<system.web>
      <httpHandlers>
        <add verb="*" path="*.ve.aspx" type="DBTileHandler" />
      </httpHandlers>
    </system.web>
Thursday, April 10, 2008
Posted on Thursday, April 10, 2008 10:36:00 AM (Mountain Daylight Time, UTC-06:00)  Comments [0] | 
Categories: ASP.NET | Virtual Earth

The last two days we've been setting up a demo integrating the National Geographic MetaLens service with Virtual Earth. It's now live and can be seen at: http://www.metalens.org/mlve/.

ngs

MetaLens is a geospatial content management and archival system that National Geographic uses to secure and manage it's content. "Out of the box" it has a Flash interface, but it also has a nice REST interface. We built some server side code that requests the public assets in the current view, and then applies clustering. At this time, the site is only fetching images, but we expect to extend the demo to include audio and video as well.

Map Tools

What's a map without some tools? The Zoom To tool allows you to hop over to a location where there is some data of interest.

ngs-zoomto

For Lake Tahoe, we have added in a Parcels layer that comes from an ArcDeveloper.net Tile Service.

ngs-parcels

For Vermont, we have a map from National Geographic being tiled using the same code base.

ngs-vermont

You can toggle these layers on and off using the Map Data command on the toolbar. We have also added a Base Map drop down that allows you to select which of the VE base layers to view.

ngs-basemap

 

What's on the Server Side

The main app is a ASP.NET with a data provider class library that actually talks back to the Metalens service. I really like this pattern because it allows us to test the provide completely independently of the web site itself. Basically we just wire up some unit tests, and then via TestDriven.net they are automagically re-run when we build that class library. Sweetness.

MetaLens

MetaLens is built on a product called Clear Path Explorer (CPX) built by a local Fort Collins company called Clear Path Labs. Their web site talks more about CPX, and it's API. From out brief experience working with it, it's pretty nice.

Tile Services

Not surprisingly we are leveraging the ArcDeveloper.net Tile Service. At this time we are just pulling in 2 services, and both use the ArcGIS Server SOAP tile provider. The only wrinkle is that one service is hosted by DTS in our Fort Collins office, and the other is on a National Geographic server.

I have to give a big shout out to my colleague Mike Juniper who did an awesome job tracking down a bunch of CSS weirdness, and put the final polish on the site. We knocked this out in about 2 days start to live. Amazing what you can accomplish these days! There are still a few strange things in IE6, but that's pretty much how it is with that browser. Works great in IE7 and FireFox.

Friday, March 28, 2008
Posted on Friday, March 28, 2008 10:05:07 AM (Mountain Daylight Time, UTC-06:00)  Comments [0] | 
Categories: ASP.NET

Data binding is a really handy way to fill list controls with values from some data source - usually a database, but it can come from almost anywhere. However, there are times when you may want your list box to have some additional items - maybe an instructional message - "Please pick a value..." or an "All" option.

So - just a quick reminder that you can do this very very easily in ASP.NET.

<asp:DropDownList ID="cboCountry"
           runat="server" Width="250px"
           DataTextField="CountryName"
           DataValueField="CountryId"
           AppendDataBoundItems="true">
     <asp:ListItem Value="-1">All</asp:ListItem>
</asp:DropDownList>

Just set AppendDataBoundItems to true, and add in your other values as asp:ListItem entries. In my case the drop down list allows the user to filter data by country. But I also needed a way to have them remove a country filter - i.e. show all the data again. Thus the "All" option, and the -1 value.

Wednesday, March 19, 2008
Posted on Wednesday, March 19, 2008 8:27:15 AM (Mountain Daylight Time, UTC-06:00)  Comments [0] | 
Categories: .NET | Ajax | ArcGIS Server | ASP.NET

Yesterday I focused on the REST, and Javascript API's, and today's task is to get the skinny on the re-vamped .NET ADF.

In talking to some folks, apparently the .NET ADF includes a Javascript API based on the Microsoft Ajax client library. In contrast to the relatively simple Dojo based API, apparently this one has more depth, and with that (one would assume) comes more fine grained capabilities. So, we'll see what some pointed questions can drag out.

At a high level, I think that the .NET ADF may have a tough sell moving forward. The REST stuff is clean and elegant. It may be that on paper it can't do as much, but I believe that there are creative solutions that can step around any limitations. Secondly, everyone I've talked to who has used the 9.2 .NET ADF, well, I'll be charitable and say they are "luke warm" on it. The difficulties they faced in getting good performance, and implementing seemingly simple functionality is what will drive them to try the REST API before the .NET ADF. And if the licensing story for the .NET ADF remains the same, and the REST API is gratis, well, money talks.

I hope that the 9.3 ADF does more than support the ASP.NET Ajax "extender" model (this is how the Ajax Control Toolkit works). From my work with the toolkit (which has been restricted to the ModalPopupExtender and the ListSearchExtender), it's a nice model if you really want to keep your fingers out of the client side code. The difficulty is that since the extenders are setup as part of the page lifecycle, they seem to need everything they interact with to be "runat=server". For reasons I'll skip right now, this is not always easily done. Since all this behavior is implemented in the client, theoretically you can wire up events and use the client side code however you like, but in the Control Toolkit, this is not exactly documented (please send me details if you know otherwise!).

Getting back to the .NET ADF, I hope that the Javascript libraries are designed to be directly consumed and manipulated. Personally I like the Microsoft Ajax client library, and working with server side services is just a slick developer experience. So - I have high hopes.

Monday, February 04, 2008
Posted on Monday, February 04, 2008 5:42:36 PM (Mountain Standard Time, UTC-07:00)  Comments [2] | 
Categories: Agile | ASP.NET | careers
desk Do you have solid ASP.NET, Ajax, Javascript, and CSS skills? Are you a web development guru? If you are, this might be your workstation. Data Transfer Solutions is hiring in our Fort Collins Colorado office, and we need someone who can do some serious web development. We’re an agile development shop and we’re looking for a web developer that will fit well with our existing team. Experience with agile development practices and geographic information systems are a plus but are not a prerequisite. If you’re a web developer and you’re looking for a new place to sit, drop me an email using the Contact link above.
Monday, January 21, 2008
Posted on Monday, January 21, 2008 6:36:06 PM (Mountain Standard Time, UTC-07:00)  Comments [3] | 
Categories: ArcGIS Server | ASP.NET

I keep getting more requests for the code, so I'm just going to put it up here. I sent the code out to a bunch of people over the weekend, and I thought I should put the same disclosure here...

As I said in the posting - this is really hacked up, pure "can it work" research code – it’s based on Rob Blackwell’s VE WMS Tile Server sample, with some “create a map” stuff from the AGS SOAP documentation added, and a few lines of projection stuff smashed in.

Known Issues:

Currently the projection is not exactly right, the caching stuff is just plain ugly, and requires that ASPNET (or whatever identity you run the handler as) have write access to the hard coded path. All the tiles are stored in the same folder, which clearly won’t scale. The Handler matches *.png, which means you need to map that extension to the aspnet_isapi.dll for the web application you drop this into. The SOAP end point is poorly named. And there are no comments of any use.

So – I think that about covers it – you have been warned! That said have fun with it, and let me know how it goes.

Download

Thursday, January 10, 2008
Posted on Thursday, January 10, 2008 10:49:38 PM (Mountain Standard Time, UTC-07:00)  Comments [0] | 
Categories: ArcGIS Server | ASP.NET

When writing a web application, it can be useful to store information between client requests, and this is exactly what "session state" is used for. By default, ASP.NET stores this information in the IIS process, in a mode called "InProc".

However, when you need to run a web site across a number of web servers this model can become problematic. Right now we are working with a client who needs to run the application we are building on two web servers (for redundancy). In front of these web servers is a hardware load-balancer. This particular environment does not support client affinity (i.e. your first request may go to server X, a subsequent request may go to server Y).

In this environment, we can not store the session state on the web server itself. Conveniently ASP.NET supports  two other modes - StateServer and SQLServer. This is configured in the web.config file using the <sessionstate> element.

With the StateServer option, the session state from all the servers in the farm are stored in the RAM of a single server that's running the ASP.NET State Service.

Since we did not have the option of adding another server into our client's architecture (and it would be a single point of failure), we had to go with the SQLServer option.

Setting up SQL Server for ASP.NET Session Storage

There are two options for how the session state is stored - either in tempdb or in a persisted mode in a database. The persisted mode allows sessions to persist through a SQL Server re-start. I opted for the tempdb option.

Despite being a built in part of ASP.NET, it was not obvious how to actually setup SQL Server for this - the documents indicate that you should just run aspnet_reqsql.exe without command line options, and use the wizard. However, this does not install the State stuff. You need to run aspnet_regsql.exe with the -ssadd switch.

This is what I used:

aspnet_regsql.exe -S <server> -ssadd -sstype t

(fyi aspnet_regsql.exe is in C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727.)

This will create an ASPNETDB database where the stored procs are stored, and the actual state will be stored in tempdb.

ADF Support

I did a lot of Googling and searching on EDN, and the ArcGIS Server Forums, and could not find anything that authoritatively said that the ADF would play nice with ASP.NET session state models other than "InProc". The quick answer is that it does work. We are able to run all the basic out of the box stuff without issue.

Tuesday, March 27, 2007
Posted on Tuesday, March 27, 2007 4:30:43 PM (Mountain Daylight Time, UTC-06:00)  Comments [0] | 
Categories: ASP.NET | Team System | Unit Testing
As if the title of the post is not painful enough, I thought I'd just whip up a quick post for others who have run into issues with getting debugging of ASP.NET unit tests working for classes in App_Code.

Just a quick note - ordinarily I'd create a separate assembly, but part of this code is working with the ProfileCommon class, which is created on the fly by ASP.NET based on settings in web.config, and as far as I could find, you can't access this class outside of an ASP.NET web app.

So - I've created myself some classes and had Visual Studio cook up some unit tests for me. I jumped in there and added some legit code, and went to run the test and... I was not getting an error per se - the break points were simply not being hit. In the border of the code, there was a small icon as shown below...


Mousing over this, I got the highly informative message: "The breakpoint will not currently be hit. No symbols have been loaded for this document". So the obvious thing you're thinking is that I need to attach the debugger to the ASP.NET Worker process. Anyhow, that's what some intensive Googling revealed.

The MSDN guidelines on setting up debugging of ASP.NET Unit Tests is here. And while it is conceptually correct, the only issue is that my version of Visual Studio 2005 (Team Edition) does not have "Attach to Process" on the debug menu (not sure why) . Which led to this article which notes that it may also found at "Tools --> Attach to Process". Armed with this, I was able to make some headway...

Here are the abridged steps:
  1. Write some classes in App_Code that you want to test
  2. Create a test project, and have it generate the tests - this is critical as it will build private accessors for you.
  3. Attach the VS debugger to aspnet_wp (XP) or w3wp.exe (Windows Svr 2003)
  4. Then run your test from the Test View (Test --> Windows --> Test Viewer)
Even like this, it's not exactly bomb-proof. I need to re-attach a lot, and start/stop standard debugging of the site occasionally. All the more reason to put your business logic in another assembly if at all possible!

Monday, February 12, 2007
Posted on Monday, February 12, 2007 8:57:11 PM (Mountain Standard Time, UTC-07:00)  Comments [1] | 
Categories: .NET | ASP.NET | Xml
In building ArcExperts.net, I wanted to create a simple blog roll from the OPML file that I was using to run the aggregator. For those not familliar with it, OPML is an xml "format" which describes a list of blogs. i.e...

<?xml version="1.0" encoding="utf-8"?>

<opml version="1.0">

  <head />

  <body>   

      <outline title="Dave Bouwman"

              xmlUrl="http://blog.davebouwman.net/SyndicationService.asmx/GetRss"

        htmlUrl="http://blog.davebouwman.net/"

        description="Software Development :: .NET - GIS - ESRI" />   

  </body>

</opml>


Before digging into this I did a little Googling and found an example where someone was using an XmlDataSource to do something similar. For grins I tried it and had a blog roll up in about 5 minutes. Here's how (download the source to skip the steps):

1) Create a new web project in Visual Studio 2005
2) Copy an OPML file into the root (get ArcExperts one here)
3) Add an XmlDataSource to Default.aspx
4) Point it at the OPML file. In the XPath box enter opml/body/outline
(this just tells it to bind to the outline elements in the file - which contain the items we want in our list)

5) Drop a DataList on the page, and set it's data source to the XmlDataSource

6) Add some markup so we get a nice list

7) Done - view your output and add more CSS to make it pretty


Maybe you all use this every day and are very ho-hum about it, but this was my first time using this, and I think it totally rocks.

Download the source code here

Posted on Monday, February 12, 2007 8:23:01 PM (Mountain Standard Time, UTC-07:00)  Comments [0] | 
Categories: .NET | ASP.NET | Blogging | ESRI
The ArcExperts.net feed is up and running. It caches for 15 minutes, pulls it's own copy of the feeds, and is running through Feed Burner.

You can subscibe using this url: http://feeds.feedburner.com/arcexperts or directly at ArcExperts.net

Neither the site, nor the RSS feed are particularly optimized beyone caching. Specifically, they do not utilize Entity Tags or SkipHours in the source RSS feeds (read more here). Both of these allow the consumer of a feed to be more intelligent about when it pulls feed data - thus saving everyone bandwidth and improving performance. Also, the underlying JRN blog roller code does not specify how far back the posts should be pulled - it just says how many posts should be pulled.  So, for blogs where there are sparse postings - say one in May, one in June, 2 in September and one in November - the current code would fetch all of them because it's just pulling the last 5 posts in the feed (this assumes the person's rss feed is not date sensitive - many are).

If we actually get a reasonable number of subscribers / users of the site, then I'll look at re-tooling it - either with Dimitry Robsman's ASP.NET RSS Toolkit, or just try to re-work the JRN blog roller code to be a little smarter.

Anyhow - it's up and running, and we'll see how it goes. Contact me if you've got questions / ideas / want a blog added or dropped etc.
Posted on Monday, February 12, 2007 9:13:30 AM (Mountain Standard Time, UTC-07:00)  Comments [0] | 
Categories: .NET | ASP.NET | Blogging | ESRI
Just a quick note - ArcExperts.net does not have an RSS feed yet. Your browser or RSS reader may detect a Feed on the page, but it's just picking up the feeds on the Blog Roll.



I'm going to try and whip up an RSS feed this evening, but we'll see how that goes. I think it should be pretty easy to put together as I parse the feed items, but I'm a little unsure about ordering the xml fragments by date. It will also use a 15 minute cache.

Also - If you've asked to have your feed added,  they will be added this evening.

I will post the feed once I have it working.
Sunday, February 11, 2007
Posted on Sunday, February 11, 2007 12:45:32 PM (Mountain Standard Time, UTC-07:00)  Comments [0] | 
Categories: .NET | ASP.NET | Blogging | ESRI
Since PlanetGeospatial is running in "Lite" mode, and I had a few hours while my son was napping,  I decided to put up a blog roller that will aggregate ArcGIS / ESRI Developer blogs. I like PlanetGeospatial, but all the Google Earth / Virtual Earth and Island shoreline stuff was a little off-topic for me.



Anyhow - you can play with it over at ArcExperts.net. I grabbed this domain a year or so ago, and it's just been wasting space, so it seemed like a good home for something like this. As usual, this is a free service - any ads which show up are part of the originating RSS feed.

About the code...
The site is ASP.NET 2.0 code, based on the JRN Blog Roller project that's hosted over at CodePlex.com. This gave me the foundation of the roller itself - conveniently packaged as a UserControl that is easily embedded into any page.

At it's current 0.3 release, JRN's blog roll configuration is a semi-colon delimited list, stored in the web.config file. Since this was kinda kludgy, I grabbed an OPML parser from Bruno 'Shine' Figueiredo's site http://www.brunofigueiredo.com . By folding these two together, I'm able to manage the blog list as an OPML file (download here), and still leverage the bulk of JRN (which actually is pretty lean).

I also took this opportunity to play with XmlDataProvider and found out just how easy it is to work with Xml files in ASP.NET. The blog list is literally 5 lines of code... I'm going to do post about this since it was just so crazy easy to use.

Getting on the Roll...

If you want your blog added to this site, contact me through this site, or over at ArcExperts.net. If your blog is listed, and you want it removed, same thing. Right now it's working with RSS 2.0 feeds and stuff from Feedburner. I'm skeptical if it will parse Blogger.com feeds - if someone wants to have one added, I'll try.

Performance...
I've set things up to cache for 15 minutes, so as long as people hit it occasionally, it should be pretty speedy. I'm still having issues with my hosting, so we'll see how this works out.

Future Additions...
When I have some more time, I'm going to change the main page to background load the feeds via Ajax. From there I'll create a RSS feed from the site, and run that through feedburner.

I'm open to any other ideas, and would love to hear how this works for everyone.

Thursday, February 08, 2007
Posted on Thursday, February 08, 2007 7:21:55 PM (Mountain Standard Time, UTC-07:00)  Comments [0] | 
Categories: .NET | ArcIMS | ASP.NET
I'm on the road this week, but thought I'd post a quick follow up to my posts on creating configuration section handlers for your .NET applications.

This is a case where you may "know" something, but you do something, which leads to really  "understanding" that thing. In this case, I knew that config handlers are cached - the config file is only read once, and the data (objects etc) that you create in the handler are then cached. This is done for efficiency, and that makes sense.

In a project I was working on I created real business objects from my configuration file. In their role as business objects, they maintained selection sets on particular layers (hence the config file) in an ArcIMS application. And actually stored them in Session.

This all worked fine, and things were progressing until two of us hit the app on the same server at the same time. For some reason, we appeared to be sharing the same selection set. I would select a feature, and it would appear on my map, and and the other users map. This caused a few minutes of head scratching before I clued into it - the objects that were created in the config section handler were cached by ASP.NET, and thus each session was accessing (pointers to) the same instance of the objects - thus the selection set appeared to be shared.

Interesting behavior, if that's what you're after. This was easily solved by whipping up a quick SimpleClone method that I could invoke prior to setting other properties and sending the object into session (I opted not to implement IClonable because I did not want to actually clone the whole object hiearchy - just a simple clone of the configuration data). This solved the issue, and showed that despite "knowing" something, sometimes it's hard to see the implications.

Thus, my suggestion is to only create <myobject>Config classes from Configuration handlers - this will help avoid the tendancy to simply use the classes directly, and possibly stuff them into session where you'll get unexpected results.




Tuesday, January 16, 2007
Posted on Tuesday, January 16, 2007 9:41:34 PM (Mountain Standard Time, UTC-07:00)  Comments [2] | 
Categories: ASP.NET | Web Services
I've been running my Server Monitor for a few days, and logged serveral thousand data points - more or less getting "perfmon" type data every minute. Pulling this into Excel we can see some interesting patterns...




As the number of running ASP.NET applications increase (blue dots) they top out around 28. At that point the server runs for a while, until something happens, and ASP.NET is effectively killed - all the worker processes are killed off (the running applications drop), RAM is freed up (we see the Mem Usage drop), and the response time is significantly higher than usual - which makes sense since the server is busy re-starting IIS or ASP.

Anyhow - I'm sending this data off to my hosting provider. I'll let you know how they handle it.

Source Code
Also - as promised - I've uploaded the source code for the web service and the viewer/logger. There is no step-by-step how to for using it - I assume that you understand web services, and WinForms. Just grab the code and have at it. You can contact me if you have problems. The logger is using SQL Express, and the SQL for creating the tables and a view (handy for pulling the data int Excel) is also in the zip.
Sunday, December 17, 2006
Posted on Sunday, December 17, 2006 10:23:35 AM (Mountain Standard Time, UTC-07:00)  Comments [0] | 
Categories: .NET | ASP.NET | Team System
While we all know that Microsoft/Live/MSN etc are all running on ASP.NET, I was just reading a post on Paul Wilson's blog, which referred to an earlier post by Scott Guthrie (for those who don't know, he's General Manager in Microsoft's Developer Division) that says MySpace is also running on ASP.NET 2.0.

Here's a quote regarding scalability (circa March 2006)...

  • MySpace.com is now processing 1.5 Billion page views per day
  • MySpace.com handles 2.3 million concurrent users during the day
  • MySpace.com’s average server CPU utilization went from 85% to 27% after moving (from another technology) to ASP.NET 2.0

Since then, MySpace has apparently surpassed Yahoo! in daily page views. In the comments on Paul's post, we find more interesting news...

One other nice-to-know: They're actually developing with Visual Studio Team System on top of Team Foundation Server. Not a big deal, but when you consider their all-up continuous integration solution, it definitely speaks to the power and capability of the VSTS/TFS combo.

As a developer working with Microsoft tools, it's nice to hear about these sorts of things. Now if I could just cook up a site as popular as MySpace... ;-)

Tuesday, November 14, 2006
Posted on Tuesday, November 14, 2006 9:46:33 PM (Mountain Standard Time, UTC-07:00)  Comments [0] | 
Categories: ASP.NET
I've been spending my "free" time re-skinning my main site, and adding more content into it. It's not fully cooked at this point, but at least I've got the fluid layout working in both FireFox and IE (I think). During my testing, I also found that there are still IE issues on this site are not exactly fixed, so I'm going to look into that as well (sorry if you use IE!).

Here's a shot of the new site - not suprisingly it looks like the blog skin.


It does use the ASP.NET site map to generate the navigation links. Since the out of the box ASP.NET menu control emits tables (yikes!), I'm also using the "CSS Friendly Control Adapters" which enable various controls to emit different markup. Specifically, the menu contol can emit ul / li tags which significantly simplify CSS styling. Once this is done, you can just drop by DynamicDrive's CSS Menu page and pick out the base menu you like, swap out some colors, and you are on your way. Nice and easy.

On a GIS note - I'm also going to be adding more content into the site, including some OpenLayers examples in the Sandbox, and a "white paper" on Code Generation + Geodatabase + ArcDAL.

Monday, June 19, 2006
Posted on Monday, June 19, 2006 5:06:55 PM (Mountain Daylight Time, UTC-06:00)  Comments [2] | 
Categories: ArcGIS Server | ASP.NET | Security
Let’s face it – ArcGIS Server is not fast. Any there are many processes (raster modeling, map production, data extraction etc) that simply can not be completed in the typical web request – response time frame.

In a recent project, I was creating 36 by 36 inch PDF maps, which was taking around 3 minutes (DRG raster layers really slow this down).

My plan was to create an asynchronous web service which we could simply send requests to, and the client would just continue on, and not wait for a response. The web service itself would notify the end user, via email, when the map was ready to be picked up.

Web Method Attributes

In a generic purely .NET situation, this is relatively easy to do – just add the SoapDocumentMethod attribute to the web method…


[WebMethod]

[SoapDocumentMethod(OneWay=true)]

public void CreateMapSheetSynchronous(string emailAddress, string serviceName, string mapSheetId )

{

    CreateMap(emailAddress, serviceName, mapSheetId);

}



When this attribute is in place, the web server returns a HTTP 202 response, which indicates that processing has begun. This occurs prior to the web method being invoked. What’s interesting is that it seems that the ASP.NET impersonation does not work in this situation. When it comes to the point of actually connecting to AGS I get an UnauthorizedAccessException. I added in a little code to show me what the current identify was (WindowsIdentity.GetCurrent().Name) and it was my local ASPNET account. If I change back to SoapDocumentMethod(OneWay=false) the impersonation is working correctly, and right before connecting, I could see the current identity was the impersonated identity that I specified in the web.config file.

Ok – so simply setting SoapDocumentMethod(OneWay=true) is not going to cut it. Time to dig into threading…

Asynchronous Threads
Threading is very cool, and a very hot topic now that multi-core processors are becomming the norm. But, the usual usage is to have some work occur on a background thread, which then notifies the origniating thread that the work is complete. This is not what I want - I want to create a thread, and let it do its work and have it correctly kill itself off. I do not want to have the originating thread wait for it to complete.

Early on in my Googling, I found Mike Woodring’s sample that shows how to support “Fire-and-Forget” for Asynchronous Delegates without leaking. Nice name! Why is this needed you may ask?
Starting with the 1.1 release of the .NET Framework, the SDK docs now carry a caution that mandates calling EndInvoke on delegates you've called BeginInvoke on in order to avoid potential leaks. This means you cannot simply "fire-and-forget" a call to BeginInvoke without the risk of running the risk of causing problems. (from Mike's site)
Mike’s sample code shows exactly how to do this – very elegant, and best of all – it works.  The idea here is that I can call the web service, where I’ve created a delegate to a private method, which actually does the work of creating the map (ok, it calls other classes, but I'm trying to keep this simple!).  Mike’s AsyncHelper class will create a thread and call the delegate, and it will correctly call EndInvoke to clean up memory for me. As soon as FireAndForget is called, the WebMethod exits, and the client can continue. Very nice.

Here’s the web method code:

delegate void CreateMapDelegate(string emailAddress, string serviceName, string mapSheetId );


[WebMethod]

[SoapDocumentMethod(OneWay=false)]

public void CreateMapSheet(string emailAddress, string serviceName, string mapSheetId )

{

    CreateMapDelegate d = new CreateMapDelegate(CreateMapAsUser);

    AsyncHelper.FireAndForget(d, emailAddress, serviceName, mapSheetId);

}


As you can see, the code is pretty simple, so it was quick to implement, but I ran into the same problem – the identity on the new thread was ASPNET, and not the impersonation account. But – this time I was in a better situation. The impersonation was in effect prior to calling AsynchHelper.FireAndForget – thus I could get the identity without having to dig into the Win32 LogOnUser mess. All I needed to do was create a wrapper function for my real CreateMap function which would take an additional argument: the Identity.

So here’s the new Web Method, and the wrapper function…

//Delegate

delegate void CreateMapAsUserDelegate( WindowsIdentity identity,string emailAddress, string serviceName, string mapSheetId );

 

/// <summary>

/// Create a map sheet using an asynchronous web method

/// </summary>

[WebMethod]

[SoapDocumentMethod(OneWay=false)]

public void CreateMapSheet(string emailAddress, string serviceName, string mapSheetId )

{

    // Get the current identity - which is the impersonated

    WindowsIdentity impIdent = WindowsIdentity.GetCurrent();

    CreateMapAsUserDelegate d = new CreateMapAsUserDelegate(CreateMapAsUser);

    AsyncHelper.FireAndForget(d, impIdent, emailAddress, serviceName, mapSheetId);

}

 

/// <summary>

/// Create the map as a specific windows Identity.

/// This simply wraps the CreateMap function

/// with code that re-sets the impersonation on the new thread

/// </summary>

private void CreateMapAsUser( WindowsIdentity identity, string emailAddress, string serviceName, string mapSheetId )

{

// Get the current identity.

    System.Security.Principal.WindowsImpersonationContext wi = identity.Impersonate();

    CreateMap(emailAddress, serviceName, mapSheetId);

    wi.Undo();

}



The CreateMapAsUser simply impersonates the passed in identity, and then calls the CreateMap function. As long as the impersonation identity has access to ArcGIS Server, all is well.

Summary
If you need to create long-running AGS based web services which can handle their own notification, this is a really good solution. I also tested it’s scalability by firing off requests for ~ 20 maps at a time from my unit tests. And it (slowly) ground through all of them!

Wednesday, May 31, 2006
Posted on Wednesday, May 31, 2006 10:27:16 AM (Mountain Daylight Time, UTC-06:00)  Comments [1] | 
Categories: .NET | ArcGIS Server | ASP.NET | ESRI
After my previous post, I got an email from a reader internal to ESRI (since it was an email and not a comment, I'll allow them to remain anonymous) pointing me to two new (April 10th) techincal articles which address lifetime managment.

Problem:  Microsoft .NET Framework applications explicitly manage references to remote COM objects

FAQ:  Do I need to explicitly manage references to remote COM objects in a Microsoft .NET Framework application? (Answer is now No.)

If you apply the referenced patches from Microsoft (they are included in XP Sp2), you no longer need to explicitly manage the lifetime of these objects.

For all my digging on the ESRI site, one thing that led me astray was reading the "Managing Server Resources in ArcGIS Server .NET Applications" white paper (October 2005), which desribes using the IDisposable pattern and WebObject.ManageLifetime. Since this was a "whitepaper", I assumed it was the last word on this, and did not look further. Hopefully they can get this new information into this document or at least it's description.

Anyhow - thanks for pointing me in the right direction. Since I am running XP Sp2, it does explain why adding or removing WebObj.ManageLifetime made no apparent difference.

Tuesday, May 30, 2006
Posted on Tuesday, May 30, 2006 2:14:57 PM (Mountain Daylight Time, UTC-06:00)  Comments [0] | 
Categories: .NET | ArcGIS Server | ASP.NET
One of the big promises of ArcGIS Server is that you can build web services that access ArcObjects. This opens up all kinds of interesting options. But some of the requirements of using those (DCOM) ArcObjects cause problems in object-oriented designs.
 
To illustrate the problem, I'll talk about a web service that I'm currently working on.
 
Map Series Web Service
The concept is quite simple: create a web service that will generate 1:100K PDF maps, based on the selection of a 1:100K tile. The front end will be an ArcIMS powered portal, and since the PDF production takes a few minutes, the resultant map will be emailed to the user. All layout etc is handled in the MXD - the only thing the web service does is swap out some text for titles, and zoom to the correct tile. This is about the simplest "real-world" web service.
 
Since I like to keep my code organized, I created some business objects to actually do the work behind the scenes (this also makes it much more testable, but that's another post)...
 
SheetService Class
This class actually handles the request - the asmx just spins up an instance of this class and let's it do the work. It reads needed info from a config file, creates the other helper classes (below), connects to a SOC, locates the tile feature, zooms the map, and creates the PDF and shoots off the email.
 
MapTitle Class
This parses map title information from an XmlNode passed to it by SheetService as it is reading web.config. It is responsible for setting the text of the title elements in the layout, and then re-setting them back to their original "tags" so they can be located the next time the context is used.
 
TileLayer Class
This class is involved with holding settings from the config file which specify information about the tile layer itself - the layername, the dataframe it is contained in, the field to be used for the Tile Title.
 
TileLocator Class
This class handles drawing the active 1:100K tile onto the locator map. It also parses config info from a node passed to it by SheetService as it is reading web.config. Similar to the MapTitle, after the PDF is generated, this class needs to delete the tile element so that it will not be on the map the next time the context is used.
 
DCOM & ServerContext
The hitch is that with ArcGIS Server, whenever you explicity create a new object, you must do it in the SOC - via context.CreateObject("esriWhatever.SomeClass").  This way the actual object is created in the SOC, and you get back a proxy which communicates back via DCOM. Thus any class that uses ArcObjects also needs access to IServerContext. In order to make sure that these objects will be disposed of correctly, you also need to call WebObject.ManageLifetime once you have a pointer to the proxy. The same goes for objects you create implicitly (i.e. by casting). Thus you also need access to a WebObject.
 
The question is: How to we manage the lifetimes of ArcObjects (and their proxies) in our custom business objects?
 
Solutions?
One option is to pass along both the Server Context and web object into any methods. Passing in a webobject instance is particularly thorny, since it is typically created via a using () block, anything you add into it will be cleaned up when it is disposed. Thus you must be very careful about accessing properties which may expose ArcObjects (proxied) for fear that the webObject disposed, and thus you get null back. This sort of thing could get really really ugly to debug.
 
The next option is to pass in a ServerContext only, and create a webObject as needed, in it's own using block. This makes for cleaner method signatures, but now you are ensuring that you dispose all of your created ArcObjects when the method completes, but what if you need to hold onto some of those objects for longer than your method call? Next!
 
Much of the example code, and lots of code in the forums seems to go with the "Big-Ugly-Function" design philosophy. Simply stuff everything you need to do in one large function, wrap that in a using ( WebObject webObj = new WebObject(true) ) block, call CreateObject and ManageLifetime every other line, and you're off. Just because this is a common pattern does not change the fact that it's evil. Re-use? Nah. Unit Testing? Good Luck! Next!
 
The other option I can come up with is to write all our AGS logic as COM objects that run in the SOC. This is somewhat compelling (and the recommended pattern for Cross-Product Development), as you can do away with all the CreateObject and ManageLifetime stuff, but it would seem that you then have other problems in relating those classes into whatever you are doing on the "client" side of the DCOM connection. And what of the case when only part of your object model is involved in the geospatial aspect of things?
 
What are you doing?
For this project, I'll likely go with wrapping the classes into COM, but I'm very interested in what other people are doing to address this issue. I doubt that I'm the only developer who wants to be able to have a nice clean object model to work with, and DCOM certainly adds another layer of complexity.

Monday, May 29, 2006
Posted on Monday, May 29, 2006 10:40:55 AM (Mountain Daylight Time, UTC-06:00)  Comments [0] | 
Categories: ASP.NET | dasBlog
After much messing around trying to get the dasBlog CAPTCHA to work when my blog was running under a .NET 2.0 application, I finally gave up and moved it out to it's own sub-domain (blog.davebouwman.net). This way both sites can happily coexist - one in 1.1 and the other in 2.0, and CAPTCHA will reliably work.

The main feed is via FeedBurner, so it has been updated. The other problem was avoiding 404 errors for people who have bookmarks to the site, or who come in from Google. The basic problem was how to redirect all requests to the old Url to the new Url.

The pattern looks like this:

www.davebouwman.net/blog/SomeContent.aspx -->  over to blog.davebouwman.net/SomeContent.aspx

This is just the place for a HttpHandler. The handler itself simply intercepts requests, and transforms the Url as noted above, and then redirects the client browser to the new location. Since the main site runs in .NET 2.0, I created a class in App_Code, and had it implement IHttpHandler.

The redirection code for the handler is super simple:

  public void ProcessRequest(HttpContext context)

       {

           context.Response.Cache.SetCacheability(HttpCacheability.Public);

          //re-map the Url

           string inputUrl = context.Request.Url.AbsoluteUri;

           string redirUrl = "";

        

           if (inputUrl.Contains("http://www.")) {

               //drop the /blog

               redirUrl = inputUrl.Replace("/blog", "");

               //repoint the subdomain

               redirUrl = redirUrl.Replace("http://www.", "http://blog.");              

           }

           context.Response.Redirect(redirUrl, true);

       }


Next is register the handler in web.config. The syntax for adding the handler into Web.Config is not exactly clear: Depending on where you handler is implemented, there are two ways to specify the type:

1) If the implementation is in another assembly, then you must put the assembly name after the type. This is what I most commonly found whe Googling, and is standard practice for .NET 1.1

    <httpHandlers>
      <add verb="*" path="~/blog/*.*" type="SomeNamespace.SomeHandler,SomeAssembly" />
    </httpHandlers>


2) If the implementation is in App_Code (ie it's in the running assembly), you do not need to specify the assembly name. This makes sense as in ASP.NET 2.0, there is no assembly name! But this is not exactly clear.

     <httpHandlers>
      <add verb="*" path="~/blog/*.*" type="BlogRedirectHandler" />
    </httpHandlers>

After I got this sorted out, everything was working smoothly on my home system. However, when I moved the code to my hosting provider (www.webhost4life.com), the handler would not work.

Solution
I'll jump to the conclusion, and if you're having strange problems with web.config & HttpHandlers, read the Details section below.

The underlying issue was the in order to have dasBlog live inside a .NET 2.0 web site, I had to create a separate .NET application at www.davebouwman.net/blog. Even though I removed the folder, IIS was still thinking that the application existed. Since it's a hosted system, and I did not have direct access to the IIS manager, I had to muddle around in their ".NET Application" tool - where I finally saw that the application was still mapped in IIS. Once I deleted this application, everything worked.

More Details...

I had moved the /blog folder out of my main site, and did not intend to have a /blog folder at all - the HttpHandler would match the Url pattern, and handle the request without needed a real folder. Only when I made a request, I got an ASP.NET Error stating that the requested folder does not exist. While technically accurate, this seemed like an odd error because I did not get it on my home system.

I then re-created the /blog folder, when I got a "Could not create type" error, which would show the handlers section from web.config. No matter how I setup the handler  - in App_Code or external assembly, it would not load the handler.

So, I suspected that this was somehow related to the fact I used to have an application in /blog, so I changed the path in the handler to /junk/*.* and fired off a request. And it worked. This narrowed it down to something related to the /blog folder. From there I just banged around in the site manager tools that I have access to, and finally was able to delete the ".NET Application".

So - if you are having issues with httpHandlers, getting "Could not Create Type" or "could not create assembly" errors, make sure the path you are trying to handle is not registered as it's own application.

Wednesday, April 05, 2006
Posted on Wednesday, April 05, 2006 6:25:19 PM (Mountain Daylight Time, UTC-06:00)  Comments [1] | 
Categories: .NET |