# Tuesday, January 27, 2009

LINQ and the SqlCacheDependency

When I've needed to invoke a cache mechanism for data on a web site, I've happily used the ASP.Net Cache object over the past few years. It's a nice model for storing a chunk of data under a key until a specific time or for a given duration, providing the application domain doesn't recycle.

I've even hooked up some systems that resemble a Rube Goldberg apparatus to get the same features as a job scheduler, but I don't like to talk about them. ASP.Net will fire an event that you can use when something expires from the cache and therefore give you the "Cache, Wait, Expire, Do Something, Repeat" model for solving a job scheduling problem.

.Net 2.0 and up supported the SqlCacheDependency class. One of its talents is the ability to expire a cached object when the underlying database changes. Before this feature was available, you might wire up specific cache expiration logic in relevant insert, update or delete functions of your app. However, that falls short when the database is updated outside of the trip wires you set in your own code. Another application might update that database, or someone might edit the database through Query Analyzer.

Way back in the day, my team set out to bag the big beast by writing a trigger in SQL Server that would reach out an edit a text file whenever a specific table was modified. This event would be observed by the cache dependency object that was good at watching physical files for changes and ska-doosh! We've got our SQL Server cache dependency working.

Nowadays, those long, verbose methods aren't required. We've got a set of fancy new tools, namely, the SqlCacheDependency object. Or so I thought.

Its great that support goes all the way back to SQL Server 7, albeit via a costly polling mechanism. SQL Server 2005 gave us a direct feedback loop that avoided the polling system. I've know about this feature, as a concept, since it came out but only in the past few weeks have I really looked at the code to use it and I'm honestly a little shocked at the site.

I was really expecting something akin to the familiar practice of checking the cache for the existence of the object, and if necessary, repopulation of the cache. It turns out there's a few more steps, and a little craziness if you want to use LINQ to access your database.

I'll always leave the door open and hope that some day I'll learn the super easy way of doing this, but today, so much of this is in the non-trivial bucket that I'm really tempted to leave it alone and continue my current caching practices.

A year or more ago, I would reach for the data access block inside the Enterprise Library as a default choice. It's a great data access layer and sure beats writing all that goo on my own. Today, I'll reach for LINQ as my default choice. As I understand it, Microsoft is touting the Entity Framework as the future of data access, but today, I'm still sitting contently in the LINQ boat. Who knows what the future holds for me.

To be fair, the example code in MSDN that shows how to implement the SqlCacheDependency actually looks pretty good when you're creating your own Command objects. Not much to talk about there. But I was disappointed when I looked at the LINQ story. Perhaps LINQ just got out ahead too far from the pack and better things are coming.

One of the cleaner approaches to caching LINQ data I've seen was on the MSDN Code Gallery web site. Here, the developer created a sweet extension method that creates the Command object on the fly, executes the query and caches it all inside a generic method. It bears a little more review on my part, but its neat enough that I thought I'd share it. Its less than 50 lines of actual code, but they comment the crap out of it.

The whole idea of having SQL Server notify ASP.Net involves more considerations than the simplistic approach that served me well for years. For example, I can have SQL Server watch a specific table, a set of tables, or a specific query (this last options requires SQL Server 2005 or better). In addition, you'll need to start and stop the entire process when the application domain comes up and gets recycled respectively. You probably with a few lines of code in Global.asax. Finally, you're going to need a few more elements inside your config file that describe a few more details of the cache behavior. All in all, I like the direction, but I'll be happier when/if the LINQ story improves.

#    Comments [0] |
# Thursday, July 31, 2008

Importing Data From Excel In C#

Behind the scenes, I see a lot of companies run on Excel and duct tape. So it's common for clients to hand me a slab of data organized into neat little rows and columns. Excel is just a handy way to throw some data over the wall and get things done. This might be a list of dealers, a collection of user profiles, product information, or anything that just needs to get somewhere else.

Depending on the scenario, I'll might massage this data and slide it into a SQL Server database or an XML file. I'm a web developer so I use the ADO.Net stack on a regular basis. If I were a Windows client developer, I might prefer to solve this problem with the Excel object model, but that really looks like more code to me, so here's how I like to roll:

The following code block accepts an Excel file path and returns an ordinary DataTable object, which can be manipulated easily by the code that calls this method. The first row of the Excel document becomes the columns in the DataTable object and each row thereafter are DataRow objects.

ImportDataFromExcel
(Click to enlarge)

If you like, take a look at both techniques for working with Excel data and see what one speaks to you. With this block of code, I can happily accept large chunks of data from a client without spending precious time fiddling with administrivia.

#    Comments [0] |
# Thursday, May 22, 2008

Joel Spolsky Agrees With Me

Joel Spolsky (of http://www.joelonsoftware.com/) and Jeff Atwood (of http://www.codinghorror.com/blog/) selected my audio question on their new podcast, episode #6.

Stackoverflow is a new property created by a joint venture between these two blogging juggernauts.

http://blog.stackoverflow.com/

So far, their podcast is interesting like Paris Hilton. She’s famous for just being herself. Nevertheless, I am now famous.

I really do look forward to each new episode. Its a nice change of pace from the others that decorate my new iPhone. It's a little like a Seinfeld episode too, its a show about nothing, but in that span of 30 minutes, something interesting always happens. I love the history that Joel sprinkles in and the minutia that intrigues Jeff so much.

My question picked a little bit on Jeff, but hopefully the core of it came through. It was essentially a version of the eternal buy vs. build issue, or in this case "use what's in the box" vs. "build your own" scenario. Jeff had a respectable answer that centered on what he felt was important and what he was happy to inherit from the .Net Framework.

These are the types of debates I have every day building software. Its fun to ping someone outside your normal group every now and again and see what they have to say about it.

#    Comments [0] |
# Sunday, March 23, 2008

Millisecond Update Problem with SqlDataSource

I spent a little too long on this rainy Sunday afternoon tripping over myself. I'm using the SqlDataSource and the FormView controls in a quick prototype. I clicked the "Use optimistic concurrency" field in the SqlDataSource configuration page so it would compare the original values before updating the database.

I was going along fine when I realized the updates didn't work, but the insert was OK. After several repeated attempts with zero rows updates, I fired up the SQL Profiler and saw my update query right there, plain as day. Why wasn't the update working?

sql-profiler

I even grabbed the SQL out of SQL Profiler and ran it through Query Analyzer - and then bam! I could see it on my screen. The milliseconds for the date time fields were all set to zero, and the actual values in the database were non-zero values. Who was loosing the milliseconds?

Finally, it hit me. I was losing the data.

I was trying to be real smart about setting the hidden "Created" and "Modified" DateTime fields during the updating event of the SqlDataSource control. I would do a similar assignment during the update event for just the "Modified" DateTime field.

protected void SqlDataSource1_Inserting(object sender, SqlDataSourceCommandEventArgs e)
{
   DateTime now = DateTime.Now;

   e.Command.Parameters["@Created"].Value = now;
   e.Command.Parameters["@Modified"].Value = now;
}

As I was peering at this method, it struck me that I was inadvertently setting the fields to a value that was too granular for my SqlDataSource object. It wasn't passing back the millisecond value, so I compensated by making sure the value is always zero milliseconds, like so:

protected void SqlDataSource1_Inserting(object sender, SqlDataSourceCommandEventArgs e)
{
   DateTime now = DateTime.Now;
   now = now.AddMilliseconds(1000 - now.Millisecond);

   e.Command.Parameters["@Created"].Value = now;
   e.Command.Parameters["@Modified"].Value = now;
}

With this adjustment, my inserts and updates are playing nicely. Yay!

So, now that i works, I'm still not real happy with it. I'd much rather have the code check a single timestamp column named "Version" than see all that bloat in there.

#    Comments [0] |
# Friday, February 15, 2008

Test for Existence of a JavaScript Method

One of my teammates recently had a problem with some Flash injection code. She was setting some properties and then calling a function on a JavaScript object that threw an error. We got a message that said "Object doesn't support this property or method".

 Object doesn't support this property or method

We isolated it down to a timing issue by dropping an alert() into the code just before we called the method. With the alert() pausing execution for a few seconds, the browser had enough time to fully instantiate our object. Without the alert(), the method was called before it was built. Its a problem that can make your brain hurt.

Here's how you can test for the existence of a JavaScript method before you call it. You might need to do this if you're working with asynchronous calls or dynamic objects that take a while to build.

I created a sample page to demonstrate the issue and how to deal with it. I put three hyperlinks on the page that each call a JavaScript method. Here's the JavaScript code.

Testing for a JavaScript method

At the bottom of the previous code block, you'll see some JavaScript in the mainline section. This code gets executed as soon as the browser sees it. JavaScript is an interpreted language and the browser processes it in a top-down fashion. So, by definition, the browser already knows about my three methods before it runs my code in the mainline.  This is where I define an empty object to test and then create an instance of that object. Just two lines, not much so far.

When I click the link that runs the function named TestForFunction(), I perform some due diligence tests by checking for an undefined object as well as a null object. The last test with the arrow pointing to it is the key part. This will tell me if the method exists without actually running the method. I show one of two messages based on the existence of the method.

The top function named CreateFunction() just spot welds a method onto my object. Even though I've already created an instance of my object, I can still see this method in my instance because I've added the method to the prototype. When this function returns, I've got my new method. When I run TestForFUnction() again, it will see the method.

The bottom method just executes the function I added on the fly. When my DoWork() method fires, it shows a message in the alert() pop-up.

If you find that you need to deal with this type of problem in your code, inspect the object for the method before you call it. If the method doesn't exist, refactor your code to call the window.setTimeout() function for a few milliseconds and check again. Once it exists, your code can continue executing.

#    Comments [0] |
# Wednesday, November 28, 2007

Hey, I got 59 out of 100!

I got a favorable message, so I reckon its not all that bad.

http://www.lhotka.net/weblog/MagenicWantsYouThisTimeWithAQuiz.aspx

After all, Rocky got an 84. I think I rocked most of the ASP.Net questions, but the smart client stuff and multi-threading is a little foreign to me.

What did you get?

#    Comments [0] |
# Friday, November 09, 2007

Double Hop Disaster

I was working with a web service client that talked to SharePoint this week. I wrote a quick Silverlight app that extracted a list of tasks out of a SharePoint list. I love how simple this is to tap into:

http://sharepoint.yourcompany.com/_vti_bin/lists.asmx

The previous URL will show the path to one of the web services provided out-of-the-box by SharePoint. This particular web service provides several methods for dealing with SharePoint lists. I just needed to call the GetListItems() method and pass in the list name and view name as parameters in order to get my data.

This worked fine on my local machine, but when I migrated the solution to a staging server, the SharePoint web service refused to send me the data. After a bit of finagling, I realized the web service was happy to talk to my laptop, but it politely declined the remote web server's request as I lacked the proper credentials. I even was running the web solution with impersonation enabled because I wanted to pass along my Windows credentials during the call from the web server to the remote SharePoint server. I forgot that this is just simply not possible.

For security reasons, the web server does not hold on to my credentials "just in case" I might want to use them later. In this scenario, I do need them - but no dice. This particular list in SharePoint is locked down tight.

I didn't have the proper credentials when my web service client fired off a request to SharePoint. When I was running on my local machine, using Casinni, my credentials were readily available. On a remote web server, the credentials vanish. This is called a "Double Hop" problem - the hops are from the browser, to the web server, to another remote web server. The second hop tanked. It was a silly mistake on my part, but hopefully by writing this down now will help me remember sooner next time.

The code below shows how I solve this problem by manufacturing a credential on-the-fly and then fire off a request to SharePoint. There are multiple ways of solving this problem; I just happened to chose this one.

         MySharePoint.Lists list = new ProjectBoard.MySharePoint.Lists();
 
         list.Credentials = new System.Net.NetworkCredential(
            ConfigurationManager.AppSettings["Credential.Username"],
            ConfigurationManager.AppSettings["Credential.Password"],
            ConfigurationManager.AppSettings["Credential.Domain"]);
 
         XmlNode listItemsNode = list.GetListItems(
            ConfigurationManager.AppSettings["LoremIpsum.Guid"], 
            ConfigurationManager.AppSettings["LoremIpsum.View.Guid"], 
            null, null, "100", null, null);
 
         XmlNode dataNode = listItemsNode.ChildNodes[1];
 
         foreach (XmlNode rowNode in dataNode.ChildNodes)
         {
           // extract values here
         }

Now that I've got this little prototype functional, I can encrypt the credentials in the web.config file by using DPAPI, an encryption tool that makes it easy to protect sensitive information. Since I'm creating new credentials here, I can avoid the Double Hop problem and get on with my prototype.

#    Comments [0] |
# Friday, August 17, 2007

dasBlog v2.0 and discountASP.Net

About a year ago, I attended an MSDN event in my area and won the boobie prize: 12 months of free hosting from an vendor at the event. I had been hosting my site on my company's web servers, so that seemed like a good opportunity to leave the nest and test my skills in the wild. Plus, the price was right.

Man, did that suck.

On Wednesday, I signed up for 12 months of hosting with DiscountASP.Net. They advertised on www.asp.net, and had a lot of good word-of-mouth referrals that eached my ears.

In the matter of about an hour, I did the following:

  • Completed an online transaction with DiscountASP.net
  • Accessed my new site through the Control Panel provided by DiscountASP.Net
  • Downloaded the latest dasBlog bits for ASP.Net 2.0, medium trust
  • Uploaded the dasBlog bits to my new site
  • Switched the DNS servers to my new ISP
  • Moved the content folder from my old host to DiscountASP.Net
  • Minor configuration to secure and customize my blog

I'll have you know that I have not yet contacted DiscountASP.Net by phone or e-mail, nor have I had to read their online help documents. The entire process was smooth as silk. I was shocked. My previous personal and professional experiences have always left me in need of some type of resolution.

This is how it should be — DiscountASP.Net has done a great job and I'm grateful.

I was also relieved by the ease of my dasBlog upgrade. I was about a year off the head in their source code repository. The new version has several sweet skins out-of-the-box and they're easy to customize.

I highly recommend dasBlog for any type of blogging solution — it's a rock solid open source system under active development. Now that I'm 99% out-from-under of my enormous eight month project, I'd love to saunter over to that open source project and try my hand at helping the dasBlog dev team. The weekend is just starting, Le Wife is out of town, and I'm dinking around on a new laptop with Vista. Can't ask for a better set up than that!

#    Comments [1] |
# Tuesday, July 24, 2007

System.Net.WebException : The operation has timed-out.

I've been battling some crazy ASP.NET 1.1 code the past couple of days. First, I was calling some web services with entirely hand written SOAP protocol management. That's always fun. While tedious and too low of a level to be mucking around, it was the fastest means to an end with a crotchity ol' Apache/AXIS web service. Even though I'm fresh off the WCF training regimen; that new stuff wouldn't have helped me here. Perhaps just having the new stuff in my head pushed me in the right direction to tackle this challenge - a man can dream, can't he?

Then, just to rub it in a little, the HTTPWebRequest object decided to give me a swift kick in the shorts. You see, in .Net 1.1, there's a tiny little unknown (unanswered) bug.

I have two instance methods and each make a single call over HTTP with their own HTTPWebRequest object, there's no sharing here. Every time, the second request would hang and throw a "System.Net.WebException : The operation has timed-out." error. The same code runs great in .Net 2.0 so I figure its a framework bug.

I call one method and return a string which is used as an input argument for the second method. Both calls go to the same top level domain, but call to different end points on the domain.

After much thought, trial and error, as well as reading about the same problem other folks were having, I finally figured out the workaround.

It turns out that calling the Abort() method on the HTTPRequest instance after completing the request is sufficient to break up the clog that is preventing subsequent calls. The following code block shows the working helper method. If I comment out the calll to Abort(), it no worky.

private string ExecuteHttpWebRequest(
   string url, RequestMethod method, string message)
{
   HttpWebRequest request = 
      (HttpWebRequest)HttpWebRequest.Create(url);

   request.Timeout = 30000;
   request.KeepAlive = false;
   request.Headers.Add("Cache-Control", "no-cache");
   request.Headers.Add("Pragma", "no-cache");

   switch( method )
   {
      case RequestMethod.Get :

         request.Method = "GET";

         break;

      case RequestMethod.Post :
         
         request.ContentType 
            = "application/x-www-form-urlencoded";
         request.Method = "POST";
         request.ContentLength = message.Length;

         StreamWriter sw = 
            new StreamWriter(request.GetRequestStream());
         sw.Write(message);
         sw.Flush();

         break;
   }

   string html = null;

   using( HttpWebResponse response 
      = (HttpWebResponse)request.GetResponse() )
   {
      Stream rs = response.GetResponseStream();
      StreamReader sr = new StreamReader(rs);
      html = sr.ReadToEnd();
      response.Close();

      // subsequent calls fail without this abort in .Net v1.1
      request.Abort();
   }

   return html;
}	
#    Comments [0] |
# Thursday, July 05, 2007

My First Legit Use of Snippet Compiler

Snippet Compiler

Like many of the three of you who subscribe my blog, I download a bunch of tools & utilities that I read about online and seldom have an opportunity to use on a real project.

Today was my first bona fide use of the Snippet Compiler and it just plain rocked. Its a small client application that can take the place of many throw-away command line programs written just to test out a concept. The application launches fast and I can start writing and executing my code immediately. I don't need to select a project template, name it, or any other of the standard housekeeping items. It even has statement completion!

I was working on an existing ASP.Net v1.1 code base for a quick maintenance project. I gleaned a test order number from the system and quickly realized my specific test required an obfuscated order number from the following "simple" and "natural" function:

//=====================================================================
/// <summary>
///    This method performs exactly the opposite action as
///    EncodeOrderId(), and is meant as the natual companion to that
///    method.  Performs a very simple wrapping bit shift (4 bits wide,
///    towards the most significant bit) on the input value (unsigned
///    32 bit integer) and returns it as a signed 32 bit integer.
/// </summary>
/// <param name="orderId">Value to decode.</param>
/// <returns>Decoded value.</returns>
//=====================================================================
public static int DecodeOrderId( uint orderId )
{
   int newOrderId = (int) (( orderId << 4 ) | ( orderId >> 28 ));

   if ( newOrderId < 1 )
   {
      throw( new ArgumentException( "Invalid orderId: '" +
         orderId.ToString() + "'.", "orderId" ));
   }

   return( newOrderId );
}

I was doing integration testing and further more, I was nearly done. I didn't feel like firing up VS.Net to figure out how to get my test order number obfuscated, so I thought about it and decided to have a spontaneous moment.

I (1) fired up the Snippet Compiler, (2) added a reference to the assembly and (3) wrote a single line of code that called the static method DecodeOrderId() which wrote the result to the console output. Booya!

#    Comments [1] |
# Sunday, May 13, 2007

Portland Code Camp

The PDX Code Camp is next weekend, May 19th and 20th. I've been preparing a talk on how to create and use X.509 certificates. Developers need this technology for local testing of plain old ASP.Net sites, Web Services Enhancements (WSE) or Windows Communication Foundation (WCF) code.

The Windows SDK and Visual Studio.Net have some good tools for helping developers use certificates. I'll show some certificate basics, common examples of certs in action and tools that help us along the way. My goal is to get the session attendees comfortable with creating & installing certificates on their local machine in a variety of code scenarios - that seems like a reasonable task for a 60 minute presentation and 15 minutes of Q & A.

Just for fun, I worked on a local checkout of DotNetOpenID and implemented SSL for the authentication steps. A lot of the other code in the presentation is based on the excellent examples from Michele Leroux Bustamante. She does a great job of providing info on these topics for the developer community.

I have to leave for New London, CT on Sunday so I can only attend one day of this developer event. Normally, that would suck big time, but I'm also gearing up for a week long IDesign WCF Master Class at Carl Franklins house. When it rains, it pours!

#    Comments [0] |
# Sunday, April 01, 2007

C# Operators

I recently became aware of the "??" operator in C#. I guess it doesn't have a real name like the ternary operator that I've long been a fan of using. I'll call it the WTF operator until I learn a better (more popular) name. Let's take a look at the "What The #$@#%@#" operator in action:

public int PageTabId
{
   get { return Convert.ToInt32(Request.QueryString["PageTabId"] ?? "-1"); }
}

Per the MSDN reference, the WTF operator inspects the value on the left side of the "??". If it's null, the value on the right is returned. If its not null, the value on the left is returned. Its a lot like IsNull in T-SQL. The code example above is just a simple helper property for an ASP.Net page. The property looks for a querystring value. If its there, the value is cast as an integer and returned to the caller. If its not in the querystring, the default integer value of -1 is returned.

I've been using the ternary operator for a while. Here's the same property as above, but uses the ternary operator instead. Its more verbose than WTF, but still one line of code.

public int PageTabId
{
   get 
   { 
      return Request.QueryString["PageTabId"] != null 
         ? Convert.ToInt32( Request.QueryString["PageTabId"] ) : -1; 
   }
}

I love writing WTF all over my code!

#    Comments [0] |
# Thursday, February 15, 2007

CardSpace & the Laws of Identity

I attended the Software Association of Oregon (SAO) event today. The Development Special Interest Group (DEV SIG) hosted a discussion about Microsoft CardSpace, the open source framework of OpenID, and basic identity management.

Stuart Celarier walked the audience through Kim Cameron's paper called The Laws of Identity that articulate seven desired aspects of a good identity system.

Microsoft CardSpace was formerly named "InfoCard". This is a joint effort to implement the identity metasystem defined by the laws of identity. CardSpace is the "identity selector" for Windows. It needs IE7 and Microsoft .Net Framework 3.0 to operate. It implements the WS-* specifications in this service.

OSIS - Open Source Identity System: This is an open source group that's involved in the identity space.

Stuart also showed a demo of a system he's been working on. It logs a user into Wachovia banking site using CardSpace.  Scott Kveton of JANRAIN presented OpenID to the SAO DEV SIG group. OpenID hopes to solve the problem of having too many usernames and passwords.

  • Single Signon for the web
  • Simple, light-weight
  • Easy to use, easy to deploy
  • Open development process
  • Decentralized

Your OpenID is a URL: http://kveton.myopenid.com/

  • OpenID comes from the blogosphere
  • Biggest problem with identity; namespace
  • OpenID solves this by using DNS
  • Your identity is a destination
  • You have a unique endpoint on the web

Scott Kveton explained how sites enabled with OpenID enable users to authenticate. Visitors type in their OpenID, and the browser redirects to your OpenID provider. The visitor makes the appropriate decision and the browser redirects back the website.

Scott's site is http://scott.kveton.com

Last week Bill Gates announced support for OpenID. AOL announced support for OpenID this morning. More companies are about to make similar announcments. Here's some interesting stats on adoption:

  • 12-15 million users with OpenIDs.
  • 1000+ OpenID enabled sites
  • 10-15 new OpenID enabled sites each day
  • 7% grown each week with new sites

Kveton also brought up "Microformats" - a way to describe data in an HTML format (contact info, social network, calendar). These can be embedded on pages. There are some interesting ways to use OpenID with these technologies:

  • OpenID + iCal
  • OpenID + hCards
  • OpenID + Social Networking (XFN, FOAP or FOAF?)
  • OpenId + Reputation (jyte.com)

OpenID Predictions from Kveton:

  • 7500 sites supporting OpenID
  • 100 million users with OpenID
  • Big players adopt OpenID

OpenID.net has a ton of info.

Scott Hanselman explained how he enabled OpenID on his blog. Hte added two HTML <link> tags to his website. Simon Willison has an OpenID enabled blog. A visitor can click Sign in with OpenID. The OpenID logo lives inside the textbox. Scott entered his OpenID in the textbox on Simon's site. Using a web service, Simon's blog discovered Hanselman's OpenID provider, then it redirected the browser to Scott's OpenID provider.

Scott's website indicates the OpenID provider is www.myopenid.com

The OpenID provider prompts Scott to authenticate. After a successful login, the browser redirects back to Simon's page and recogizes Scott Hanselman. This is how Simon doesn't need to keep track of usernames and passwords for his blog; a huge benefit.

Stuart helped explain the difference between self-insued cards and managed cards: Business Cards from Kinko's versus a card issued from Visa.

Scott Hanselman displayed a different identity selector using Firefox on Windows. The page contains an HTML <object> tag of type "application/x-informationCard". It wasn't as pretty as the CardSpace in IE7 and .Net 3.0, but it had the same behavior.

There was some last minute discusson on "I-Name", an XRI technology (extensible resource identifier). It sounds like its still being baked.

2idi relays comments on Scott's blog. They will issue an I-Name. =kveton is Scott's I-Name. They have an DNS resolver where visitors may enter xri://=scott.hanselman/photo to redirect to his Flickr account.

#    Comments [0] |
# Sunday, October 15, 2006

Presenting at Seattle Code Camp

I was accepted to speak at the next Code Camp, the weekend of Oct 28th, in Seattle! Hooray!! I thought for a little bit on (A) what would be a fun topic and (2) what do I have to say about said fun topic. I finally settled on talking about something I do on a daily basis: balancing the needs of the web designer, leveraging sufficient power of a great platform (read that as using the base class libraries and everything else given to me), along with the needs of the client and the overall budget.

Our designers at Pop Art are top shelf. They've come up with some fantastic ideas for sites. They're on the leading edge of what's possible with today's browsers and giving consideration to the downlevel browser folk.

Given that, they have some high demands on the HTML emitted by anything on the server. It absolutely, positively must be W3C compliant. It doesn't matter if its HTML 4.01 Transitional, or HTML 1.0 Strict; so long as it conforms to the given specification. Gone are the days of using menu server controls that emitted glorious reams of <table>, <tr> and <td> tags. Enough for you to knit a small blanket. Amen for the CSS Control Adapters.

The designers have a lot to say on usability too. There are just some things that developers will step right over like a country boy; where as the country boy's college roommate visiting for the weekend will stop, stare, point, hold their nose and give it a wide birth.

Enter DotNetNuke. Out-of-the-box, DNN is a developers playground. They know there's so much capability under the hood that they're (and I'm generalizing here) too busy envisioning what they're going to build next instead of rethinking the user interface that a client would need to maintain a site. That seems like small potatoes next to the glorious reams of code we can write.

So, I've settled on presenting the issues, challenges, arguments, counter-points and three-point-takedowns that we've had to address over the past 18 months with DNN. That would be a little too gloomy, so the remaining 67% of the discussion will contain some solutions that bridge the gap and keep the web site looking beautiful long after it launches. My presentation is in no way the rule; simply my experiences in dealing with this issue since I came to Pop Art in 2002. As with most things, I'm sure they are lots of ways to handle them, and I'm as open minded as the next guy; providing the next guy is sans jerk.

A basic introduction of DotNetNuke would be better served by a different session, but people who've never downloaded the bits from www.dotnetnuke.com will still get a reasonable insight into the problem sets and ways to deal with them.

My basic fear is probably the same as any other presenter who ever presented in all of presentation-land: getting slotted in the same time slot as ScottGu or anyone else in the rock star line up. What a problem to have!!   :)

#    Comments [0] |
# Tuesday, September 12, 2006
# Saturday, September 02, 2006

Flippin Encodings!

Getting stung by the simple stuff with Url Encodings.
#    Comments [0] |
# Tuesday, August 22, 2006

This site has moved

I attended a recent MSDN Event and won a free one year hosting contract at www.hosting.com.
#    Comments [0] |
# Tuesday, August 08, 2006

You may receive an InvalidCastException error in an ASP.NET-connected Web application

How to fix errors with dynamically loaded web user controls in asp.net that throw an invalidcastexception.
#    Comments [0] |
# Sunday, July 30, 2006
# Monday, July 24, 2006
# Friday, July 14, 2006

Server Side Comments

Use server side comments to temporarily block ASP.Net from handling controls.
#    Comments [0] |