Alex Papadimoulis' .NET Blog

Alex's musings about .NET and other Microsoft technologies

  • ScriptOnly - The Opposite of a NOSCRIPT

    Despite all of the advances in client-side scripting, the wonderful JavaScript libraries like Prototype and Scriptaculous, and the ease of writing AJAXy code in ASP.NET, there’s still one aspect of modern web development that can be a complete pain in the butt: accessibility for users without JavaScript. If you’re lucky – perhaps you’re developing an Intranet application, or the like – a simple <noscript>Error: This application requires JavaScript enabled</noscript> is all it takes. But other times, you need to take that extra step and make it work for those with and without JavaScript enabled.

    There’s a lot of ways that this can be accomplished, but one of the more popular ways is with the SCRIPT/NOSCRIPT combo...

    <script type="text/javascript">
        document.write('Only users with JavaScript will see me.');    
    </script>
    <noscript>
        Only users without JavaScript will see me.
    </noscript>
    

    While this works fine in a lot of scenarios, it can get especially tricky when you want to put server-side controls on the SCRIPT side of things. A lot of developers resort to something like this...

    <div id="javaScriptOnly" style="display:none">
        Only users with JavaScript will see me.
        <asp:LinkButton runat="server" ... />
    </div>
    <div id="noJavaScript" style="display:block">
        Only users without JavaScript will see me.
        <asp:Button runat="server" ... />
    </div>
    <script type="text/javascript">
        document.getElementById('javaScriptOnly').style.display = 'block';
        document.getElementById('noJavaScript').style.display = 'none';
    </script>
    

    ... and of course, things quickly get much uglier once you do this in the real world.

    One solution that I use is a simple, custom-control called ScriptOnly. It works just like this...

    <inedo:ScriptOnly runat="server">
        Only users with JavaScript will see me.
        <asp:LinkButton runat="server" onClick="doSomething" ... />
    </inedo:ScriptOnly>
    <noscript>
        Only users without JavaScript will see me.
        <asp:Button runat="server" onClick="doSomething" ... />
    </noscript>

    JavaScript users see a LinkButton, while non-JavaScript users see a plain old submit button. What’s neat about this technique is that you can put any type of content - server-controls, html, script tags, etc - and that content will only be displayed for JavaScript users. In essense, it works like a reverse NOSCRIPT tag.

    Behind the scenes, ScriptOnly is a very simple control...

    [ParseChildren(false)]
    public class ScriptOnly : Control
    {
        protected override void Render(HtmlTextWriter writer)
        {
            //Render contents to a StringWriter
            StringWriter renderedContents = new StringWriter();
            base.Render(new HtmlTextWriter(renderedContents));
    
            //write out the contents, line by line
            writer.WriteLine("<script type=\"text/javascript\">");
            StringReader sr = new StringReader(renderedContents.ToString());
            while (sr.Peek() >= 0)
            {
                // This could be optimized to write on one line; but
                // I've found this makes it easier to debug when
                // looking at a page's source
                writer.WriteLine(
                    "document.writeln('{0}');",
                    jsEscapeText(sr.ReadLine()).Trim());
            }
            writer.WriteLine("</script>");
        }
    
        private string jsEscapeText(string value)
        {
            if (string.IsNullOrEmpty(value)) return value;
    
            // This, too, could be optimzied to replace character
            // by character; but this gives you an idea of
            // what to escape out
            return value
                /*  \ --> \\ */
                .Replace("\\", "\\\\")
                /*  ' --> \' */
                .Replace("'", "\\'")
                /*  " --> \" */
                .Replace("\"", "\\\"")
                /*  (newline) --> \n */
                .Replace("\n", "\\n")
                /*  (creturn) --> \r */
                .Replace("\r", "\\r")
                /* </script> string */
                .Replace("</script>", "</scri'+'pt>");
        }
    }

    When "pre-reistered" in your web.config, it works just as well as the NOSCRIPT tag.

    Read more...

  • A Workaround For VirtualPath Weirdness With Custom VirtualPathProviders

    [[ Meta-blogging: as you may have noticed from the name/description change (and of course, this article) I’ve decided to shift the focus of this blog back to the “front lines” of Microsoft/.NET development technologies. All other rants and ramblings will go to Alex's Soapbox over at WTF ]]

    If you've ever come across this error...

    The VirtualPathProvider returned a VirtualFile object with VirtualPath set to '/global/initrode/embeddedControl.ascx' instead of the expected '//global/initrode/embeddedControl.ascx'

    ... then chances are you're implementing VirtualPathProvider in order to serve up embedded Page/Control resources or something fun like that. Let's just hope your not serving pages from a ZIP file. And if you have no idea what a VirtualPathProvider is, then do check out that MSDN article I linked to get an idea.

    The reason behind this error is identified in Microsoft Bug #307978: ASP.NET is erroneously replacing page compilation errors with the bad virtual path error. While ensuring that your virtual-pathed page will compile is a sure-fire way to fix the error, finding the compilation errors can be a bit of pain.

    Fortunately, there's a pretty easy workaround that will let you see some of the compilation errors. First, make sure that your custom VirtualPathProvider has a static method that can determine if given virtualPath is on disk or is virtualized (e.g. an embedded resource). Next, create an IHandlerFactory that inherits PageHandlerFactory, overrides the GetHandler method, and has a try/catch around a call to base.GetHandler(). In the event that an exception occurs, simply determine if the request's virtual path is "virtual" (through that static method) and, if so, rethrow the exception with only the error message. In other words,

    public class MyPageHandlerFactory : PageHandlerFactory
        {
            public override IHttpHandler GetHandler(HttpContext context, string requestType, string virtualPath, string path)
            {
                try
                {
                    return base.GetHandler(context, requestType, virtualPath, pathTranslated);
                }
                catch (Exception ex)
                {
                    //TODO: ASP.NET 2.0 Bug Workaround
                    // There is an error generating a stack trace for VirtualPathed files, so 
                    // we have to give up our stack trace if it's a resource file
    
                    if (EmbeddedResourceVirtualPathProvider.IsResourcePath(virtualPath))
                        throw new Exception(ex.Message);
                    else
                        throw ex;
                }
            }
        }

    Since we're only wrapping the GetHandler method (as opposed to the IHttpHandler's ProcessRequest method), the only errors you'll see wrapped like this are pre-ProcessRequest errors (e.g. compilation errors). And while this won't give you the full stack trace, at least you'll see something like this instead:

    http://server//global/initrode/embeddedControl.ascx(5): error CS1002: ; expected

    Read more...

  • Coghead: Web Applications for Dummies by Dummies

    There's been a buzz going around about a new web startup called Coghead.  Heralded by Business 2.0 as one of the "innovations that could reorder entire industries," Coghead is lead by former Red Hat executive Paul McNamara and Extricity founder Greg Olsen. El Dorado Ventures, a Venture Capitalist firm, recently invested $2.3M in the company. According to McNamara,

    Read more...

  • Stop Using Enterprise Manager! (Use DDL Instead)

    Of all the tools that ship with SQL Server, Enterprise Manager is by far the most feature-packed and widely-used. Nearly every SQL Server developer is familiar with Enterprise Manager. They are comfortable using the wizards and GUI to do everything from creating a new table to adding a schedule job. But as a project grows to encompass more developers and environments, Enterprise Manager becomes a detriment to the development process.

    Most applications exist in at least two different environments: a development environment and a production environment. Promoting changes to code from a lower level (development) to a higher level (production) is trivial. You just copy the executable code to the desired environment.

    • Click on the desired database.
    • Click on Action, New, then Table.
    • Add a column named "Shipper_Id" with a Data Type "char", give it a length of 5, and uncheck the "Allow Nulls" box.
    • In the toolbar, click on the "Set Primary Key" icon. Then you skip 22 steps.
    • In the toolbar, click on the "Manage Relationships…" button.
    • Click on the New button, and then select "Shippers" as the Foreign key table.
    • Select "Shipper_Id" on the left column and "Shipper_Id" on the right column. Skip the remaining steps.

    Not only is this process tedious, but you're prone to making errors and omissions when using it. Such errors and omissions leave the higher-level and lower-level databases out of sync.

    Fortunately, you can use an easier method to maintain changes between databases: Data Definition Language (DDL). The change described in the previous example can be developed in a lower-level environment and migrated to a higher-level environment with this simple script:

    CREATE TABLE Shippers (
      Shipper_Id CHAR(5) NOT NULL 
        CONSTRAINT PK_Shippers PRIMARY KEY,
      Shipper_Name VARCHAR(75) NOT NULL,
      Active_Indicator CHAR(1) NOT NULL 
        CONSTRAINT CK_Shippers_Indicator 
          CHECK (Active_Indicator IN ('Y','N'))
    )
    ALTER TABLE Orders
      ADD Shipper_Id CHAR(5) NULL,
      ADD CONSTRAINT FK_Orders_Shippers
        FOREIGN KEY (Shipper_Id)
        REFERENCES Shippers(Shipper_Id)
    

    You can manage all the DDL scripts with a variety of different techniques and technologies, ranging from network drives to source control. Once a system is put in place to manage DDL scripts, you can use an automated deployment process to migrate your changes. This process is as simple as clicking the "Deploy Changes" button.

    The perceived difficulty of switching changes from Enterprise Manager to DDL scripts is one of the biggest hurdles for developers. The Books Online don't help change this perception. A quick look at the syntax for the CREATE TABLE statement is enough to discourage most developers from using DDL.

    Enterprise Manager helps you with this transition. Before making database changes, Enterprise Manager generates its own DDL script to run against the database. With the "Save Change Script" button, you can copy the generated DDL script to disk, instead of running it against the database.

    But as with any code generator, your resulting T-SQL script is far from ideal. For example, having Enterprise Manager generate the DDL required for the change described in the example involves six different ill-formatted statements. What do you do now? You can add a bit of refactoring to the generated script, and the result looks almost identical to the example script I showed earlier. After a few more rounds of generating and refactoring, you'll want to transition straight to DDL, and never look back at tiresome database development within Enterprise Manager.

    Read more...

  • Holy Crap: I'm an Official MVP for MS Paint

    There are few emails that one will receive in his lifetime that will render him completely speechless. This past weekend, I received one such email. Its subject read Congratulations on your MVP Award!

    I struggle with the words to describe how elated I am to be chosen for this award. Sure, I’ve worked my butt off in microsoft.public.accessories.paint, helping both newbies and vets solve their problems. But I never expected this. For me, it’s always been about my love of the Paint, and sharing my knowledge and expertise of Paint with the world.

    I don’t want to bore you with me patting my self on the back, so I’ll just use the rest of this space to share my top three tips and tricks. I’ve got plenty more, so if you ever need some help with Paint, don’t hesitate to ask this MVP!


    Why are some of my edges jagged?
    You’ve discovered one of the dark secrets of digital art: pixilation. Because everything in your Paint image is made of small square blocks, the only way to make a diagonal line or a curve is to arrange the pixels in “steps;” these very steps give the image that ugly, jagged appearance.

    Fortunately, we can help smooth out the jagged edges with a technique called anti-aliasing. The trick is to make the jagged edge an in-between color of the two bodies of colors. For our red circle and white background, all we need is pink, applied with the spray paint can tool.

    And like magic, the jagged edge is no more!

    How do I do shadows?
    Shadows in Paint are incredibly easy to do:
    1) Draw the shape you want to draw, but instead use black
    2) Draw the shape you want to draw, using the colors you really want to use, but draw it at an angle slightly away from the black shape

    Look ma, a shadow!

    How can I make realistic looking Hair?
    This is one of the more difficult things to accomplish in Paint. But it’s certainly doable. First, you need to figure out what hair style you want to use. Once you figure that out, it’s just a matter of using the right tool.

    Curl

    Believe it or not, this is a simple matter of using the wonderfully handy spray can tool. Just pick the hair color, and go crazy!!!

    Baldy

    This hairstyle is so ridiculously simple you’ll wonder why more cartoons characters aren’t bald. Simply apply the ellipse tool twice, above each ear, and you’ve got yourself a bald guy!

    Side Part

    When you want to make your character look neat and orderly, only the polygon tool will do. Here’s something funny: I like to part my own hair on the left, but draw it parted on the right. Funny, see, I told you!

    Bed Head

    Oh no, caught red handed without a comb! You can easily achieve this look with the use of the paint brush tool. Don’t go too crazy, it’s pretty easy to slip and go through an eye.


    Be sure to congratulate Jason Mauss as well. He was awarded this year’s MSN Messenger MVP.

    Read more...

  • Express Agent for SQL Server Express: Jobs, Jobs, Jobs, and Mail

    UPDATE: My appologies, but with the advent of relatively inexpensive commercial solutions avaiable, I've decided to suspend this project indefinitely. If I do need a solution for myself, I may take it up again. But until then, I would recommend getting a commercial version (http://www.valesoftware.com/products-express-agent.php is one source) or using the Windows Task Manager to run batch files.

    UPDATE 2: I no longer "officially" recommend Vale's agent; though I've used the product for well over a year, they were completely non responsive (via phone or email) to a showstopper bug in their product (stopped working after 24 hours when a job was set to run every 5 minutes). My workaround was to have a Windows Task stop then start VAle's SQL Agent service. Also, as a commenter noted, a free version (http://www.lazycoding.com/products.aspx) is out there - I have not used this, however.

    I was pretty excited to learn about SQL Server: Express Edition. It is a stripped-down of version of SQL Server that is free to get, free to use, and free to distribute. This is great news if you're in the business of building small- and mid-sized database applications but not in a position to fork over five grand for the full edition.

    A free, stripped-down version of SQL Server is nothing new; afterall, MSDE filled this niche for the previous version of SQL Server. One thing that sets SQL Server Express apart is its branding and accessiblity. Not only does Express "feel" like SQL Server, it's easy to install, use, and administer. MSDE did not have these qualities, which kept it out of the reach of many would-be database developers.

    The limitations imposed by SQL Server Express do not hinder most small- and mid-sized applications. A single processor and a gigabyte of RAM is enough to run most of these applications and it certainly takes a *lot* of data to fill a database up to four gigabytes. One thing that makes Express a deal-killer is the lack of SQL Agent, which runs scheduled jobs and automates backups. That's important in just about all-sizes of applications.

    I'm developing an application that will fill this functionality gap: Express Agent. I was hoping to have this complete before the launch of SQL Server Express, but other priorities prevented this from happening. Express Agent strives to replace and improve upon the SQL Agent that was left out.

    Like the SQL Agent, Express Agent runs as a service. However, Express Agent can also be "plugged in" to a hosted web-application as a HttpHandler. This allows Express Agent agent to run as background thread, running jobs and sending email as needed.

    The jobs are modeled in a similar fashion to the way SQL Server handles them. A job contains a number of tasks (SQL Scripts) that are run depending on whether the previous task was successful (no errors) or successful (errors). Jobs can also be scheduled on a one-time, idle, start-up, and recurring basis. The recurring schedule is handled much the same way SQL Server handles jobs as well.

    Express Agent also adds database-email capability to Express Edition. Though not as complex as SQL Server's implemntation, this should cover just about any emailing you'd need to do from within your stored procedures. The mail feature is used to send success/failure notifications after jobs have been run.

    It's difficult for me to show progress, since much of the work I've done is the "behind the scenes" stuff. I'm still working out the UI, HttpHandler, and some other issues, but so far it works great on it's own, so long as jobs are added via the stored procedures. No less, here's a few screen shots from the Jobs Manager UI ...

    If this app looks like it may be of interest to you, I'd appreciate your feedback. If you're interested in lending a hand with some of the remaining portions, I'd really appreciate that, too. I plan on offering this completed product for free, but most likely not open source.

    Read more...

  • Gettin' Down in Detroit: The 2005 Launch Party

    I saw that Jason Mauss wrote about his experience at the San Fransisco 2005 Launch Party, so I thought I'd share my experience at the Detroit venue. Because it wasn't the "real" Launch Party, we didn't have anything fancy like a speech from Steve Balmer, songs performed by AC/DC, or appearances by the guys from Orange County Choppers. But it was still a good time. Please bare with my lack of actual photographs, as I did not have the foresight to bring a camera.

    Read more...

  • MySQL 5.0: Still A "Toy" RDBMS

    "Ha," an email from a colleague started, "I think you can finally admit that MySQL is ready to compete with the big boys!" I rolled my eyes and let out a skeptical "uh huh." His email continued, "Check out Version 5. They now have views, stored procedures, and triggers."

    My colleague has been a MySQL fan since day one. He loves the fact that it's free and open source and could never quite understand why anyone would spend tens of thousands of dollars on something else. But then again, he has never really had an interest in understanding; data management just isn't his "thing." Thankfully, he readily admits this and stays far, far away from anything to do with databases, leaving all of that "stuff" to the experts. No less, he'll still cheers whenever there's a MySQL "victory." it is, after all, free and open source.

    Data professionals have traditionally relegated MySQL as a "toy" relational database management system (RDBMS). Don't get me wrong, it's perfectly suitable for blogs, message boards, and similar applications. But despite what its proponents claim, it has always been a non-choice for data management in an information-system. This is not a criticism of the "free open source" aspect of the product, but of its creators.

    The MySQL developers claim to have built a reliable RDBMS yet seem to lack a thorough understanding of RDBMS fundamentals, namely data integrity. Furthermore, they will often surrogate their ignorance with arrogance. Consider, for example, their documentation on invalid data [emphasis added]:

    MySQL allows you to store certain incorrect date values into DATE and DATETIME columns (such as '2000-02-31' or '2000-02-00'). The idea is that it's not the job of the SQL server [sic] to validate dates.

    Read more...

  • "When Should I Use SQL-Server CLR User Definied Types (UDT)?"

    No one has asked me that question just yet, but with the release of SQL Server 2005 just around the corner, I'm sure a handful of people will. Unlike regular User Defined Types, CLR UDTs are a new feature of SQL Server 2005 that allows one to create a .NET class and use it as a column datatype. As long as a few requirements are followed, one can create any class with any number of properties and methods and use that class as a CLR UDT.

    Generally, when a new feature is introduced with a product, it can be a bit of a challenge to know when and how to use that feature. Fortunately, with SQL Server's CLR UDTs, knowing when to use them is pretty clear:

    Never.

    Let me repeat that. Never. You should never use SQL Server CLR User Defined Types. I'm pretty sure that this answer will just lead to more questions, so allow me to answer a few follow-up questions I'd anticipate.

    Why Not?
    CLR UDTs violate a fundamental principle of relational databases: a relationship's underlying domains must contain only atomic values. In other words, the columns on a table can contain only scalar values. No arrays. No sub-tables. And, most certainly, no classes or structures. Remember all the different levels of normalization? This is the first normal form, you know, the "duh" one.

    This is a big thing. One can't just go and fudge a tried-and-true, mathematically-validated, theoretically-sound concept and "add and change stuff to it 'cause it'll be cool." Think of how much your car would love driving on a road made of stained glass blocks three years after it was built by an engineer who thought it'd look better.

    Deviating so grossly from the relational model will bring as much joy as a dilapidated glass road. Take Oracle's foray into relational abuse: nested tables. I don't believe that there has ever been a single, successful implementation of that abomination. Sure, it may work out of the box, but after a year or two of use and maintenance, it decays into a tangled mess of redundancy and "synch" procedures -- both completely unnecessary with a normalized relational model.

    And if that doesn't convince you, just think of having to change that CLR UDT. How easy do you think it would be to add a property to the class representing a few million rows of binary-serialized objects? And, trust me, it won't be nearly as easy as you think.

    But wouldn't I want to share my .NET code so I don't have to duplicate logic?
    This is always a novel goal, but an impossible one. A good system (remember, good means maintainable by other people) has no choice but to duplicate, triplicate, or even-more-licate business logic. Validation is the best example of this. If "Account Number" is a seven-digit required field, it should be declared as CHAR(7) NOT NULL in the database and have some client-side code to validate it was entered as seven digits. If the system allows data entry in other places, by other means, that means more duplication of the "Account Number" logic.

    By trying to share business logic between all of the tiers of the application, you end up with a tangled mess of a system. I have illustrated this in the diagram below.

    Read more...

  • "What's the Point of [SQL Server] User-Defined Types?"

    I'm asked that question every now and then from other developers who've played around in SQL Server Enterprise Manager and noticed the "User Defined Data Types" tab under their database. UDT seem a bit strange and pointless because they do not allow one to define (as one might expect) a data structure with more than one related data element. A UDT consists simply of a name and a base type (INT, VARCHAR(6), etc).

    Read more...