September 2008 - Posts

Using JSON with MonoRail

As a sort of follow-up to my AJAX and MonoRail post, I've put together a small project showing how you can easily use JSON with MonoRail.  I planned on posting a blog entry and the code tonight but instead, I decided to spend some time working on the AJAX and JSON section of the MonoRail Users Guide.  It's my way of saying "Thanks!" to the Castle team for putting together (and supporting!) such a wonderful project.

Documention always seems to lag behind features and MonoRail is no exception.  While MonoRail is continually being updated and improved, the documentation is getting old and some of it hasn't even been updated since RC2 (RC3 was released in September 2007).  I've submitted a few documentation patches over the past few months and tonight I submitted another one to cover the AJAX and JSON sections.  It's not a complete patch, but it's a start.  Hopefully, the patch will be applied within the next week or so and the result will be there for everyone to enjoy.

Do you use an open source project?  Have you ever dropped a note of "Thanks" to the developers or helped out with some documentation of bug fixes?

Technorati tags: ,
Posted by PSteele | 1 comment(s)
Filed under: ,

Day of .NET Fall 2008 (Ann Arbor)

It's time for Day of .NET Ann Arbor!  As John Hopkins has pointed out, the date is October 18th, 2008.  Once again will be using Washtenaw Community College as our venue.  Registration for this FREE event is now open and since we always "sell out", I would recommend an early registration.

Day of .Net October 18, 2008 - Be there!

 

Technorati tags: ,
Posted by PSteele | 1 comment(s)
Filed under:

The End of SpamBayes (for me)

I've been happily using SpamBayes for years (almost 5 to be exact) as my SPAM filter.  Its worked remarkably well for a free Microsoft Outlook Add-in.  However, something happened today and things haven't been right since.

Lots of SPAM

From looking in my SPAM folder (which I don't clean out as often as I should -- more on that later), I receive between 650 and 750 pieces of SPAM every day.  However, most of that -- I'd say at least 95% -- I never see.  SpamBayes scores it as SPAM, moves it to the SPAM folder and marks it as read.  I probably have to review about 10 - 20 "SPAM Suspects" a day and about 98% of those really were SPAM.  It's a pretty good solution.

Lots of Mail

I have to admit that I rarely delete email.  I don't know why that is.  I like having an electronic paper trail of my sent mail and I've got a decent folder structure within my Inbox to categorize almost every piece of incoming mail (it's not perfect -- but it's decent).  I do perform manual archiving from time to time, but I don't do it nearly as often as I should.  That's why my SPAM folder had tens of thousands of emails that allowed me to compute my average of 650-750 SPAMs per day.

Beginning of the end

Sometime early this morning, Outlook did it's regularly scheduled download of email.  I was doing something else when it happened but I suddently got some error from SpamBayes.  I don't recall the exact wording, but it had something to do about not being able to access my SPAM folder.  I thought that was odd so I switched to Outlook and noticed a block of about 20 SPAM emails sitting in my Inbox.  You could tell from the sender and the subject that they were SPAM.  Plus, they where new but marked as Read (SpamBayes is set to do that).  I wasn't sure why these emails couldn't be moved to the SPAM folder and I didn't want to mess with it at the time so I just selected them all and hit <Delete>.

That's when I got the next error.  This time from Outlook.  Again, I don't recall the exact wording, but it was basically "Your PST file has reached it's limits.  You need to trim it down a bit.  You know Outlook can handle more than one PST, don't you?!"  I do use multiple PST files.  In fact, I even have separate PST's for each year that I archive old mail into, but hadn't done that in a while.

So I take a peek at the size of my PST file -- 1.8GB.  Yeah, gigabytes.  I'm almost embarassed to admit it.  I wasn't really too upset though.  Outlook has been running slowly the past few months and I knew the size of my PST file had to have something to do with it.

Cleanup Time

So I spent an hour or so cleaning out my inbox.  I found old stuff from 2004 that I quickly deleted.  Other stuff got moved to my archive PST's.  And I cleaned out all of the old SPAMs that were loading up my SPAM folder.  Lots and lots of cleanup.

I then told Outlook to compact the PST file.  After about 45 minutes, it was down to a much more reasonable 344 megs.  SUCCESS!  I thought I'd be flying now.  So I shutdown and restarted Outlook.

It's even slower?!

My visions of a newly revitalized Outlook were crushed as soon as Outlook started.  Or should I say, when it finally started.  It was crawling!  And I'm not talking just receiving email (which was easily 2 - 3 times slower than it used to be).  Switching folders took close to 30 seconds.  Yes, 30 seconds!  I timed it.  I would click on a different email folder and start counting: one-one thousand, two-one thousand, three-one thousand...  Usually around 10 - 15 seconds I would get a busy cursor.  At around 25 - 30 seconds, focus would suddenly shift away from Outlook (like a little gnome had walked on my keyboard and pressed ALT-TAB to switch to a different app).  When I switched back to Outlook, the folder I clicked on was now displayed.

Was it a problem with one particular folder?  Nope, I spent a few minutes trying various folders.  Any folder switching was a 25-30 second wait while Outlook was basically unresponsive.  I was really looking forward to a speedier Outlook after doing so much house cleaning.

My Feeble Attempts at Recovery

So I did a google search on "Outlook 2007 performance".  Whoa -- that's scary.  26 million hits?!  Ok, let's start browsing a few and see if anything stands out.  Of course, the obvious one -- reduce the size of your PST file.  I went from 1.8GB down to 344MB, so I think that one can be marked complete.  Defrag the hard drive.  Ok, I used the Diskeeper Lite that came with my laptop to defragment the hard drive.  And then I rebooted.  Outlook was still crawling.

Another tip was disabling all add-ins.  I tried that too (all except for SpamBayes since I couldn't really live without it and it's been working fine since 2003).  Still no luck.  I finally decided to disable SpamBayes just to see what would happen.

Bingo!  Outlook was running much quicker now.  I could happily click, click, click across different folders with no delay.  I don't know what chain of events caused SpamBayes to work so flawlessly for 5 years and then all of a sudden cause me problems like this.  I'm still not convinced its SpamBayes fault, but Outlook just isn't usable right now unless I disable SpamBayes.

Cloudmark Desktop

I've read a lot of good things about Cloudmark Desktop so I downloaded the trial and installed it.  So far so good.  Installation was quick and painless and it's already caught it's fair share of SPAM.  I've got 15 days to see if this is right for me and then it'll be only $40/year after that.  Not too bad when you consider how much time I would waste cleaning out SPAM manually.

Posted by PSteele | with no comments

Easy AJAX with MonoRail

AJAX gives your web applications a better user experience.  They'll get quicker feedback on their actions and it won't require a full page refresh.  In this post, I'm going to review the basics of doing AJAX in MonoRail.  A complete Visual Studio 2008 solution for this sample project is available from my Google Code page.

Scenario

For this demo, we'll have a dropdown listbox that allows the user to select a number.  After selecting the number, we'll update an area of the web page with dynamically generated content and this will all be done without doing a full page refresh.

Setup

Like many MVC frameworks, MonoRail allows you to define a common layout for your website (think Master Pages in webforms).  My layout for this sample is simple (I'm using the NVelocity view engine):

<html>
<head>
    <title>MonoRail Sample Application</title>
    <link rel="stylesheet" href="$siteroot/Content/css/base.css" />
    $AjaxHelper.InstallScripts()
    $!head
</head>
<body>
    $childContent
</body>
</html>

MonoRail has built-in AJAX support via the prototype framework. The "$AjaxHelper.InstallScripts()" emits the proper javascript tags to include the prototype scripts for use in our application.  You're free to choose a different AJAX library if you'd like, but the built-in "AjaxHelper" class makes using AJAX from MonoRail a breeze.  I also have a placeholder ($!head) so that individual pages can add additional code to the <head> tag (like page-specific Javascript).

Some people may ask, "If you install the prototype library in the layout, doesn't it get included in every page even if you don't need it?"  Yes, it does.  If you'd like to limit where your AJAX is available, move the $AjaxHelper.InstallScripts() into each individual page as needed.

The Controller

Let's create a simple MonoRail controller for our AJAX sample.  For right now, we'll define the "Index" action as the action that is the beginning of this app:

[Layout("default")]
public class AjaxSampleController : SmartDispatcherController
{
    public void Index()
    {
        PropertyBag["counts"] = Enumerable.Range(1, 10);
    }
}

Nothing fancy.  We place a range of numbers (1-10) in the PropertyBag and we'll use that to generate our combo box.

The View

We'll keep the view simple so we can focus on concepts.  Something like this will do:

image

We'll implement the above in our view:

<div>
Select the number of items to display: $Form.Select("count", $counts, "%{onchange='DoAjax()'}")
</div>
<br />

Now we've got a dropdown list and we've hooked up the onchange event to call "DoAjax()" -- we'll discuss this later.

Now comes the part of the page for our AJAX update.  We'll define a <div> tag to hold the part of our screen we want to dynamically update.  Further, we'll define a separate view just for rendering the AJAX portion of the div.  Heres' the entire view for the "Index" method:

<div>
Select the number of items to display: $Form.Select("count", $counts, "%{onchange='DoAjax()'}")
</div>
<br />
<div id="ajaxarea" style="border: solid 1px blue; background-color: #cccccc">
#parse("ajaxview.vm")
</div>

I placed a border around the <div> and changed the background color to help differentiate it from the rest of the page.  What do we put in "ajaxview.vm"?  Whatever we want to!  Here's ajaxview.vm.  It looks for the existence of a "selectedCount" variable and loops to create the <p> tags:

#if($selectedCount)
    #foreach($i in [1..$selectedCount])
        <p>item: $i</p>
    #end
#end

Client-side AJAX Code

Now we'll add the Javascript necessary to implement the DoAjax() function:

#capturefor(head)
<script language="javascript">
    function DoAjax()
    {
        var items = $('count').selectedIndex + 1;
        new Ajax.Request('$UrlHelper.For("%{action='GenerateCounts'}")',
            {
                method: 'get',
                parameters: {
                    count: items
                },
                onSuccess: updateAjaxArea,
                onFailure: showMessage
            });
    }
    
    function updateAjaxArea(transport)
    {
        $('ajaxarea').innerHTML = transport.responseText;
    }
    
    function showMessage(transport)
    {
        alert('An error occurred during the AJAX request.');
    }
</script>
#end

First off, we use MonoRails "capturefor" component.  This takes everything between the "#capturefor" and the "#end" and assigns it to the "head" variable.  As you remember, the "head" variable was defined in the layout and therefore, all of this javascript code get's put in the proper place in our HTML page (i.e. inside the <head> tag).

The first thing DoAjax does it determine the item selected in the dropdown list.  Next, it creates an Ajax.Request object that calls the "GenerateCounts" method of our controller.  The "count" parameter is set to the value selected in the dropdown.  Finally we set the "onSuccess" and "onFailure" callbacks.  As you might have guessed, one of these will be called depending on the success or failure of our AJAX call.

The update callback takes the responseText of our AJAX request and updates our <div> tag we set up earlier.  The prototype documentation contains a full description of all of the properties and methods exposed by the transport object in the callbacks.

Server-side AJAX Code

The controller code for our method is even simpler that our client-side code:

public void GenerateCounts(int count)
{
    PropertyBag["selectedCount"] = count;
    RenderView("ajaxview");
}

All we do is take the value passed in (the value selected in the dropdown list) and place it in the PropertyBag as "selectedCount" (NOTE: MonoRail's SmartDispatcherController takes care of pulling the "count" from the HTTP request and assigning it to the "count" parameter of the method).  Then we tell MonoRail that it should render the view "ajaxview".  Remember that this view simply loops and creates <P> tags based on the value of "selectedCount".

Success!

That's it!  That's all there is to it.  Run the sample code and select a value from the dropdown list.  You'll notice that the area under the dropdown is updated as you pick different values and there is no full page refresh.

The complete VS2008 solution for this project can be downloaded in ZIP form or you can access the SVN code.

Conclusion

This exmaple just showed the mechanics of making AJAX requests via MonoRail's built-in AJAX support.  You can see how something like this can easily be used to render an entire <TABLE> from a database query or do some other complex logic -- all without a full page refresh.  If you have any questions, please don't hesitate to contact me.

Technorati tags: , ,
Posted by PSteele | 3 comment(s)
Filed under: ,

Now Syndicating with FeedBurner

I've hopped on the FeedBurner wagon.  If you're subscribed to my blog, please update your subscriptions to:

http://feeds.feedburner.com/dotnetmvp

If you're not a subscriber, why not subscribe now?

 Subscribe in a reader

Posted by PSteele | with no comments

Why do I keep trying?

I see it's been almost 2 years since I lashed out on Data Binding in .NET.  Judging from the comments I received, it seems I'm not the only one who thinks this technology is not implemented very well.

Nevertheless, I had a situation today where I thought Data Binding might be able to help me.  Sure, I was very apprehensive, but I really thought data binding could help me out and reduce my work.  Plus, despite my loathing of Data Binding and my continued bad experiences with it, I keep thinking it's me and I just don't know how to use it.

The Situation

I'm writing an application and I need a UI to display a bunch of pressure ranges (min/max pairs) for the user to edit.  So to make things easier, I created a UserControl that contained a label (the name of the pressure range) along with two text boxes -- one for the min and one for the max.  The user control exposed two integer properties, LowValue and HighValue.  These were just wrappers to access the Text property of the two textboxes.

Data Binding

Now I want to data bind a couple of properties in a business object to these values:

rangeControl1.DataBindings.Add("LowValue", config, "PressureMin");
rangeControl1.DataBindings.Add("HighValue", config, "PressureMax");

Seems simple enough.

I start my app and the "config" business object defaults PressureMin to 20 and PressureMax to 200.  When I pull up the UI to edit the high and low value, they are both properly displayed as 20 and 200.  I change the low pressure to 10 and press TAB to go to the next field.  Everything is fine.  I change the high pressure to 330 and press TAB.  That's when I get bit by databinding!

The "330" changes back to 200 after I leave the field!

Huh?  Why would one property work (the low pressure) but not the high one?  I double-checked my data binding (it's just defined through strings so I thought I might have made a copy/paste error).  Nope, everything looks good.

The Problem

I messed with this and Google'd for over an hour thinking this should work fine.  I figure it had to be me doing something wrong.  Once I realized what was happening, maybe it is me.  Maybe I just don't grok how Microsoft implemented data binding.  In any case, here's what I think is happening:

1. I enter "10" for the low pressure and hit TAB.  I'm still inside the user control so nothing related to data binding happens.

2. I enter "330" for the high pressure and hit TAB.  Now we're leaving the user control so data binding takes over.  It starts looping though it's data bindings collection.

3. The first binding to "LowValue" causes the "10" to be sent to my business object via the "PressureMin" setter.

4. Data binding now detects that my business object has changed and updates all bound controls!  This means the value for "PressureMin" (the value 10 that was updated in step 3) is moved to the LowValue control (no problem there) and the value for "PressureMax" (which is still 200) is placed in the HighValue control.

At this point, I'm, well, you know -- up someone's creek without a paddle.  My edited high pressure value of "330" is now gone.  Sure, data binding continues looping throughthe bound properties and "200" is passed to my business object via the PressureMax setter.  But that's moot at this point.

Again, it's probably me.  Maybe Microsoft never intended for multiple properties to be bound to a single object (why?).  I played around with setting DataSourceUpdateMode on my data bindings to Never.  That seemed to work as my edited high value didn't revert back to 200 when I tabbed out of the field.  But as soon as I used the binding source's "WriteValue" method, it caused step 4 to fire again, thus wiping out my other edits.

I do have a workaround, although I don't like it.  I expose the individual TextBox controls for the high and low values.  I do the data binding on each individual textbox instead of the usercontrol as a whole.  I feel dirty exposing those text boxes only to support data binding, but it does save me some work.

I think this is the last time I'm going to try messing with data binding.

Posted by PSteele | 2 comment(s)
Filed under:
More Posts