Welcome

Please contact me (Phil Haack) at here with any errors, problems, and/or questions.

To learn more about the application, check out the Subtext Project Website.

Powered By:

Syndication

Blog Stats

Bloggers (posts, last update)

Latest Posts

Useful to have a anon method?

I'm writing some code to get a snippet of text back from a passed in string.  I initially came up with:

        protected string Snippet(string text, int length)
        {
            text = StripBbCode(StripHTML(text));
            int spaceIndex = length;

            if(text.Length > length)
            {
                spaceIndex = text.IndexOf(' ', length);

                bool spaceExists = spaceIndex != -1;
                if(!spaceExists)
                {
                    spaceIndex = text.Substring(0, length).LastIndexOf(' ');
                }
                spaceExists = spaceIndex != -1;
                if(!spaceExists)
                {
                    spaceIndex = length;
                }
            }
            return text.Substring(0, spaceIndex);
        }

Then I got to thinking, would this be a good place to plop in an anonymous method?

        private delegate bool SpaceExists(int spaceIndex);

        protected string Snippet(string text, int length)
        {
            text = StripBbCode(StripHTML(text));
            int spaceIndex = length;

            SpaceExists spaceExists = index => index != -1;

            if(text.Length > length)
            {
                spaceIndex = text.IndexOf(' ', length);

                if(!spaceExists(spaceIndex))
                {
                    spaceIndex = text.Substring(0, length).LastIndexOf(' ');
                }
                if(!spaceExists(spaceIndex))
                {
                    spaceIndex = length;
                }
            }
            return text.Substring(0, spaceIndex);
        }

Which is better?  While the anon method is cooler, I think the prior is more readable.  I just wonder if there are other benefits.  Or is there a better way to do this?  Neither feels very good because I'm duplicating the same is = to -1 twice.

 

Update:  All tests passing and what I ended up with:

    [TestFixture]
    public class For_Snippet : BaseViewModel
    {
        [Test]
        public void When_Length_Less_Than_Space()
        {
            const string text = "[b]1<b>2</b>34[/b] 6789";

            string expected = "1234";
            string actual = Snippet(text, 4);

            Assert.That(actual, Is.EqualTo(expected));
        }

        [Test]
        public void When_Length_Equal_To_Space()
        {
            const string text = "[b]1<b>2</b>34[/b] 6789";

            string expected = "1234";
            string actual = Snippet(text, 5);

            Assert.That(actual, Is.EqualTo(expected));
        }

        [Test]
        public void When_Length_More_Than_Space()
        {
            const string text = "[b]1<b>2</b>34[/b] 6789";

            string expected = "1234";
            string actual = Snippet(text, 6);

            Assert.That(actual, Is.EqualTo(expected));
        }

        [Test]
        public void When_No_Space()
        {
            const string text = "[b]1<b>2</b>34[/b]";

            string expected = "1234";
            string actual = Snippet(text, 4);

            Assert.That(actual, Is.EqualTo(expected));
        }

        [Test]
        public void When_Long_With_No_Space()
        {
            const string text = "[b]1<b>2</b>34[/b]56789";

            string expected = "1234";
            string actual = Snippet(text, 4);

            Assert.That(actual, Is.EqualTo(expected));
        }

        [Test]
        public void When_Length_Out_Of_Bounds()
        {
            const string text = "[b]1<b>2</b>34[/b]";

            string expected = "1234";
            string actual = Snippet(text, 24);

            Assert.That(actual, Is.EqualTo(expected));
        }
    }

 

On:

        private delegate bool SpaceExists(int spaceIndex);
        protected string Snippet(string text, int length)
        {
            text = StripBbCode(StripHTML(text));

            SpaceExists spaceExists = index => index != -1;

            if(text.Length <= length)
            {
                return text;
            }
            int spaceIndex = text.IndexOf(' ', length);

            if(!spaceExists(spaceIndex))
            {
                spaceIndex = text.Substring(0, length).LastIndexOf(' ');
            }
            if(!spaceExists(spaceIndex))
            {
                spaceIndex = length;
            }
            return text.Substring(0, spaceIndex);
        }

posted @ 3/12/2009 6:44 PM by WTF

jQuery: Is a checkbox checked?

A few different ways, but the one that I find the most readable is:

$('#checkbox-id').is(':checked');

posted @ 3/4/2009 9:52 PM by WTF

Having your computer wake you up

I've often thought that instead of the alarm clock, it would be nice to have my computer wake up for me in the morning and then wake me up by playing some of my favorite tunes.  What a headache it would be to program though, and who's got the time.  Seems Dennis Babkin did with WakeupOnStandBy.  The utility essentially wakes your computer at the specified time, and then does whatever command you want it to do.  To become an alarm clock you simply specify a playlist that you'd like to play upon wake up time.  Bam, alarm clock.  And shoot, that took 5 minutes.

image

posted @ 2/9/2009 6:43 PM by WTF

Code Quality, not a matter of if, but when

I just read an article by Uncle Bob.  The blog post essentially states a direct counter to the Stack Overflow #38 podcast from Joel and Jeff plopped out.  Uncle Bob obviously knows a ton more about software than I do and with my coder hat on I totally agree with him. 

Wierdly enough, I don't think Joel and Jeff are wrong either though.  I think in today's day an age if you are putting a new product out there quality of the code doesn't matter <b>until</b> you actually have an audience using your code.  Once you have that, then I think it's the refactor time to go back in make sure your code coverage is high and the app is solid.

Until then what's the point?  Think about it, if you have a truly awesome product, the code smells great, 95% coverage everything's great on that end, but no one uses it.  Did you suceed? 

We're talking about 2 guys that coded something up in a matter of weeks, sure the code my stink a bit, but is there site stackoverflow.com successful?  Seems to be so far.  At this time I would think that they would now have a person implementing features, while 2 or so other people started refactoring and writing the tests to back the software up.

Why test until you need to?

Now...if they were developing the product for someone else, then it's a different matter.  In this case you have a super small team, with a tightly focused project, small code base, I just don't see the problem of not having 90% coverage.

posted @ 2/2/2009 9:06 AM by WTF

Getting back a strong type from app settings

What do you think of this?

 

protected TYPE GetApplicationSettings<TYPE>(string key) where TYPE : IConvertible

{

   string keyValue = System.Configuration.ConfigurationManager.AppSettings.Get(key);

   return (TYPE)Convert.ChangeType(keyValue, typeof(TYPE));

}

posted @ 1/30/2009 3:00 PM by WTF

Lego Mindstorms Competition

What a good time.  After attending a few sessions at the last So Cal Code Camp, the last session, a Lego Mindstorms Competition was up to bat.  Thomas, our presenter had 4 little square robots that 4 groups were going to try to use to push each other out of a circular ring.  There were a bunch of different sensors that we could use, 30 minutes, and the brain power of the group.  My son also was able to come in to "play with Lego's" and the wife and daughter joined in a little later. 

Take note that only one person in the entire classroom of people had ever touched a Lego Mindstorm kit, and they had only done one program.  The rest of us were pretty clueless and 30 minutes didn't seem like much time.  Strangely enough my group only had me and another dude, the other teams were comprised of 5-6 people, but that was pretty cool as there was less discussion about the approach we were going to take.  Surprisingly in the first few minutes we were able to get our robot moving and rotating.  I was "driving" by inserting commands as fast as possible into the machine, compiling, uploading it onto the robot, and then seeing what it'd do.  It was a ton of fun.

Getting to the competition with only seconds to spare as I inserted one last angled turn to try and bisect the largest portion of the circle had us pretty pumped.  With the code camp raffle underway we decided to do that and I missed the first round.  We won anyway, not sure what happened.  The next round was pretty interesting.  Our robot hit the front corner of the other robot's head and both robots went into a spin of death.  Our robot kept inching the other out of the circle, but it was taking awhile.  Thomas almost called the match a draw but we said, "Just a few more rotations" as we analyzed the competitors algorithm and noticed that the back sensor was about to hit the line and was going to go the other direction.  Sure enough on the next revolution it tagged it, backed right into our sweet spot and our robot shoved the sucker right out of the ring, and then actually reversed after it was finished to "find" more adversaries.  How freaking cool it was to have everyone yelling and having a good time (yes as my wife noted, we are nerds).  With that win we were in the finals.

I can almost say it was like having your mind on the mat for you actually fighting the other mind in a physical space.  You could totally tell as the competitor complained that "that's not my algorithm!" as we tried a test run before the final bout.  You see, the other team in the finals had the dude that had experience programming the robots.  I looked over at his program and it was pretty complex.  There was a lot of logic.  I thought we were screwed.  Weirdly enough though, on our first try after all the kinks were worked out and they were satisfied, their robot simply left the circle!  Win!

That was chalked up to a fluke though (I think it had to do with the logic loop latency screwed with their program) so we had another go.  Our robot again found it right away and bashed it out of the ring.  My son looked at me and was like "We won Dadda!"   High fives for the team!  The nerd ego was flowing though so the other team conceded just like a nerd would, "It was a hardware problem" and let it go.  We then were presented with our Lego #1 and that was it.

After the event I came away with a few revelations:

1) We had a non-programmer on our team, my wife.  She pointed out that "Well if you hit the robot, you want to keep pushing not back out".  Duh!  We had it just going to the line and then backing away.  She didn't know the first thing about actually programming the device, but her "business analyst" sense was right on the money.

2) After seeing a question on da stack I can safely say that my kids will be ready to program when they want to, if ever.  Me taking my son to the event wasn't to have him learn about programming, it was to hang out with Dad and play with some Lego's.  For some reason I really like to insert him into mature situations and see what he does.  He's a very good 5 year old, and I'll never forget his face after we pushed the final robot out of the ring.  If he picks up on programming, that's cool, but so are a lot of other things in life.

3) The smaller group (only 5 people - 2 kids, 1 wife and 1 programmer) really allowed us to keep a good focus on the task at hand.  We threw some ideas out there, saw what stuck and then kept refactoring till we had something solid.  Our design was simple in principle, so I was able to adjust at the very last minute to great affect.  Everyone on the team had bought into our ideas, and we were able to act on them quickly and precisely.  I'm not sure if on a larger team any of my own ideas would have been heard let alone the discussion of "why" to get 4 other people's buy ins to do it.

posted @ 1/26/2009 9:52 PM by WTF

SoCal Code Camp this weekend

Hit it up if you can.  I went to the last one and it was pretty fun.  Of course I was all by myself so it felt like the first day of school or something.  They did have a geek dinner last night but it was about an hour away from me so was a no go.  One day I will go to those.  One area I saw that was missing was ASP.NET MVC in general.  Maybe I should have tried to give a talk.  Man would that be a change from my usual shy self, but would be a fun experience.  Probably next time.  The one that has me the most interested, funny enough is the Lego Mindstorms competition.  Always interested in combining real world objects with code.  Would be a gentle entry I'm sure.

posted @ 1/24/2009 8:32 AM by WTF

Using JQuery with ASP.NET MVC to post data

I've seen some examples of how to do this, but they are older than the beta release and none seems to handle the entire process of posting to the server, and then displaying a result from that data on the screen using JQuery.  JQuery makes this process really simple (is anything really hard with JQuery?) but it took a bit of figuring so I thought I'd share.  All of the methods were also different so I have my own take.  Just remember that there are lots of ways to do things with JQuery, so modify to your needs.  For my example I am again using Spark, as I think it kicks serious butt.

What I have is essentially a thread or discussion listing.  It looks like:

<table cellpadding="7" border="1" width="100%">
  <thead>
    <tr>
      <td colspan="2">    
        <h1>${thread.Topic}</h1>
        Replies: ${thread.NumberOfPosts - 1} |  Views: ${thread.Views} | Score: ${thread.Score}
      </td>
      <td align="right">Email me when this is replied to</td>
    </tr>
  </thead>
  <tbody id="posts">
  <for each="var message in messages">
    <tr>
      <td>${message.PostedBy.NickName}</td>
      <td>Posted: ${message.Posted}</td>
      <td align="right">Quote Message</td>
    </tr>
    <tr>
      <td valign="top" nowrap="nowrap">
        Joined: ${message.PostedBy.Joined.ToShortDateString()}<br />
        Standing: ${message.PostedBy.Standing}<br />
        Location: ${message.PostedBy.Location}
      </td>
      <td colspan="2">
        ${message.Comment}
      </td>
    </tr>
    <tr>
      <td><a href="javascript:scroll(0,0)">Back to top</a></td>
      <td>PM | Email</td>
      <td align="right"></td>
    </tr>
  </for>
  </tbody>...

I would like to allow the user to add another message to this thread.  So I give them something like:

<form action="~/Message/New" method="post" id="newMessage">
<input type="hidden" id="threadId" name="threadId" value="${thread.Id}" />
Post Comment<br />
<textarea id="comment" name="comment" style="width:100%;" rows="4"></textarea>
<input type="button" value="Post Message" id="postMessage" />

And over in my MessageController I'd have something like:


    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult New(FormCollection form)
    {
      Message message = new Message();
      message.Thread = _repository.Get<Thread>(int.Parse(form.Get("threadId")));
      message.Posted = DateTime.Now;
      message.PostedBy = _userService.FindUserBy(User.Identity.Name);
      message.Comment = form.Get("comment");
      message.Score = 0;
      _repository.Save(message);
      ViewData["Message"] = message;
      return View("New");
    }

Here, I tried to use the TryUpdateModel but the threadId wasn't playing nice with Message's Thread property.  Instead I had to load it up.  If you know a work around I'd love to know about it.  Now, if you don't need anything back, replace ActionResult with void and don't return anything. In my case though I want to show the posted message on the page, so I shove the message into my ViewData and render the New.spark view.  That looks like a slice of the above thread html.  In fact I copy and pasted it like this:

<viewdata message="Message" />
<content name="page">
<tr>
  <td>${message.PostedBy.NickName}</td>
  <td>Posted: ${message.Posted}</td>
  <td align="right">Quote Message</td>
</tr>
<tr>
  <td valign="top" nowrap="nowrap">
    Joined: ${message.PostedBy.Joined.ToShortDateString()}<br />
    Standing: ${message.PostedBy.Standing}<br />
    Location: ${message.PostedBy.Location}
  </td>
  <td colspan="2">
    ${message.Comment}
  </td>
</tr>
<tr>
  <td><a href="javascript:scroll(0,0)">Back to top</a></td>
  <td>PM | Email</td>
  <td align="right"></td>
</tr>
</content>

This brings me to a hiccup that I encountered.  You might need to modify your master page (Application.spark).  A "use" tag around the entire master page will allow you to only output what we need, and not the entire page.  Normally we'd want to insert content within the master page, but for this we just want a snippet of html.  To further illustrate what I'm talking about, here's a sample from Application.spark:

 

<use content="page">
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

...

</html>
</use>

 

This makes sure that I only output the tr snippet from above.  There might be a different work around for this, and again I'm all ears, but for now this worked great.  Now that we have all of that in place it's time for us to bring it home with some JQuery script.

<script language="javascript">
  $(document).ready(function() {
    $("#postMessage").click(function() {
      $("#newMessage").fadeOut("slow");
      var form = $("#newMessage");
      var action = form.attr("action");
      var serializedForm = form.serialize();
      $.post(action, serializedForm, postAdded, "html");
      return false;
    });

    function postAdded(html) {
      $('#posts').append(html);
      $("#newMessage").fadeIn("fast");
    }
  });
</script>

 

I'm using the click event, but I believe you could also turn it into a submit button and handle a onsubmit event.  This might work better for browsers with javascript not enabled, but I'll have to investigate that further.  Once it's clicked I hide the postMessage box, grab the form, get it's action attribute, serialize the data from the form, and then use the .post to post to my controller in an ajax fashion.  Within the post you can see that my callback method is postAdded, this will fire when my New controller is finished with the snippet on my view.  I've specified "html", but it seems that this is not needed.  I kept it to remind myself that there are other data types that can be returned like "json", in case I decide to construct the html within the postAdded method instead or something.

Now that the html snippet is returned and passed into the postAdded method, I simply grab my post's tbody tag , #posts and then appending the snippet.  Then I make the comment box reappear.  Nifty!

posted @ 1/9/2009 10:39 AM by Willie

Control Adapters are like weak Kool Aid

After analysing, changing, and implementing a control adapter for the menu control I don't see the point.  I started off with the CSSFriendly adapters which were great and all, but then noticed that a CSS only solution wouldn't work for me.  I then started to tinker, and at the end, I'm thinking a custom control would have done the same thing but had more with less code.

I would say that if you have a ton of existing controls or existing controls that inherit parent .net controls then use the adapters as it's literally no work involved to get a better CSS format out of the controls.  However if you need to customize a specific control's output and you don't have it up on the page yet, use a custom control.  I'm already planning on ripping the code out of the adapter to place it into it's own control.

posted @ 1/7/2009 2:35 PM by Willie

OpenId's Attribute Exchange is the Problem not DotNetOpenId

As I suspected, DotNetOpenId is doing it's job fine, it's the Attribute Exchange that's the problem, or more specifically that none of the providers can pick a schema to use.  See this informative overflow q&a.  Looks like if you are going this route, use Simple Registration.  Now where's an example of that using DotNetOpenId?

posted @ 1/6/2009 9:06 AM by Willie

Table vs Table-less with Divs Layouts

Years ago, trying to be cool like the dude with the blue hat, I too tried to use only div's for my layout.  I mean, less bandwidth, cached style sheets and all that...why waste?  Problem is, I started to run into quirks.  Browser quirks specifically, and there was so many of them, mainly with IE, but not always.  Instead of being a great way to layout a site I now felt like there was a massive undertaking when just trying to have a simple 3 column layout with certain elements like a menu in specific places.  My test would mainly be to increase and decrease the font size and then resize the window.  I then repeated this across browsers and different browser versions.  I was NEVER EVER able to get a consistent look and feel across any of them.  I eventually gave up and went back to tables.  I've never looked back.  Why?

Tell me how you can get the exact same look cross Operating System, Browser, Version.  Tables.  End of story.  Whatever their nasty bloat they got me the same look that I wanted in 5 minutes.  I don't have to spend time looking up quirks or anything, and it feels pretty good.  Now I can focus on creating, and less on designing.  Have I mentioned that I hate designing?  Blah, let some artsy person do that, just not for me.  Me, I want to create a website that looks the same for everyone that's functional and does something.  More doing, less figuring.

I guess I'm one of the few though.  Being regulated to IE6 at work, it seems that quite a few people went down the DIV route.  And you know what?  Their sites look like shit on IE6.  Perhaps they just don't know about it, or maybe they just don’t care?  I looked up stats on how many people are still using IE6.  It looks like it’s around 20%.  That would be a big number folks! 

That means that 1 of 5 people coming to your site are looking at your site, wondering what’s wrong with it.  All because you used your awesome cool div tags.  Me, I’d rather serve up 100% of users with no problems.  I think my thinking has got some real backing too, backing by some moola.

Google, ebay, amazon:  All making lots of money and they are using tables for layout.  How about when they switch I will too.  They know what they are doing way more than little ole me.  Crap, I bet there are teams that analyzed each tag and came up with what they got.  Tables…

Now, I’m not saying CSS is bad or anything, in fact I’m a fan, but it’s mainly for styles and look n feel, not layouts.  On that front all browsers pretty much do the same thing and you get the benefits of not going back to the old crappy font tags of the past.

posted @ 1/5/2009 3:27 PM by Willie

Extending the GridView Part 3

Didn't get the earlier stuff?  Read up on Part 1 and Part 2

Download GridView.cs

Adding a CheckBox Column and Ascending/Descending classes to Headers

For this part I mainly took some code that was originally authored by Dino Esposito and then later expanded into maintaining the state of the checkbox columns across pagination requests by maxcarey.  Like a good boy scout though, I thought I'd leave the code a little prettier than when I found it so you can read maxcarey's stuff and then refactor it yourself or grab the code I've shared below.  As for the adding ascending and descending classes (which we will then style later with pretty arrows) I grabbed code from Scott Mitchel.

This is going to be more of a "code" based post as both Dino, maxcarey and Scott have pretty much covered the bases.  Get the explainations from them as I'm not really doing anything new, just refactoring.  Here’s a taste:

private void AddDataKeys(int rowIndex)
{
    bool contains = false;
    foreach (DataKey key in _selectedDataKeys)
    {
        if (CompareDataKeys(key, DataKeys[rowIndex]))
        {
            contains = true;
            break;
        }
    }

    if (contains == false)
    {
        _selectedDataKeys.Add(DataKeys[rowIndex]);
    }
}

private void RemoveDataKeys(int rowIndex)
{
    int removeIndex = -1;
    for (int j = 0; j < _selectedDataKeys.Count; j++)
    {
        if (CompareDataKeys((DataKey)_selectedDataKeys[j], DataKeys[rowIndex]))
        {
            removeIndex = j;
            break;
        }
    }

    if (removeIndex > -1)
    {
        _selectedDataKeys.RemoveAt(removeIndex);
    }
}

private void PopulateSelectedDataKeys()
{
    EnsureChildControls();
    for (int i = 0; i < Rows.Count; i++)
    {
        if (IsRowCheckBoxSelected(Rows[i]))
        {
            AddDataKeys(i);
        }
        else
        {
            RemoveDataKeys(i);
        }
    }
}

private bool IsRowCheckBoxSelected(GridViewRow row)
{
    CheckBox box = row.FindControl(AutoCheckBoxField.CheckBoxId) as CheckBox;
    bool boxExists = box != null;
    return boxExists && box.Checked;
}

private bool CompareDataKeys(DataKey keyA, DataKey keyB)
{
    bool numberOfValuesAreDifferent = keyA.Values.Count != keyB.Values.Count;
    if (numberOfValuesAreDifferent)
    {
        return false;
    }

    int mismatchedKeyIndex = FindMismatchedKeyIndex(keyA, keyB);

    bool allKeysMatched = mismatchedKeyIndex == keyA.Values.Count;
    return allKeysMatched;
}

private int FindMismatchedKeyIndex(DataKey keyA, DataKey keyB)
{
    int index = 0;
    while (index < keyA.Values.Count && keyA.Values[index].Equals(keyB.Values[index]))
    {
        index++;
    }
    return index;
}

protected virtual ArrayList AddCheckBoxColumn(ICollection existingColumns)
{
    ArrayList columns = new ArrayList(existingColumns);
    AutoCheckBoxField checkBoxField = new AutoCheckBoxField();
    //checkBoxField.HeaderText = "<input type=\"checkbox\" />"; // Could be used for a "check all" functionality
    checkBoxField.ReadOnly = true;

    if (CheckBoxColumnIndex > columns.Count)
    {
        CheckBoxColumnIndex = columns.Count;
    }
    columns.Insert(CheckBoxColumnIndex, checkBoxField);
    return columns;
}

And the method that kicks it all off:

protected override void PrepareControlHierarchy()
{
    base.PrepareControlHierarchy();
    if (HasControls())
    {
        Table grid = Controls[0] as Table;

        bool userHasSortedTheGrid = string.IsNullOrEmpty(SortExpression) == false && ShowHeader;
        if (userHasSortedTheGrid)
        {
            bool gridExistsAndHasSomeRows = grid != null && grid.Rows.Count > 0;
            if (gridExistsAndHasSomeRows)
            {
                GridViewRow headerRow = GetHeaderRow(grid);
                TableCell cell = GetCellSortedOn(headerRow);

                bool gridIsSorted = cell != null;
                if (gridIsSorted)
                {
                    CreateSortArrowOn(cell);
                }
            }
        }

    }       
}

posted @ 1/5/2009 3:13 PM by Willie

Implementing OpenId and more with ASP.NET MVC and RPXNow

OpenId is a single sign on that could eventually be a universal sign in for every site out there.  At least, in theory, it'd sure be nice.  Not having to remember multiple site passwords and stuff, just throw it your authenticated ticket and you're ready to rock.  MyOpenId has a pretty slick interface for managing an account with Personas and what not.  Their site mostly makes sense, although for some newbs I'm sure there would be a bit of confusion, but I think it'd be stupid to not include the option of signing up with a site in this manner.

Having said that I wanted to implement it on a new site that I've been developing.  I tapped DotNetOpenId before to try and get things rolling but as you can see from prior blog posts, wasn't very successful.  I was ready to give it up and wait a few more years, but then heard about RPXNow.com within a "I can't get DotNetOpenId" to work thread on the Overflow.  Also it was pretty crazy getting a comment on the blog minutes after complaining about how I couldn't get OpenId implemented properly, talk about service!

So what to do now...hit up the search engine to see if anyone's already done something with RPX.  Sure, rpxnow.com has their own "helper" cs file (available in other languages as well) but how about something that has some tests, uses dependency injection, and is a little more robust?  I'm not asking for much right?  Steven Burman had exactly that with his newly released RpxLib, some truly delicious code that my bits can bite.  And now I can really show up Scott Hanselman by having my own quote something like:

Wow, rpxnow.com is sweet. I was able to implement it in about 30 minutes on an ASP.NET MVC site. Blog post which shows you how to do it in 10 is right here Mr Hanselman.  -WT a Developer, not at Microsoft

Probably not as cool sounding but whatever.  And I did it in half the time!

To get this rolling grab the RpxLib version 0.0.2 or later and reference it in your projects.  I'm using Windsor and I have a Core project that contain my services and a Web that contains the VC parts of MVC.  Here's how I set everything up.  First, created a class that implemented IRPXApiSettings like so:

using System.Net;
using RPXLib.Interfaces;

namespace Services
{
  public class RpxApiSettings : IRPXApiSettings
  {
    public string ApiKey { get { return "<paste your API key from rpxnow.com here>"; } }
    public string ApiBaseUrl { get { return "https://rpxnow.com/api/v2/"; } }
    public IWebProxy WebProxy { get { return null; } }
  }
}

Pretty simple, you give it the API key from the RpxNow site, base URL and if you are using a Proxy place that here instead of what I've done. 

I created another class, which is optional, but Windsor is setup to automagically use my Services project to find dependent objects and I'm lazy.  Resharper essentially wrote this class for me :)

using RPXLib;
using RPXLib.Interfaces;

namespace Services
{
    public class RpxService : RPXService
    {
        public RpxService(IRPXApiSettings apiSettings) : base(apiSettings)
        {
        }

        public RpxService(IRPXApiWrapper apiWrapper) : base(apiWrapper)
        {
        }
    }
}
This is essentially a pass through class and really isn't needed.  If you don't wanna do this, follow Steven's advice on the wiki.  He knows what he's doing.
My view code for my localhost realm looks like:
<script src="https://rpxnow.com/openid/v2/widget"
        type="text/javascript"></script>
<script type="text/javascript">
  RPXNOW.token_url = "http://localhost:4502/User/Authenticate?ReturnUrl=${Request.Path}";

  RPXNOW.realm = "127-0-0-1";
  RPXNOW.overlay = true;
</script>
<a class="rpxnow" onclick="return false;" href="https://127-0-0-1.rpxnow.com/openid/v2/signin?token_url=http://localhost:4502/User/Authenticate">Sign In</a>

Spark allows me to put this in its own little control of sorts (a partial) which I can then splash around code like:

<if condition="LoggedIn">
 Howdy ${NickName} | <a href="~/User/Edit?ReturnUrl=${Request.Path}">My Account</a> | <a href="~/User/Signout">Sign Out</a></if>
<else>
 <Signin />
</else>

The only thing left is the controller which shows the awesomeness of both rpxnow.com and Steven's RpxLib.  Look at how little coding there is!

    private readonly IUserService _userService;
    private readonly IRPXService _rpxService;

    public UserController(IUserService userService, IRPXService rpxService) : base(userService)
    {
      _userService = userService;
      _rpxService = rpxService;
    }

    public object Authenticate(string token)
    {
      RPXAuthenticationDetails details = _rpxService.GetAuthenticationDetails(token);

      string identifier = details.Identifier;

      string returnUrl = Request.Params["ReturnUrl"];

      User user = _userService.FindUserBy(identifier);
      bool userExists = user != null;

      try
      {
        if (userExists == false)
        {
          user = _userService.GetUser(details, identifier);
          _userService.Save(user);
        }
        return Redirect(user, returnUrl);
      }
      catch
      {
        return Redirect(returnUrl);
      }
    }

    private object Redirect(User user, string returnUrl) {
      FormsAuthentication.SetAuthCookie(user.ClaimedId, true);
      return Redirect(returnUrl);
    }

With my UserService GetUser looking like:

    public User GetUser(RPXAuthenticationDetails details, string identifier)
    {
      User user = new User();
      user.ClaimedId = identifier;
      user.Email = details.Email;
      user.NickName = details.PreferredUsername;
      DateTime birthDate = new DateTime();
      if (DateTime.TryParse(details.Birthday, out birthDate))
      {
        user.BirthDate = birthDate;
      }
      user.Gender = details.Gender ?? string.Empty;
      user.FullName = details.DisplayName ?? string.Empty;
      user.UtcOffset = details.UtcOffset ?? "0";
      if (details.Address != null)
      {
        user.PostalCode = details.Address.PostalCode ?? string.Empty;
        user.Country = details.Address.Country ?? string.Empty;
      }
      user.AvatarUrl = details.PhotoUrl ?? string.Empty;
      return user;
    }

This seems to degrade nicely, as I've gone through testing it with most of the providers.  At the minimum it gives you the ClaimedId, Email, and NickName which was what I was trying to do all this time.  The other fields for me are just nice to haves.  If they are there, great if not let the user enter them through their profile page if they wanna. If a user closes the window, or decides not to share their information it just goes back to the original page. I finally have a User login piece that just feels solid.  I never felt that with DotNetOpenId as there was too much dependency on all of the providers.  RPXNow.com has taken the onus of all those dependencies and made it easy for us to implement it which feels pretty good.

posted @ 1/3/2009 8:51 AM by Willie

The Problem with Yahoo's OpenId Implementation

Yahoo seems to support OpenId in a sporadic matter.  I couldn't get it working with using the DotNetOpenId implementation but was able to get things going with RPX.

Given that, it seems that if a user is already logged into Yahoo the default option open is to just "Share Information" with the referring site.  There is no login, or for that matter a way to sign in as another user.  I talked about how the wife went to yahoo to sign in and then ended up browsing their site almost forgetting about what she was doing.  And no she isn't OCD, she was just illustrating a point.  When you sign out from yahoo it takes you to their homepage, and not to a log in screen where you can continue the process of logging in and then being referred back to the original site.

For now, I'm just going to hope this doesn't happen, but that's pretty huge.  Having seen the potential problems with this method, I think I will eventually need to make a user sign up page along with my RPX implementation.

posted @ 1/3/2009 7:52 AM by Willie

OpenID, just not an option

I've tried for a few days, roughly 8 hours to setup and configure OpenID for a new website I am making.  The benefits I thought would outweigh the cost of setting up something that I really know nothing about.  Blogs like ScottHa told me how simple and easy it was to get going.  I mean, offload all that membership and authentication work?  I'm sold.

Sadly it doesn't seem that everything is mature enough.  I used the existing dotnetopenid library, and that might be where I'm going wrong but probably not.  At first I was able to use it to login and grab an email and some user details and then I upgraded to a newer version 2.5.  Now I'm trying to use the Attribute Exchange stuff but my code keeps crapping out on request.RedirectToProvider() for no apparent reason.  I'm going to guess it's some kind of threading issue?  I'm getting a "Unable to evaluate expression because the code is optimized or a native frame is on top of the call stack."  Fantastic.

Once this dies, my code continues the login process like all is normal due to the fact that I've caught all exceptions, and don't know how to catch individual ones.  This just outputs the "Canceled" or "Not a valid username or provider" that I've written.  So the user goes off to the provider, and comes back and var fetch = openid.Response.GetExtension<FetchResponse>() always ends up null.  I thought maybe this was just something I was doing wrong, but remembered that Rob had put in OpenID into his MVC Storefront so I decided to try that out as well.  Guess what, his code isn't working for me either, in fact I couldn't even log in.  I grabbed dotnetopenid's sample code to try that out, and couldn't get anything to with that to work at all.  Sigh.  Since Fetch is null that pretty much invalidates what I was trying to do in the first place as I have no username, or email so I have to have a special case where I have the user enter that.  Precisely what I was trying to avoid.

Even if I did get it to work it seems that not all providers process everything the same.  I'm beginning to wonder what the hell kind of "standard" this is.  It seems that MyOpenID will return values in certain cases, and the yahoo provider is painfully incomplete.  WTF?  I've only tried those two, I wonder what other issues are with the others?  Then I get deeper into looking for a solution and find more problems that other people have come up with.  Mainly that there code that was working, now is no longer working.  Umm...ok.

So I could use OpenID but it could fail for any provider at any time because they change something on their end.  Then not all providers work the same?  Geezus man, all I'm trying to do is abstract getting a username, and email address.  Why in the hell is this that hard?  I want to be cool too, but good god this is tough.

I'm thinking even if that worked, I don't think anyone outside of a developer gets it.  I put it through the wife test and she seriously went to yahoo, and then proceeded to just start browsing the yahoo's site.  Going to their front page and reading the news.  OMFG, she's not a developer by any means, but she isn't a LCD granny type that forgets a password every 5 minutes.  I couldn't imagine what other LCD users would do.

I think instead of OpenID I'm going to increase my requirement to grabbing a password.  This will be definitely easier than this crap.  Yeah I might try out RPX because I'm feeling like more of a beating, as it seems to have no documentation either.  For laughs it quotes how easily Scott again got it to work "Wow, rpxnow.com is sweet. I was able to implement it in about an hour on an ASP.NET MVC site. Blog post soon." Scott Hanselman, Principal Program Manager, Microsoft.  And wouldn't you know it, I can't find the GD blog post, and I feel like more of an idiot that I've spent all this time doing essentially...nothing.

posted @ 1/1/2009 9:41 AM by Willie

Development Trends

Was reading another blog (and in my "excitement" closed the window...thanks IE 6!) but they were discussing the Google trends of some search words.  I came up with my own that I thought was really interesting:

http://www.google.com/trends?q=C%23+%7C+.net+%7C+dotnet+%7C+asp.net%2C+%28ruby+-gem%29+%7C+rails%2C+C%2B%2B%2C+Java%2C+php&ctab=0&geo=US&geor=all&date=mtd&sort=0

Seems like most people in the .NET, Java, PHP, and C++ world are using their languages at work, doing most of their searches during the week.  You can even see a dip around the Christmas break and .NET going down to Ruby levels.

Ruby is quite interesting.  It just keeps remaining the same throughout.  I'm guessing this might be because Ruby is a rock and Rails could be all sorts of things as well, but maybe not.  The only other thing that I tried to infer from this stuff was that Java seems to be the most popular, then .NET and Ruby and the others are behind.  .NET also seems to be way popular in Redmond, WA, where C++ also takes a lead as well.

Java seems to be way popular in San Jose, CA but Utah takes the cake for the state while Washington still wins with .NET stuff.  That's enough playing with that for now...

posted @ 12/31/2008 2:16 PM by Willie

Extending the GridView Part 2

Creating a Customized Pager

Now that we have our grid sorting and paging, lets get rid of that fugly pager.  We really don't need a lot of latitude on how our pager will look, just that it be uniform across all grids we make and allow us to quickly see how much information we have, how many pages there are, and how to quickly change the page.

Lets get started back in the extended GridView class we created in Part 1.  I overrode the InitializePager method to get us started.  Looks like:

protected override void InitializePager(GridViewRow row, int columnSpan, PagedDataSource pagedDataSource)
{
  if(Designmode == false)
  {
    TableCell cell = new TableCell();
    cell.ColumnSpan = columnSpan;
    cell.CssClass = "pager";
    row.Cells.Add(cell);
    CreatePager(cell, pagedDataSource);
  }
}

All I'm doing here is creating a cell and setting it's columnSpan to the given columnSpan passed into the method.  I give it a css class so that I can later style it up, and then I give that cell off to the CreatePager method.  Within there I want to have the code that makes up what I want the pager to look like.

private void CreatePager(TableCell container, PagedDataSource dataSource)
{
  int currentPage = dataSource.CurrentPageIndex + 1;
  int pages = dataSource.PageCount;
  int rowCount = dataSource.Count;
  int totalRowCount = dataSource.DataSourceCount;
  Table table = new Table();
  table.Width = new Unit("100%");
 
  TableRow tr = new TableRow();
  TableCell leftSide = new TableCell();
  TableCell rightSide = new TableCell();
  rightSide.HorizontalAlign = HorizontalAlign.Right;
 
  if(pages > 1)
  {
    AddFirstPrevButtons(leftSide, currentPage);
    AddXOfPagesBox(leftSide, currentPage, pages);
    AddNextLastButtons(leftSide, currentPage, pages);
  }
  AddSummary(rightSide, rowCount, totalRowCount);
 
  tr.Controls.Add(leftSide);
  tr.Controls.Add(rightSide);
  table.Controls.Add(tr);
}

As you can tell, I just create an inner table with two cells, a left and right side.  On the left side I want to first add the first and previous buttons, the a textbox that takes in a number to page to, and then the next and last buttons that can be used to page as well.  I only want to show the pager controls if the grid has more than one page.  On the right side I want to show a summary telling the user how many records there are, and what records we are currently viewing.

Here it is, some more code served sunny side up (because you are special):

private void AddText(Control container, string text)
{
  Literal literal = new Literal();
  literal.Text = text;
  container.Controls.Add(literal);
}

private void AddPagerButton(TableCell cell, string commandArgument, string text, bool enabled)
{
  Button btn = new Button { CommandName = "Page", CommandArgument = commandArgument, Text = text, Enabled = enabled };
  cell.Controls.Add(btn);
}
}

private void AddFirstPrevButtons(TableCell container, int currentPage)
{
  bool enabled = currentPage != 1;
  AddPagerButton(container, "First", "<<", enabled);
  AddPagerButton(container, "Prev", "<", enabled);
}

private void AddNextLastButtons(TableCell container, int currentPage, int pages)
{
  bool enabled = currentPage != pages;
  AddPagerButton(container, "Next", ">", enabled);
  AddPagerButton(container, "Last", ">>", enabled);
}

private void AddXOfPagesBox(TableCell container, int currentPage, int pages)
{
  AddText(container, "Page ");

  TextBox CurrentPage = new TextBox();
  CurrentPage.Columns = 3;
  CurrentPage.CssClass = "currentPage";
  CurrentPage.AutoPostBack = true;
  CurrentPage.Text = currentPage.ToString();
  container.Controls.Add(CurrentPage);

  AddText(container, string.Format(" of {0}", pages));

  CurrentPage.TextChanged += delegate
  {
    int newPageIndex = 0;
    bool newPageIndexIsValid = int.TryParse(CurrentPage.Text, out newPageIndex);
    if(newPageIndexIsValid)
    {
      bool exceedsNumberPages = newPageIndex > pages;
      if(exceedsNumberPages)
      {
        newPageIndex = pages;
      }
      if(newPageIndex < 1)
      {
        newPageIndex = 1;
      }
      PageIndex = newPageIndex - 1;
      BindGrid(SortExpression, SortDirection);
    }
  };
}

private void AddSummary(Control container, int rowCount, int totalRowCount)
{
  int firstResultIndex = PageIndex * PageSize + 1;
  int lastResultIndex = firstResultIndex + rowCount - 1;
  AddText(container, string.Format("Viewing {0} to {1} ({2} total)", firstResultIndex, lastResultIndex, totalRowCount));
}

And there we have it.  We have a text box that accepts a number, which when focus is lost an AutoPostBack occurs and changes the page number.  The one thing we need to add to our constructor is PagerSettings.Visible = true; so that our pager is always being shown.  This way our summary will always be shown with 1 or many pages.

This pager is used exactly how you'd use a pager from the GridView that we extended.  We can show it on the top, bottom, or both top and bottom, and I've given it a style so that we can later make the buttons pretty.  Yippee!

posted @ 12/29/2008 2:33 PM by Willie

Extending the GridView Part 1

Not able to partake in the ASP.NET MVC goodness or just wanna stick it out with WebForms?  Sooner or later you are going to want more from your GridView than just it's off the shelf capabilities.  Sure it's got custom paging and sorting, if you are using a fugly datasource control with it.  Man, is that pager fugly too!  And geez, no custom headers?  No way to filter the information that's displayed?  What about not having to specify the same ole look and feel for every grid I create? Meh, what a piece of junk this sucker is.  Good thing we can do better.  Lets get started with:

Note: I'm building a control all on the server side.  I've heard rumors of doing some of this crap all on the client, which is what I'm going to look into doing next, but for now, I want to outline how I've done it on the server.  Also, I found many various ways to do some of the stuff I'm going to show here.  If you have a better way, please share :P

Extending the GridView: Custom Paging and Sorting

Now there have been some other articles on how to do this.  Mainly they involve a data source control like the object data source or the sql data source.  Who actually uses those past a prototyping stage?  Not me, no sir.  I like the old fashioned way of doing this sort of crap:

Grid.DataSource = GetFakeData();
Grid.DataBind();

I also usually wrap those magical lines within something like a BindGrid method.  That way, I can easily call it when I have a postback or in an event or what not.  Like so:

Page_Load(object s, EventArgs e)
{
  if(!Page.IsPostBack)
    BindGrid();
}

Fair enough?  Now we just gotta wrap that up and have some way for our GridView itself to call our BindGrid when it needs to, which happens to be the sorting and paging we're talking about.  Pay attention!

First step is to create your Grid control.  Call it what you want, I normally create a project just for custom controls, mainly because after we create our awesome GridView we'll want to do some columns, maybe have a flashy button...who knows what else our mind will come up with.  Once I have my custom project I just keep it simple.  I will call my extended GridView a...wait for it...did you know that I could kick your butt at Racquetball...a GridView!  If you don't like calling your controls after what they inherited then make up your own name.  How about GridPoop.  I'm not that original.

Anyway, now that we have our custom control, get rid of everything that VS put into it except for something like:

namespace Controls
{
  [ToolboxData("<{0}:GridView runat=server>")]
  public class GridView : System.Web.UI.WebControls.GridView
  {
  }
}

Now that that mess is all cleaned up we're done!  Just kidding.  We need a way for our Page's BindGrid method to be attached to our control.  I'm going to use a delegate for this purpose.  I haven't really seen anyone else do it that way buuut, I've been doing it that way ever since the DataGrid was a wee babe so why change (unless you got a better way)?  Lets define our delegate for now like this:

public delegate void BindGridDelegate();

Which then we can create a property to set the delegate like this:

public BindGridDelegate BindGrid
{
  set;
  protected get;
}

Now that we have this setup, lets see how we'd use this.  Go ahead and add the assembly into your website's references and plop into your web.config under the <pages> tag:

<controls>
  <add prefix="my" assembly="Controls" namespace="Controls" />
</controls>

This will allow you to plop a <my:GridView ID="Grid" runat="server" /> on your page.  Now our Page_Load method from before will change just a tad to include the setting of the delegate. Observe:

Page_Load(object s, EventArgs e)
{
  Grid.BindGrid = BindGrid;
  if(!Page.IsPostBack)
    BindGrid();
}

That's pretty darn simple! A one-liner in fact. But if you run with this, nothing is going to change. Because we haven't setup the paging or sorting dumb ass!  Sheesh.

Paging is way simple. Almost boring in fact. We simply have to, within our extended GridView's constructor add a PageIndexChanging += PageIndex_Changing line. Our PageIndex_Changing event will be similar snore fest with:

protected void PageIndex_Changing(object sender, GridViewPageEventArgs args)
{
  PageIndex = args.NewPageIndex;
  BindGrid();
}

Now if you get that fugly pager out, with some data that exceeds the PageCount you be able to Page the data.  Nice eh?  See, I'm not a crackpot biatch!  But what about sorting you say?  Man, you are a grade A asshole.  Fine, no one calls you impatient right?

To sort we are going to need to change up our delegate, and the corresponding BindGrid method to accommodate the new variables that we are going to pass in.   In this case, we may vary slightly.  Use your best judgement here, I can't spoonfeed you forever.  I'm going to use:

public delegate void BindGridDelegate(string sortExpression, SortDirection sortDirection);

Which will then, of course, change the BindGrid method similarily.  If you weren't paying attention before, here it is again:

private void BindGrid(string sortColumn, SortDirection direction)
{
  Grid.DataSource = GetFakeData(sortColumn, direction);
  Grid.DataBind();
}

Our constructor will also need to wire up the event.  It's going to look like this now:

public GridView()
{
  AllowPaging = true;
  AllowSorting = true;
  PageIndexChanging += PageIndex_Changing;
  Sorting += Sorting_Grid;
}

We've made it to the last part.  The Sorting_Grid method.  I added two new properties to the GridView, and made them public in case the page using them needed to change something.  I've put them into Session variables, but whatever mechanism to persist them would be fine as well.  I chose Session as I wanted the GridView to remember the state of the sorting even if they navigated away from the page and then back again.  Looked like so:

public new string SortExpression
{
  get
  {
    string key = string.Format("{0}_SortColumn", ID);
    if(HttpContext.Current.Session[key] == null)
      HttpContext.Current.Session[key] = string.Empty;
    return HttpContext.Current.Session[key].ToString();
  }
  set
  {
    string key = string.Format("{0}_SortColumn", ID);
    HttpContext.Current.Session[key] = value;
  }
} public new SortDirection SortDirection
{
  get
  {
    string key = string.Format("{0}_SortDirection", ID);
    if(HttpContext.Current.Session[key] == null)
      HttpContext.Current.Session[key] = SortDirection.Ascending;
    return HttpContext.Current.Session[key].ToString();
  }
  set
  {
    string key = string.Format("{0}_SortDirection", ID);
    HttpContext.Current.Session[key] = value;
  }
}

The Sorting event contains a bit of hackery after I noticed that my GridView wasn't sorting opposite of what it was doing before.  This would be a chance for you dear reader to tell me wtf is going wrong.  Until then, this is working out hunky dory:

protected void Sorting_Grid(object sender, GridViewSortEventArgs args)
{
  if(SortExpression == e.SortExpression)
    SortDirection = SortDirection == SortDirection.Ascending ? SortDirection.Descending : SortDirection.Ascending;
  SortExpression = e.SortExpression;
  BindGrid(SortExpression, SortDirection);
}

Back in our page all we have to do is have a SortExpression on a column and Shazam, we now have sorting and pagination built in.  Feels good to never have to code that crap again for a GridView...

posted @ 12/24/2008 2:37 PM by Willie

SSMS Execution Plan – Insert Index Here

I was troubleshooting a performance issue with a particular stored procedure in our application.  Myself being a developer my thought process is DB Performance Issue => Add an Index.  That is about the extent of my knowledge and when that doesn’t work I stop questioning what exactly it is that a DBA does :)  To figure out where an index is needed I would usually zero in on WHERE clauses and make sure any filtered columns are indexed.   The problem is this stored procedure was ginormous and called several other sprocs on its path to destruction.  So, I thought I’d check out the execution plan.  So I was using SSMS 2005 because that is what was installed on the machine I was remoted in to.  Problem was the execution plan looks like it was a collaboration of Dr. Seuss and MC Escher.  I didn’t want to tie up the server so I saved the execution plan and copied it down to my local machine (the plan was over 1MB so you feel my pain).  I tried to grok the plan for a while but didn’t have any luck so I decided to run the sproc with the troublesome parameters on my local box to see if the performance was bad locally.  I run SSMS 2008 on my local machine and when I ran the query with the execution plan turned on, lo and behold, the Execution Plan told me in bright green letters “Hey idiot stick, you are missing an index on Table X with Columns (Y and Z) Include (A, B, C).”  Needless to say I thought this was an awesome feature and sure enough, adding the index as directed solved the performance problem.

If you are running SSMS 2008, you can repro the behavior by running the below script to create some data:

CREATE TABLE IndexMe(Guid UNIQUEIDENTIFIER NOT NULL)
GO

SET NOCOUNT ON

BEGIN TRAN

INSERT INTO IndexMe(Guid)
VALUES(NEWID())

DECLARE @i INT; SET @i = 0;

WHILE @i < 21 BEGIN
  INSERT INTO IndexMe(Guid)
  SELECT NEWID() FROM IndexMe
  SET @i = @i + 1
END

COMMIT TRAN
GO

Now, turn on the Execution Plan in the Query Analyzer and run this query:

SELECT * FROM IndexMe WHERE Guid > '889327B7-966E-4288-A459-EEE4EEA9D109'

You should be presented with something like so:

SSMS2008

The “Missing Index” tells you exactly what you need to do for your query to suck less.

posted @ 12/24/2008 11:52 AM by Bill Pierce

Using Perforce Visual Merge with Team Foundation Server

The best way to handle merging is to not do it at all :)  Merging can be a very painful process, especially if you are using the wrong tools.  I was a long time user of Perforce for source control and this company’s great tools made merging very straight forward.  I have now transitioned to a Team Foundation Server 2008 environment and while this tool is growing in usefulness, it still lacks in a few areas.  Luckily TFS can be configured to use existing merge tools, including Perforce Visual Merge (P4merge) from Perforce.

You can configure TFS to use a different merge tool by running the following from a Visual Studio Command Prompt:

tf diff /configure

This brings up a dialog like so

image

If you don’t already have a Merge operation line, you can add one, otherwise just modify the existing one and make it look like so:

image

If you are curious you can click the right-arrow to see what each command line argument represents.  You may be wondering why the command is for p4merge.bat instead of the executable.  P4Merge expects the original file, branch 1 file, branch 2 file, and the final merged file to all exist for it to operate properly.  Unfortunately, TFS does not create the merged file before it invokes the merge tool.  To work around this I created a simple batch file that will create the merge file at the path provided (%4) then invoke p4merge.exe.  Simply paste the following into p4merge.bat and you are ready to rock.

@ECHO OFF
COPY /Y NUL "%4"
START /WAIT /D "C:\Program Files (x86)\Perforce" p4merge.exe "%1" "%2" "%3" "%4"

posted @ 12/24/2008 10:45 AM by Bill Pierce

SessionItem class to deal with Session objects

What do you think of this code?  Essentially I can do things like this:

SessionItem<int> myId= new SessionItem<int>("myId");
            if (myId.Exists)
            {
                user = new User(myId.Value);
            }

and

            myId.Value = int.Parse(Grid.DataKeys[row.RowIndex].Value.ToString());

for session objects.  Code is:

using System;
using System.Web;

namespace Utils
{
    public class SessionItem<T>
    {
        private string key = string.Empty;

        public SessionItem (string key)
        {
            this.key = key;
        }

        public bool Exists
        {
            get
            {
                object value = HttpContext.Current.Session[key];
                return value != null;
            }
        }

        public T Value
        {
            get
            {
                try
                {
                    object value = HttpContext.Current.Session[key];
                    return (T)value;
                }
                catch
                {
                    throw new Exception(string.Format("No session value with a key \"{0}\" exists.", key));
                }
            }
            set
            {
                HttpContext.Current.Session[key] = value;
            }
        }
    }
}

posted @ 11/19/2008 10:59 AM by Willie

SQL and Code Completion

It seems that perhaps the SQL syntax people weren't thinking about code completion (or intellesense) when coming up with the language.

Typing in a SELECT will give you nothing, because you selecting from what?  Once you have the FROM tableName statement then you could go back to your select and specify column names but what a pain.  Maybe the SQL syntax should have looked like this:

FROM tableName

SELECT columnName, anotherOne

WHERE columnName = 'me'

Maybe the LINQ people saw this issue too so that's why they have it switched?  Makes more sense to me.  I wonder why no one has thought to switch it around, or have they?

posted @ 11/17/2008 12:35 PM by Willie

Is this missing in ASP.NET MVC or what?

The ability to send data to the server and then display the same view that you were on.  In ASP.NET it was extremely simple to do this, just have an event driven method that did some logic, with no redirect.  With the MVC flavor it seems that with each action I need a new view to go with it?  Is this just a change in my thinking or am I missing something?

For example, I have a list of comments on a thread (in a blog or forum or...).  Right now I'm viewing the thread with the comments and I have a box below that says reply.  I'll take in comments, the person's user name in a controller and then...I have to display a new view or...why can't I wrap my head around this?

Does the controller also have to take in the current view name and threadId that I was viewing so that I can show the correct view?  Seems kinda wierd...

posted @ 11/16/2008 11:20 AM by Willie

Small Steps to Coding Better Part 2: Eliminate comments from your code

I remember back in college my coding teachers would tell me that coding was a good part of a balanced breakfast.  It would help those that happened to have to work on your code in the future.  Most likely, that particular coder would have been you, so...comment well!

After coding for years now, I have to say I disagree.  I've been over commenting the crap out of code in the past when I really thought I was being helpful (I used to swear by GhostDoc and nDoc stuff...).  The problem resides in that code is changeable, and so when a specific code portion is changed we break the DRY principle because we have to update our comments as well.  Instead of repeating ourselves, lets make our code cleaner by removing all comments that don't show a thought process, buuuuut also in the same stroke keeping the readability that makes those verbose like comments so helpful in there.

It's a fairly simple process, that just takes a few days in adjusting your thinking while coding.  For instance here's what my code used to look like:

 

   1:        else // Show list of all  things in widget
   2:        {
   3:          if (!Page.IsPostBack)
   4:          {
   5:            BindGrid(string.Empty, 0);
   6:          }
   7:   
   8:          ExistingMonitorFiles.Visible = true;
   9:          ProcessMonitorFile.Visible = false;
  10:        }

and after:

   1:  else
   2:              {
   3:                  ShowListOfAllThingsInWidget();
   4:              }

What I've done here is simply take the comment I had, and refactor it into a method that contains the prior code.  Simple enough?  Instead of a comment, now I have a viable piece of code. If I do modify it, I will have to update the method name, which will in turn update all of those areas that are calling it, in essence updating the "comment".

One more example that comes up a ton, and you might not agree with at first:

if (ResultID != null) // Show results

To remove this comment I'll need to add a new boolean type, like so:

bool showResults = ResultID != null;

if (showResults)

Now, I hate adding extra lines to my code as much as the next guy, but this effectively gives us a valid in code "comment" that we can update a lot easier, and you'll find that you won't be testing for this again down the road because now showResults has way more meaning than ResultID != null.

Try it out, it's a mild switch in thinking while you're coding but in the end I think well worth it.

posted @ 11/4/2008 4:00 PM by Willie

Small Steps to Coding Better Part 1: Region commands, and why you should avoid them

As I get farther along in my goal for better coding I've started to notice something.  When before I've utilized the pre-processor region command I notice bad design.  Before, I thought I was being tidy by at least visually grouping elements of a class into a certain section of the code.  Very rarely though, does this make sense (the one place I can think of is still in the code behind for WebForm aspx pages [I typically have something like Properties, Events, Private Methods]).

If you finding yourself wanting to put region commands around a section of code, start to ask yourself, could this be it's own class?   What it comes down to is the single responsibility principle and when you are adding region commands you aren't following it.

posted @ 10/30/2008 11:15 AM by Willie

Silverlight ArrayOfXElement Error

Trying to use web services with Silverlight and getting this error?  Most likely your asmx web service is returning data using DataSets and guess what...Silverlight no likey the DataSet.  Either modify them to use DataTables or you going to be doing some hand editing of the service code the reference pooped out.

posted @ 10/22/2008 11:05 PM by Willie

Blogs I Follow

Justin Etheredge posted a list of blogs that he follows.  He also posted a snippet of LINQ that converted his OPML file to a simple unordered HTML list.  He encouraged his readership to let him know if there are any really good blogs he was missing.  I was curious to see which blogs he reads that I have not yet subscribed to.  I tweaked his LINQ slightly to output blogs that were in his OPML file, but not in mine.

var doc1 = XDocument.Load("http://media.codethinked.com/downloads/Opml.xml");
var doc2 = XDocument.Load("http://services.newsgator.com/ngws/svc/opml.aspx?uid=134891&mid=1");

var result = new XElement("ul",
  from d1 in doc1.Elements("opml").Elements("body").Elements("outline")
  join d2 in doc2.Elements("opml").Elements("body").Elements("outline")
    on d1.Attribute("htmlUrl").Value equals d2.Attribute("htmlUrl").Value into d3
  from d2 in d3.DefaultIfEmpty()
  where d2 == null
  select 
    new XElement("li", 
      new XElement("a", 
        new XAttribute("href", d1.Attribute("htmlUrl").Value), d1.Attribute("text").Value
      ), 
      " ", 
      new XElement("a", 
        new XAttribute("href", d1.Attribute("xmlUrl").Value), "RSS"
      )
    )
  );

using (var xw = XmlWriter.Create("diff.xml", new XmlWriterSettings { Indent = true }))  
{   
  result.WriteTo(xw);
}

diff.xml will contain blogs present in doc1 but not present in doc2.  This is by no means perfect so don’t cry if it includes a few entries present in both or doesn’t work with your OPML.  You could easily see which blogs both Justin and I read by changing the where clause.

Several readers wrote to Justin asking how he could possibly keep up with that many blogs.  I only spend about 20 minutes a day reading through new posts.  Only autonomous coding machines post everyday, so as long as I don’t lapse for more than a day or two it isn’t a problem keeping up.  I also skim long posts and flag them for a more thorough reading later.  Most of my subscriptions focus on .Net development but I have a few outliers like Zed Shaw’s blog.  It can make for an extremely entertaining read (as long as you are not the subject of his attentions :).

posted @ 10/21/2008 11:18 AM by Bill Pierce

Recent Projects not Populating

Chances are your recent documents are turned off.  For some reason these two are tied together.  Open up regedit:

HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer

and change the NoRecentDocsHistory to 0, most likely right now it's a 1.

Fixes for both 2005 and 2008 IDEs.

posted @ 10/20/2008 5:25 PM by Willie

NHibernate ProxyGenerators 1.0.0 Beta Released

Read about it at NHForge.org

del.icio.us Tags:

posted @ 10/7/2008 2:48 PM by Bill Pierce

Please Explain the Following

Can someone please explain how this is possible?Stumped

posted @ 10/6/2008 3:36 PM by Bill Pierce

Introducing NHibernate ProxyGenerators

NPG is dead.  Long live NHPG.  In an attempt to not duplicate information, I’m going to put most of the technical details on NHForge.  I will highlight here the improvements that have been made to NHPG since its original inception.

  • Renamed NPG.exe to NHPG.exe (Duh)
  • Improved error handling and informative error messages
  • You can now provide several mapping assemblies at once
  • Removed dependency on ActiveRecord.  Don’t cry though, you can still use it on AR assemblies.
  • Now with more cowbell
  • I was given the directive to support multiple proxy generation frameworks and while the design is there, you can currently get your proxies in any color, as long as it’s black.  (By color I mean framework and by black I mean DynamicProxy2)
  • Improved unit test coverage.  (By improved I mean there was none and now there is some)
  • Improved runtime logging (See previous bullet)
  • The code now looks like a slightly more intelligent monkey wrote it
  • Intermediate files are now cleaned up properly
  • Proxy assembly version is now synchronized with mapping assembly version
  • Working example now distributed with source

Where you can help.

  • Additional proxy generation implementations
  • The generation is dog slow
  • The need for intermediate files mentioned above could probably be avoided by someone who knows what they’re doing

Enjoy, and please proxy responsibly.

posted @ 9/22/2008 11:06 PM by Bill Pierce

Windows Live Writer on Server 2008

del.icio.us Tags:

This was way harder to find than it should have been.  There are various posts on downloading a Technical Preview that installs on Server 2008.  This installation told me it would expire shortly and it also appeared to be an older version (v12).  Some comment trolling turned up a German Windows Live Writer Blog that had what I was looking for.  The link at the very bottom installed what appears to be the latest beta for Windows Server 2008 x64.

WLW-Server2008

posted @ 9/22/2008 7:46 AM by Bill Pierce

Combining a dll and an exe into one exe file

I had a console app that used an external dll to zip files up.  The app worked great.  For the program to work properly it required both the files.  I wanted to just have the one exe with no dll.  My first search gave me some pay for utility (yucky) and then Bill saved the day once again by suggesting ILMerge.

I used ILMerge in the "Post-build event command line" box in my project settings like so (notice that you do indeed need to keep the quotes because of spaces in the path):

"c:\Program Files\Microsoft\ILMerge\ILMerge.exe" /wildcards /log:ILMerge.log /out:"$(TargetDir)JabuC.exe" "$(TargetPath)" "$(TargetDir)Ionic.Utils.Zip.dll"

This outputs what I wanted, one JabuC.exe that has the Ionic.Utils.Zip.dll included in it.  Presto!

posted @ 9/19/2008 9:19 AM by Willie

Sorting a List

I came across a different requirement today, I need to sort some objects in a certain order that wasn't alphabetical or numerical.  I looked towards SQL for a few minutes and found zilch that could help me with ORDER BY shinanigans.  I could have done multiple queries and then merged the results, but that would be pretty messy.  I ended up using Sort(), which I used before, but never to do anything more than a simple sort.

Consider that I have the following Employee objects with a Name and Position property like:

Joe, President

Frank, Vice President

Bob, Developer

that need to be sorted in that order.  Obviously sorting alphabetically wouldn't work so what to do?

Sort to the rescue!

Employees.Sort(CompareEmployeesByPositionInCompany);

with CompareEmployeesByPositionInCompany looking like:

private static int CompareEmployeesByPositionInCompany(Employee x, Employee y) {
            int xSortValue = GetSortValue(x.PositionInCompany);
            int ySortValue = GetSortValue(y.PositionInCompany);
            const int before = -1;
            const int same = 0;
            const int after = 1;

            if (xSortValue == ySortValue) {
               return same;
            }
            if (xSortValue < ySortValue) {
               return before;
            }
            return after;
         }

private static int GetSortValue(string value) {
            if (value == "President")  return 1;
            if (value == "Vice President") return 2;
            if (value == "Developer") return 3;

}

posted @ 8/12/2008 2:08 PM by Willie

Update Table with column and default value

When updating a table with a new column first I'd do something like:

alter table myTable add isGreat bit null

then fill that column with a value like so:

update myTable set isGreat = 0 where IsConfirmed IS NULL

Turns out there's a shortcut to that sucker:

alter table myTable add isGreat bit null default 0 with values;

posted @ 8/7/2008 12:11 PM by Willie