CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Karl Seguin

.NET From Ottawa, Ontario - http://twitter.com/karlseguin/

May 2006 - Posts

  • Communities and Fragmentation

    For the better part of three years I’ve been troubled by the seemingly exponential growth of crap on the internet – specifically with respect to programming topics (more specifically with respect to .NET). In the past I’ve blogged against poorly written documentation and FUD, but that’s only the tip of the iceberg. The problem is quite simple: quality technical information is increasingly difficult to find.  A chief cause for this is the sheer amount of content being published – increasingly so on personal blogs, which are outside of anyone’s jurisdiction.  In fact blogging is so easy and effective that countless clueless developers are now technical authors. A dangerously high percentage of developers will gladly follow any advice they find on the web.  The noise to signal ratio is just too high.

    A while ago, I came across a criticism of feed readers for their dumbness on glassdog as well as a comment from Nick Bradbury (creator of FeedDemon) which aligned themselves with my concerns.  As we all know, information management is currently the big thing (think search). Google has changed the nature of information indexing on the web, and huge players are trying to get back into the market space.  But Google’s scope is too big to be applicable to something small like .NET programming – I’m not even sure how feasible it is to programmatically determine the quality of a technical article.

    I do agree that we need much smarter clients – but that seems complicated. A simpler solution, as far as I’m concerned, is to build stronger and well-defined communities. Unfortunately, ever since the forced break up of weblogs.asp.net, I’ve been keenly aware of fragmentation within the .NET Community. CommunityServer is a great tool when it comes to, what I like to call, small-c communities. What about the big-C .NET Community (there are actually some decent big-C features in CommunityServer). If Paul Vick blogs an absolute gem (which he often does), what percentage of .NET developers are going to see it? Sites like DotNetSlackers and DotNetKicks have a lot of potential and maybe that’s the way we ought to keep going.

    Part of me wants Microsoft to do something wildly innovate and change the face of web-based communities; something open and pluggable. MySpace is just an imitation of other blogging engines – it isn’t a community, simply a repository. There are a lot of organic indexing examples out there: del.icio.us, digg, Slashdot.

    For readers of Kevin Kelly’s out of control, I ask “Where’s my .NET vivisystem?”


  • HttpHandlers – Learn Them. Use Them.

    My first blog post from Word 2007 – let’s see how this goes.

    Introduction

    There are many features in ASP.NET that are unfortunately underused. Sometimes a feature gets looked over because it’s too complicated. Other times, like in the case of HttpHandlers, it’s because they are poorly understood. For the longest time I understood the concept and implementation of HttpHandlers, but I just couldn’t figure out under what circumstances I’d use them.

    Googling HttpHandlers it’s obvious to me that bad tech writers are squarely to blame. A shameful amount of examples are nothing more than “hello world.” The problem with such a limited example is that it leaves the reader thinking “so? I can do that with an aspx page!” Without understanding what problem space HttpHandlers are meant for, it’s impossible to get developers to use them.

    As an ASP.NET developer, HttpHandlers are important because they are the earliest possible point where you have access to requests. When a request is made to IIS for an ASP.NET resource (.aspx, .config, .asmx), the ASP.NET worker process internally creates an instance of the right HttpHandler for the request in question and effectively hands off the task of responding to the request. How does ASP.NET know which is the right HttpHandler for a given request? Simple, via configuration files, paths are mapped to http handlers. For example, if you open your machine.config file you’ll see a list of default mapping. For example:

    <add verb="*" path="*.aspx" type="System.Web.UI.PageHandlerFactory" />
    <add verb="*" path="*.config" type="System.Web.HttpForbiddenHandler" />
    <add verb="*" path="*.asmx" type="System.Web.Services.Protocols.WebServiceHandlerFactory" />

    So every time any .aspx page is requested, the PageHandlerFactory is left to fulfill the request. HttpHandlers can also be added or changed for specific sites in the web.config. Handlers aren’t just mapped to extensions, your own handler can be mapped to “HandlePingback.aspx”, in which case it, not the PageHandlerFactory, will be called upon.

    An HttpHandler is actually any class that implements the System.Web.IHttpHandler interface. To be of any use it needs to be mapped to a path. (I lie, PageHandlerFactory doesn’t implement IHttpHandler. Instead, it implements IHttpHandlerFactory. IHttpHandlerFactory defines a method named GetHandler which returns an IHttpHandler. We won’t cover IHttpHandlerFactories here, but it’s basically a layer between the internal ASP.NET process and the handoff to the HttpHandler. Either way, in the end you end up with a class that implements IHttpHandler). The IHttpHandler interfaces defines the very important and aptly named ProcessRequest. Basically, this is ASP.NET saying “hey you! Process this request!”

    Built-in Handlers

    If we look at the most important HttpHandler, the System.Web.UI.Page class (yes, the same one that all your pages inherit from), we really start to get a good feel for what an HttpHandler is responsible for. Looking at the internals of the Page class and starting from the ProcessRequest function, we quickly get to a ProcessRequestMain function which really starts to interact with stuff you do on a daily basis. Look at some of the stuff that happens in ProcessRequestMain:

    ...
    base.InitRecursive(null);
    if (context1.TraceIsEnabled)
    {
          this.Trace.Write("aspx.page", "End Init");
    }
    if (this.IsPostBack)
    {
          if (context1.TraceIsEnabled)
          {
                this.Trace.Write("aspx.page", "Begin LoadViewState");
          }
          this.LoadPageViewState();
          if (context1.TraceIsEnabled)
          {
                this.Trace.Write("aspx.page", "End LoadViewState");
                this.Trace.Write("aspx.page", "Begin ProcessPostData");
          }
          this.ProcessPostData(this._requestValueCollection, true);
          if (context1.TraceIsEnabled)
          {
                this.Trace.Write("aspx.page", "End ProcessPostData");
          }
    }
    base.LoadRecursive();
    ...

    As you can see, it’s this method that’s responsible for causing all those ASPX events, such as OnInit and OnLoad, to be raised. In essence, the Page class does what it’s supposed to do: it’s handling the request.

    Another handler we saw listed above is the HttpForbiddenHandler (which is a straight handler as opposed to a HandlerFactory). A number of paths are mapped to this handler – generally files that might compromise a security risk if left publically accessible (like .config, .cs, .vb, .dll, …). The ProcessRequest for this handler is to the point:

    public void ProcessRequest(HttpContext context)
    {
          PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_NOT_FOUND);
          throw new HttpException(0x193, HttpRuntime.FormatResourceString("Path_forbidden", context.Request.Path));
    }

    Why use a handler?

    There are likely few times where you have to use a handler. Almost anything you can do in a handler, you could simply create an aspx page to take care of. So why bother? There are two main reasons. First and foremost, HttpHandlers are far more reusable/portable than pages. Since there’s no visual element to an HttpHandler (no .aspx), they can easily be placed into their own assembly and reused from project to project or even sold as is. Secondly, the Page handler is relatively expensive. Going with the “Hello World” examples, if you do that in a page you’ll end up raising a number of events (onInit, onLoad, onPreRender, onUnload, …) and make use of a number of ASP.NET features such as viewstate and postback. In most cases, the performance hit is negligible, but it nonetheless highlights that you’re using the page framework when you have no need to.

    Real Examples

    The first example to look at is the TrackbackHandler than’s part of CommunityServer 1.1. If you go to http://code.communityserver.org/ and open 1.1/Blogs/Components/TrackbackHandler.cs you’ll see the relevant source code. The purpose of this handler is to track pingbacks made to blog entries. Most blog engines will automatically send a pingback to any linked posts. This means that blog engines must also have a way to capture these pingbacks and record them. There’s more or less a standard between how the communication is supposed to happen, but each blog engine is really on its own as far as implementation. Without spending too much time in the code, we can see that the handler looks for a number of POST parameters and creates the trackback based on what’s passed in.

    There’s absolutely no reason why all of this couldn’t be done using an ASPX page. But as I’ve already mentioned, that would force the entire ASPX page framework to be invoked. Additionally, this handler doesn’t even have a visual element – so a page doesn’t make too much sense.

    (you can look at the web.config to see how the handler’s added).

    Another example is my open source AMF.NET project which makes it possible for a Flash application to communicate with server-side ASP.NET code. The AmfGetwayHandler deserializes the AMF input (AMF is a proprietary binary protocol used by Flash), executes the right server side .NET function and returns a serialized response. Again, a single ASP.NET page could be used to accomplish the same thing, but then it would be impossible to package AMF.NET as a single assembly.

    Another common example you’ll run across is using HttpHandlers to generate RSS feeds. Many applications will map “Rss.aspx” to an HttpHandler which generates a XML feed.

    Why not to use HttpHandlers

    The biggest and very significant drawback of HttpHandlers is that they can only be used for extensions that are mapped to ASP.NET in IIS. It might be great to create a file download counter for your .zip files using an HttpHandler, but since IIS doesn’t go through ASP.NET to serve .zip files, it isn’t going to work. One solution is to map those extra extension to ASP.NET, but that might have undesirable side effects and might not even be possible for you (many developers don’t have direct access to IIS). In this case, the only solution is to create an ISAPI filter which is much more difficult. IIS 7 promises to let us write ISAPI filters in .NET (or extend HttpHandlers beyond the ASP.NET pipeline depending on how you look at it), but that’s still a ways away.

  • Have I inherited a disaster?

    I hate taking over someone's bad code. The projects always go on forever and you are forced to follow bad practices to try and string together fixes and features without it all crumbling down. There are some quick, not always 100% accurate, ways to know whether you've inherited a disaster. I'm strictly talking about enterprise scale projects here, some of these practices might be ok for smaller stuff. Some of these are hard to quantify (what's too much?), it's all very relative to the size and type of your project.

    (if you are interested in a more detailed analysis, I'd recommend NDepend and FxCop. I actually recommend FxCop for your day to day programming, it's a wonderful tool. Both are free)

    Hit ctrl-shift-f from the 2nd dropdown box, select to look in the "Entire Solution" and enter the following into the first box:

    Exceptions
    1 - 'catch'
    The results that get displayed will give you a good idea about what type of exception handling you'll have to deal with. If there are many lines with just "catch",  "catch(Exception XYZ)"  or "catch(XYZ)" you've got some bad exception swallowing happening. You should expect to see this no more than once or twice (in a global handler). If you are seeing specific exceptions being caught, all's good.

    2 - 'throw;' and 'throw e'
    If "throw;" returns a lot of hits, I'd say it's a good sign - the previous developer understood not to mess with the stack trace when rethrowing exceptions (you should spend a bit of time figuring out what he/she was doing catching the exception in the first place). "throw e" implies that exceptions are being caught and poorly rethrown.  I use "throw e" because most people will name their caught exception "e" or "ex".

    3 - 'throw new'
    Unless there are a lot, this is a good sign. The developer wasn’t afraid of throwing exceptions. A good way to tell if he or she might have been over zealous is to see if these are spread out amongst many classes. In my experience, throwing exceptions tends to be centralized in like-classes.

    4 - ': Exception' or ':Exception' (VB.NET 'Inherits Exception') (also try ApplicationException)
    It's hard for me to imagine large projects without at least some custom exceptions.

    5 - 'using (' and 'using(' or 'finally'
    Developers who can't cleanup their resources aren't likely to cleanup their code - sorry. Hope for a lot of these!

    6 - 'on error resume next'  (VB.NET only)
    RUN!

    (FxCop does a lot of analysis around exception usage, so I'm going to recommend that you use it again.  You can learn more about exception handling here.)

    Strings
    5 - 'StringBuilder'"
    Few applications get away without needing to do a good amount of string concatenation. There've been so many blog posts about the importance of the StringBuilder class, that if you don't find any it not only means you likely have ugly string concatenation and performance issues, but the developer didn't spend much time learning and reading.

    6 - '.Fomrmat' and '.AppendFormat'
    I've expressed how important I feel string formatting is in the past, so no one should be surprised to see it in this list. Have fun maintaining impossible to read code if you aren't seeing many of these. (if the line looks like an SQL string, then take a quick skip to #9)

    7 - '.ToString("'
    Somewhat related to the above item, .NET has great built-in functionality to help you format all types of data (dates, numbers, etc) into meaningful string. You'll likely find a lot of ToString()'s in your code, but you should also find a healthy number of ToString("XYZ")'s


    Data Access
    8 - 'SELECT ' or 'UDPATE ' or 'DELETE ' or 'INSERT '
    In my experience, people who use inline SQL statements simply haven't bothered to learn how to use stored procedures. Now you get to pay the price for their laziness.

    9 - '+ " AND'  (VB.NET should also do '& " AND')
    Inline SQL statements aren't the end of the world, but injecting parameters via string concatenation is horrible (even if done via a string.Format). There's a good chance the code has security holes, sorry.

    10 - 'new DataSet'
    While not everyone will agree with me that DataSets shouldn't be the foundation of an enterprise scale project, it's good to know that you'll be dealing with them.


    ASP.NET
    11 - '"<' (change the file type to *.vb;*.cs)
    You've possibly just found HTML or XML manually being put together. Who needs ASP.NET or System.XML, not your predecessor!

    12 - 'Response.Write'
    See above

    13 - 'HttpContext.Items'
    The HttpContext is a new introduction to ASP.NET and it's quite powerful. Unfortunately it's also quite underused. Seeing the variables stored and retrieved from the HttpContext.Items collection is an good sign

    14 - 'OutputCache' and  'Cache.Insert'
    Despite the fact that server side programming exists to deliver dynamic content, there's almost always something that can and should be cached. You'll often see programmers coming from another language overlook this because it's not something you'll find in many frameworks.

    15 - 'configSection' 'httpModules' 'httpHandlers' (in the web.config)
    Seeing custom configuration, use of HttpHandlers and HttpModules should be a very encouraging sign. It's a decent sign that the previous developer had a solid grasp of ASP.NET. I wouldn't panic if I didn't see these though.

    16 - 'SqlDataSource'
    While there might be some debate about the properness of DataSets in enterprise-type application, hopefully we can all agree that you shouldn't find any SqlDataSources in your code.


    OO/Language
    17 - 'interface'
    If you don't find any interfaces in your project, you could be in trouble - especially if there are a lot of classes.

    18 - '(I' (dunno about VB.NET)
    I'm not sure what it means if you find a lot of interfaces (above), but not much code that's casting to them. I think I'd be happier a few hits on both.

    19 - 'abstract'  (VB.NET 'MustInherit')
    Somewhat similar to interfaces, there's no reason that you don't find a couple abstract classes in your code.

    20 - 'internal' (VB.NET 'friend')
    I'm a big fan of minimizing the public API published by an layer. In many cases, a public class, class member, interface or enum should really be internal. I've seen surface areas reduced by as much as 50%.

    21 - 'sealed' (VB.NET 'NotInheritable')
    There's no reason to panic if you don't find a sealed class in your code, but if you do, it's likely a good sign.


    Misc
    22 - 'GC.'
    People who mess with the garbage collector are either very desperate to cover up a deep architectural problem, or simply think they know better than everyone else. Both of which spell doom for you.

    23 - 'Int16' or 'short'
    More amusing than anything, this often indicates a micro optimization that's actually going to cause a performance hit.  

    24 - '(string)Request.Q' or 'Convert.ToString(Request.Q)' (in VB.NET try 'cstr(Request.Q')
    I wouldn't have mentioned this obvious one had I not just seen it, but come on, my grandma doesnt' unecessarily cast!

    25 - 'List<' and 'Collection<' and 'Dictionary<' (VB.NET 'List(Of' and 'Collection(Of' and 'Dictionary(Of')
    If you're working on an 2.0 project, I hope you'll find that generics are being used throughout the code. They are by far the best new feature of 2.0 and it's a shame if they aren't being taken advantage of. (note, you could search for the System.Collection.Generics namespace, but I'm pretty sure the default C# template includes)

    26 - 'assert'
    If you don't get any hits, there's little chance that the code in question was unit testing. Finding asserts doesn't guarantee anything, but it's a good start.


  • Should Response.Redirect really throw an exception?

    Every now and again, we get a question in the newsgroup asking why they are getting a ThreadAbortException. Code is provided, and 99% of the time a Response.Redirect is always the culprit.

    I've answered this atleast 10 times without really thinking about it - though I always make sure to give a thorough explanation. (I just woke up, but i'm pretty sure it's REsponse.End() that throws the exception, which is called internally by Response.Redirect()).

    Today though, for some reason, it seems really wrong that Response.Redirect throws an exception. What's worse is that one of the overloads, Response.Redirect(string path, bool end); doesn't. I see two problems here. First of all, it really isn't an exception situtation? People call Response.End() and Response.Redirect() all the time - heck, they are public functions! You are guaranteed that calling these public functions will result in an exception - doesn't seem right.

    The 2nd problem is that an overload of Response.Redirect() doesn't generate an exception. Obviously it's alright for overloads to behave differently, for example:

    public static DoSomething(Page page)
    {
    }
    public static DoSomething()
    {
       if (HttpContext.Current == null || !(HttpContext.Current.Handler is Page)
      {
            //throw exception..
      }
      DoSomething(HttpContext.Current.Handler);
    }

    But for one of them to _always_ throw an exception just seems wrong (reading over that I'm not sure there's a difference between my 1st point and my 2nd point, but whatever)...

    I imagine the reason the exception is thrown is for some deep design reason needed by the ASP.NET framework. Insight would be welcomed...




  • XSL gives me the warm and fuzzy

    I don't get to play with it often, but when I do, I absolutely love it. There's just something I love about messing with XSL (it ranks right up there with looking at SQL execution plans - seriously!)

    I think it comes down to taking a break from the rich enterprise type of stuff I'm used to.  I can forget about tiers and coupling and all that crap and do something very visual very easily. It's even more basic than classic-asp because your query language is directly embedded in your programming language which is embedded in your presentation framework.

    Sure, even slightly complicated things is likely to turn into a mess, but some projects call for that (so use the right tool for the right job).

    It probably means I'm really gonna dig LINQ - woot (sometimes I find myself prefering XPath over SQL - I think that's just 'cuz I'm less bored with it).  The interchanged HTML and control logic is also fun. It's like a mix between declarative programming (XAML) and classic ASP (embedded for/if with the html).

    Anyways, I ramble..but I want to know if there's anyone else out there that finds themselves liking it more than they should :)


  • Wishlist: System.Exception constructor to support string formatting

    As I've indicated in the past, I'm a pretty big fan of string.Format and anything else that accepts a string format (Databinder.Eval, ToString,....). I find myself using string.Format a lot when throwing exceptions:

    throw new FuelConfigurationException(string.Format("Missing required attribute \"{0}\"/", attributeName));

    It'd be cool if the base System.Exception class had a constructor that looked like:

    public Exception(string message, params object[] arguments);



  • ASP.NET isn't always the right tool

    In my 2.0 isn't a silver bullet post, Alex Lowe made it clear that my enterprise development perspective, while valid, only represents a portion of the ASP.NET developer community. The reality is that Alex is right, but should he be?  Alex argues that there are plenty of sites that "should be written using 'crappy' SqlDataSource control, DataSet, 'improper' paging, etc." I honestly wonder, should these sites be written in ASP.NET to begin with?

    I say it often, but ASP.NET is really hard. Sometimes I wonder if I over exaggerate how hard it is, or if I'm the only one who sees, but time and time again I see such horribly written code that I know I'm right. I joke not; today I'll get to maintain an application that uses:

    <!--#INCLUDE FILE="footer.aspx"-->

    And that's just one of literally hundreds of ASP.NET WTFs I've run into over the past 5 years. In my experience, the dont-get-its far outweigh the get-its and I have a hard time believing I've just been really unlucky.  Visual Studio 2005 and ASP.NET 2.0 have done their best to make things better, but the difficulties are so fundamental that no amount of abstraction is going to work. Worse, the abstraction is so high level, and the details so low, that you often get pretty serious leaks.

    Almost all of the features that make ASP.NET a superior enterprise choice are significantly more difficult than what's offered by the alternative web technologies. The page lifecycle is an incredibly powerful tool when understood and harnessed, otherwise it's just a stupid pain in the ass that's going to get in your way. Postback and viewstate are great, but it's a significant shift from simply posting to a different page. So much so that 2.0 lets you do cross page postbacks simply because the paradigm shift was too much. But crosspage postbacks are a perfect example of leaky abstraction - the first time you use it, it'll make a hell of a mess.

    The issue runs much deeper than ASP.NET. C# and VB.NET are relatively hardcore languages. The entire VB6 support fiasco is more than enough proof. VB.NET is much harder than VB6, VBScript or PHP. Drag and drop gets you so far, then you are stuck having to deal with exceptions and classes. If you are one of the developers Alex is talking about, you are likely much better off with an untyped, procedural language. Even turning off Option Explicit/Strict won't get you 1/2 way there. Then there's the .NET framework itself with the thousands and thousands of methods, which you'll need to use. Whether it's encoding  ADO.NET, XML or IO you'll have to get your hands somewhat dirty.

    In my very humble opinion, PHP is a far better alternative to ASP.NET when it comes to the type of application Alex is talking about (I'd say classic ASP too if it was still being developed and probably Ruby on Rails if I knew more about it).  As a language, despite some claims, it isn't at all an OO framework or much of an OO language, it's exception handling is pretty primitive and it's untyped. It's also a traditional web structure, doing away with complicated bound controls, postbacks and server controls. What to display 10 rows? loop through your record and echo out some tr's and td's - simple. The only hang up with PHP some might have is getting it installed and the language if you're a VB'er - but again, you'll have serious language issues in ASP.NET anyways.

    Everything .NET is simply amazing. I love ASP.NET, C#/VB.NET, ADO.NET and SQL Server. I can't imagine doing web programming without writing HttpHandlers, code generating my domain layer and creating a lot of simple custom server controls. I'm amused that anyone would attempt to do serious enterprise architecture in a language/framework that wasn't so powerful and complete (there are other alternatives, PHP isn't one). Like most development tools however, try as they might, Microsoft will never make ASP.NET the best enterprise tool while at the same time the best mom-and-dad solution (or even close to).

    ASP.NET is cool, but if you don't get it I'd strongly urge you to use something else - I keep hearing amazing things about Ruby on Rails. Remember, it's in Microsoft interest to tell you how great their products will be for you, but it might not really be the case.  Use the right tool for the right job, and if you don't even understand the tool, than it isn't the right one!


  • SQL Server - No Arrays? No Problem!

    Just a couple weeks ago I was pointing to the lack of a LIMIT keyword in SQL Server 2005. Last week, I was reminded about the lack of arrays.  I have a hard time believing that a platform/language/framework/anything aimed at developers in this day and age doesn't support arrays, but we'll assume the SQL Server team had more important things to do the past 6 years.

    You'll run into the array-wall frequently while doing SQL Server programming, but to me, the most obvious example is when dealing with a CheakBoxList. The CheckBoxList is a perfect example; because it's typically varying length (the user could pick any number of values). Take for example a blogging system that support "tags". An author could pick 0 or more tags and then hit the "Post" button.

    private sub Post_Click(o as object, e as EventArg)
      dim post as new Post()
      post.Subject = Subject.Text
      post.Body = body.Text
      foreach item as ListItem in Tags.Items
        if (item.Selected) then
          post.Tags.Add(new Tag(item.Name, item.Value))
        end if
      next
      post.Save()
      'do something nice here...
    end sub


    You'll eventually end up into a method that looks like:

    friend sub
    SavePost(post as Post)
     
    using connection as new SqlConnectio(GET_FROM_CONFIG)
       
    using command as new SqlCommand()
           'now what?
        end using
      end using
    end sub


    It'd be sweet if we could pass in an array of TagIds, like:

    command.Parameters.Add("@TagIds", SqlDbType.Int()).Value = post.TagIds

    But we can't.  One solution might be to write some dynamic SQL...but that's no fun to maintain. Another is to hit the database multiple times:

    foreach tag as Tag in post.Tags
       SaveTag(post.Id, tag.Id)
    next


    This isn't a bad idea, and can even be done in a single connection.Open(). I prefer a third solution though. In SQL Server 2000, I would pass in a comma-delimited string and convert it into a table, something like:

    CREATE PROCEDURE SavePost
    (
      ...
      @Tags VARCHAR(2000)
    )
    AS
    SET NOCOUNT ON


       DECLARE @tagIds Table
       SELECT @tagIds = dbo.CsvToInt(@Tags)
     
     
       --INSERT THE POST
       DECLARE @postId INT
       SET @postId = SCOPE_IDENTITY()
       
       INSERT INTO PostTags
          SELECT @postId, [value]
             FORM @tagIds

    SET NOCOUNT OFF


    Here's what my CsvToInt function might look like:
    CREATE  Function dbo.CsvToInt
    (
       @Array VARCHAR(2000)
    )
    RETURNS @IntTable TABLE (IntValue INT)
    AS
    BEGIN

      IF @Array <> '' BEGIN
        DECLARE @separator char(1)
        SET @separator = ','

        DECLARE @separator_position INT
        DECLARE @array_value VARCHAR(2000)

        SET @array = @array + ','
        WHILE patindex('%,%' , @array) <> 0 BEGIN
          SELECT @separator_position =  patindex('%,%' , @array)
          SELECT @array_value = LEFT(@array, @separator_position - 1)
          INSERT @IntTable Values (CAST(@array_value AS INT))
          SELECT @array = stuff(@array, 1, @separator_position, '')
        END
      END
      RETURN
    END



    This solution has a number of limitations. The first being the length which might prove problematic in some situations. Worse though is that it only really works for comma delimited integers. What happens if you want to pass in a more complexe structure, like a name=>value collection.

    Well, with SQL Server 2005, things have gotten a lot better. Instead of passing in a CSV, we can leverage the SQL Server's XML capabilities.  Changing the paramters of what we are trying to do (from a simple csv, to a key=>name bulk insert), we first need to convert our collection into some XML:

    friend shared function NameValueToXml(data as NameValueCollection) as string
      dim sb as new StringBuilder("<data>")
      foreach key as string in data
        sb.Append("<meta>")
        sb.AppendFormat("<key>{0}</key>", key)
        sb.AppendFormat("<value>{0}</value>",data(key))
        sb.Append("</meta>")
      next
    end function


    Which means we'll end up with a string that looks like:
    <data>
      <meta>
        <key>Some Key</key>
        <value>Some Value</value>
      </meta>
      <meta>
        <key>Some Key 1</key>
        <value>Some Value 1</value>
      </meta>
      <meta>
        <key>Some Key 2</key>
        <value>Some Value 2</value>
      </meta>
    </data>


    Next, we pass the value into stored procedure:

    command.Parameters.Add("@Tags", SqlDbType.Xml).Value = NameValueToXml(post.Tags)

    And finally, we store the values into our table:

    CREATE PROCEDURE SavePost
    (
      ...
      @Tags XML
    )
    AS
    SET NOCOUNT ON

       
       ...

       INSERT INTO XX
          SELECT ItemData.row.value('key[1]', 'varchar(200)'),
                 ItemData.row.value('value[1]', 'varchar(200)')
             FROM @Tags.nodes('/data/meta') ItemData(row)

    SET NOCOUNT OFF



    The syntax isn't the most obvious. In my mind, it creates a node at ItemData.row for each child of /data/meta which can be access via the value method. Note that you can also access attributes by using the @name syntax.


  • QueryString separated by something other than & (ampersand)?

    Over in the microsoft.public..aspnet newsgroup today, Richard asked if it was possible to have ASP.NET use a semicolon as the querystring separator.  My first thought was "DUH! NO!!" but I decided to read the URI RFC and learnt that the separator can be any number of reserved comments.

    Wikipedia's entry on the QueryString then pointed me to this W3C recommendation that says:
    "We recommend that HTTP server implementors, and in particular, CGI implementors support the use of ";" in place of "&" to save authors the trouble of escaping "&" characters in this manner."

    So using reflector, I decided to dig into the HttpRequest object. I quickly ended up at an internal FillFromString function of the internal HttpValueCollection class. Unless I'm looking at the wrong function, the '&' is hardcoded into the function and it really looks like it shouldn't be. At the risk of having re-invented the wheel, and understanding that I haven't tested this code (I ran it twice in SnippetCompiler!):

    //to many examples leave out their using/imports!
    using System;
    using System.Web;
    using System.Collections.Specialized;

    public static NameValueCollection ParseQueryString()
    {
       return ParseQueryString('&');
    }
    public static NameValueCollection ParseQueryString(char delimiter)
    {
       if (HttpContext.Current == null)
       {
          throw new Exception("ParseQueryString must be called from a web context");
       }     
       return ParseQueryString(HttpContext.Current.Request.Url.Query, delimiter);
    }
    public static NameValueCollection ParseQueryString(string query, char delimiter)
    {     
       if (delimiter == '\0')
       {
          throw new ArgumentNullException("delimiter");
       }     
       NameValueCollection queryString = new NameValueCollection();
       if (query == null || query.Length == 0)
       {
          return queryString;
       }
       if (query.StartsWith("?"))
       {
          query = query.Substring(1, query.Length-1);
       }
      
       string[] pairs = query.Split(delimiter);
       foreach(string pair in pairs)
       {
          string[] keyValue = pair.Split('=');
          int length = keyValue.Length;        
          if (length == 1)
          {
             queryString.Add(keyValue[0], null);  
          }
          else if (length == 2)
          {
             queryString.Add(keyValue[0], keyValue[1]);  
          }
          else if (length != 0)
          {
             throw new Exception(string.Format("An error occurred parsing {0} into a key=>value pair", pair));
          }
       }
       return queryString;
    }

  • ClientScript functions might not work without a server form

    It didn't take me hours to figure it out, but I certainly wasted more time on this that I would have liked. ClientScript.RegisterClientScriptInclude  (and I assume the other RegisterXXX classes) won't work unless you have a <form runat="server"...></form> on your page.

    What really bothers me is that no exception is raised - which is what I'd expect since a number of other framework element will throw an exception in for this exact same situation. Inconsistencies are probably to be expect with a huge framework, but they still suck!
  • Hosting - You get what you pay for (or, I like Rackspace)

    The amount of choices available when picking a hosting company can be quite overwhelming - especially when you aren't sure what kind of traffic to expect. Most of our clients are new to the adver-gaming world and have no idea what kind of traffic they are going to get.  To help, we've set up a pricing tier that covers a wide range of scenario - but in the end, it's clear that you get what you pay for.
     
    One of our projects was quoted unfortunately low for hosting charges. We decided to go with a shared/dedicated (only a few clients per box) solution. It was clear within hours of launching that we needed to move (we ended up moving the large content files (swf's and videos) off to a separate server). I won't name the hosting company because that just isn't the kind of hosting they are meant to handle - it really wasn't their fault. That client did over 3terabytes of traffic last month - that's the kind of thing you need solid hosting for (I'm expecting another site to do over 15tb next month).

    From now on, we've made sure that our sales guy understand our tiered hosting solution. At the top of of tier is content distribution providers such as LimeLight and Akamai. These guys charge big time to host your files all over the world on super-charged networks. I believe Akamai hosts a number of high profile google and microsoft assets. One of the things I hate about their offerings is the time it takes to get anything setup and everything's an extra. It really isn't an on-demand service (ie, our site was featured on digg and slashdot and we suddenly need huge capacity - that'll be 3-7 days, unless you want to pay up the nose).

    Due to the multimedia nature of our work, most of our hosting falls at the next tier - high end dedicated servers. We use a number of different providers, but of late Rackspace has proved itself far superior. I won't say rackspace is the best, because that would imply that I've compared it to everyone else, but I'm ready to bet that they are hard to beat. They pride themselves on their "fanatical" support, which is really one of those amusing marketing names, but it's hard to deny how great the support team is. What I really like about rackspace is the price. (for the love of god though, stop popping up the "live person" chat thing when I go to your website!!)

    You'll end up paying around $450-$650/month for their base server - which is a pretty powerful Opteron 246 with 1gb of ram and 2x72SCSI Raid 1 hard drives. Windows 2003 or Linux (yes, the cost the same thing). You'll get anywhere between 300gb to 1000gb of outgoing traffic a month for that price. Their $3.5/gb surcharge is a little much, but within reason (you can prepay @ $2/gb).  I wish Rackspace would make their plans more public, they want you to talk to their sales guy so they can "customize" the price, but that's a pain for someone doing a quick comparison. Best of all, the network is fast and streaming media works great. One of the only problems is that they don't offer a shared SQL Server space - which a lot of other companies do. To be honest, that isn't as much of an issue now that SQL Server Express is free and production capable - I just wish they'd offer that as a standard install.

    I know a lot of people in the .NET community really like ORCSWeb hosting. They run a number of community sites, including everything on the asp.net domain I believe (forums, weblogs). While I'm sure they are a great hoster, I can't get over how overpriced they seem. For $499 you only get 256mb of RAM and 30gb of bandwidth - I couldn't find what the surcharge was - but at $3/gb that would have cost our 3tb client over $8k more! It's insanely expensive and with only a bit of RAM and bandwidth, you can forget about hosting a couple of your clients on the same box to lower their overall hosting cost. You do get 150MB shared SQL Server space which might be a big plus to you (I still favor running SQL Server 2005 Express).

    As a lower-priced alternative to Rackspace, we use NetNation quite a bit (we have a number of linux boxes).

    Whatever hosting company you pick, you want to make sure you either know what kind of load you are going to get or make sure you have a good upgrade path (ideally, you should have both!).  For us that means being able to add more RAM, a 2nd CPU and prepaying for bandwidth.  You also need to realize you'll get what you pay for. More money doesn't just pay for better support, but should also get you a better connection to the internet.

    Finally, be careful about taking people's advice on hosting (including mine). Most companies have aggressive referral programs or partner benefits. It’s easy to say a host is great when you aren’t paying anything for it. (in case you are wondering, I don’t get anything (nor does my company) for hosting at Rackspace, though we are looking at the Partner program)

More Posts

Our Sponsors