Programming, Policitcs, and uhhh Pineapples.
# Thursday, November 05, 2009

Analysis Paralysis : Getting Carried Away With UML

Thursday, November 05, 2009 5:07:09 PM UTC

UML is a useful tool, no doubt.  It's a tool to help model complex logic in a visual manner.  It's a language in and of itself and it can aid in communicating design ideas with exacting precision, leaving little room for error.  However, at the same time, as with any formal language, it creates strict rules for communicating in that language.  Syntax, vocabulary, "grammar"...these all apply to propperly using UML.

Myself, I've never been a big fan of UML.  There are different ways to convey ideas, intent, and understanding of a set of requirements, but imposing UML is like asking a blogger to write all of their posts in iambic pentameter when prose would work just as well.  What's the point?  Your colleagues can already all read English, but not everyone reads UML...why add that burden and rigor?

There's a big difference between the rigor required to build a bridge and the rigor required to build a web application.  Miss your target by a foot, and it's a multi-million dollar mistake if you're building a bridge.  If your bridge throws you an "unexpected exception"? You're talking possible risk of life!  A web application or a portal?  Unless there are fundamental, framework level mistakes, most changes are negotiable; it's software for a reason (and oddly enough, on large projects, the cost overhead isn't necessarily associated with the development side, but with the business side and the specific processes for validation, testing, and change management - maybe those guys need to fix their processes...).

Now this isn't to say that getting it right isn't important, but at some point, process and progress starts to drag as you impose an inordinate amount of rigor. 

With regards to sequence diagrams, Scott Ambler puts it very well:

The most important things that you can do is to keep your diagrams simple, both content wise and tool wise.  I will sketch sequence diagrams on whiteboards to think something through, either to verify the logic in a use case or to design a method or service.  I rarely keep sequence diagrams as I find their true value is in their creation.   

A common mistake is to try to create a complete set of sequence diagrams for your system.  I’ve seen project teams waste months creating several sequence diagrams for each of their use cases, one for the basic course of action and one for each alternate course.  My advice is to only create a sequence diagram when you have complex logic that you want to think through – if the logic is straightforward the sequence diagram won’t add any value, you had might as well go straight to code.

I agree wholeheartedly; the exercise itself is more important than the final artifact.  It's far too easy to interpret the intent of UML incorrectly; the value is not the artifact, the value is the process.

At the end of the day, it is not a silver bullet! It doesn't make your design more complete.  It doesn't mitigate all of the risk.  It doesn't make your intent free from misrepresentation or misinterpretation.  It doesn't make a design document bulletproof.

# Monday, October 05, 2009

Ordering Wood Floors Online

Monday, October 05, 2009 2:16:48 PM UTC

Growing up in the Internet Age, I've become fairly accustomed to and comfortable with buying just about everything online.

But even for me, ordering wood floors online -- by the pallet, no less -- was fraught with "what if's" and a bit of trepidation.  It isn't one of those things that you can just return if you get it and you don't like it or it's damaged or something and it's not exactly easy to get more product if you happen to be short a few square feet.

After doing much research and collecting dozens of samples (over a period of a few years...), I finally decided that I was ready to pull the trigger a few weeks ago and decided on BuildDirect.com for a couple of reasons:

  1. They were having a sale on some really nice engineered mahogany.
  2. Their prices couldn't be beat, even factoring in delivery charges (which are very high compared to some other e-tailers).
  3. Their web app was top notch; very well done.

This review is for people who are considering ordering from them (or ordering flooring online in general) since there weren't many online resources when I was researching them.

Ordering Samples

The sample ordering process is pretty simple and the samples are free (minus a delivery fee).  I've ordered at least a dozen samples from them over a period of two or three years, trying to find the perfect flooring.  Compared to other online flooring companies, I've found that they give pretty generous sample sizes in terms of length so you can get a better feel for the wood.  I will note that there are some products which you have to call to obtain a sample for.

I would recommend that when ordering certain types of wood, get multiple samples since with mahogany or Brazilian cherry, for example, different boards can show dramatically different colors and patterns.  We were considering mahogany, Brazilian cherry, acacia, and some of their twisted strand bamboo (really, really nice floors, IMO - definitely check these out).

I particularly like how the web app displays the weight information, the delivery source, and calculates the cost of shipping and adds it to the price per square foot calculation. 

Their sample ordering process gets a 5/5.

Ordering Product

Once you've decided on your flooring type and measured your square footage, the next step is to go online and order the flooring.  It should be noted that you should probably order at least 110% of your measured square footage.  We had installers put down our floors and the first crew wasn't very conservative with their usage and we ended up one box short.  At that point, you really have a tough choice to make; for us, we've decided to sell the extra flooring (or give it away to family members) instead of ordering additional flooring since the shipping costs would be silly.  Our kitchen was supposed to get the wood as well, but since we were short about 30 square feet, we decided to just leave it for now.

The initial ordering process is generally pretty easy.  It's not much more difficult than the sample ordering process.  However, there does seem to be this extra step of having to call their offices to confirm the order and what not.  At times, it can be difficult to reach anyone in the office (a secretary always picks up, but then you need to be transferred to the right department).  This became a pain-in-the-ass game of phone tag trying to reach the guy.

It was also a bit of a pain to adjust the order size once I had the installer come out and measure and estimate.  Again, it was hard to reach someone in the right department.

They were also initially somewhat inflexible with their pricing.  I had purchased a large quantity (most of it) on a sale price but then wanted to add an additional 400 or so square feet and some accessory pieces.  The sales rep. initially gave me some beef about adding the additional square footage at the original sales price until my wife called and threatened to cancel the order.  We did get the flooring at the sale price, but I don't understand why they made us jump through hoops to get it considering this order was coming in at several thousand dollars already.

One minor gripe is that you can't just go online and order more accessories; if you try to do it online, you have to order more product to be able to do it.  It seems like you should be able to go to your original order and add a limited quantity of product or accessory pieces.  In the end, I simply had to call them up to get one or two more transition pieces, but still, it would have been easier to do it online.

Their ordering process gets a 2.5/5.

Delivery

The delivery was probably one of the most annoying aspects.  The first issue is that their delivery window is very large; you won't know when you'll get it until you get a call the day before asking you to schedule delivery.  This is a pain in the ass.

The second issue is that while their web site is generally pretty well done, their delivery estimates aren't very well integrated with their stock levels as we were delayed by a month as their stock levels were low.  This wasn't an issue for us since our schedule was flexible, but I can see how this might be an issue if you're in the process of building a house and you have a much less flexible timeline.

The actual delivery itself was a bit of a pain as well.  The product comes in pallets, but the pallets for our product were like 8x4 (oblong), which makes it a bit harder to store (say in your garage since you have to clear out a lot more space).  Not only that, they're only supposed to drop it in your driveway!  As I wasn't home that week, we asked my father-in-law to accept delivery for us.  He gave the driver a $50 tip to help him get it into the garage using his hand lift.  Just beware that the delivery process is less than optimal for average folk.

It would be one thing if they could give you a very specific delivery date and then you could schedule your installers to be there at the same time to move/install the product, but the combination of such a wide delivery window and delivery service (driveway drop) makes it a bit of a pain.

On the other hand, I have to say, the product was very well packaged and had no damage at all.  This was one of my primary concerns, that there would be damage during transit and that we'd have to deal with a messy return process.

Their delivery process gets a 3/5.

Product

Despite all that, the wife and I absolutely love the floors.  The engineered material is pretty good at this price point when you compare it against the stuff you'd find in big box stores like Home Depot or even from Lumber Liquidators.  The specific engineered flooring that we got, their house brand Vanier Santos Mahogany, came with a 4mm wear layer.  At this price point, if you were going to get it from HD or Lowes, you'd probably be getting something like a 0.5mm wear layer.  With a 4mm wear layer, it can be sanded and refinished at least once in its lifetime (probably twice).

From a durability perspective, we've only had it for a few weeks now, but we have three cats and as far as I can tell, it looks to be holding up well to their claws.  I was worried about rolling around in the office chairs and leaving marks, but so far, it's been fine, even without a protective plastic mat or an area rug.

I was a bit worried watching the installers handle the product since they were pretty rough with it, tossing around pieces here and there, stepping over them, hammering it pretty heavily with rubber mallets...but it held up.  There were only like one or two places where their rough handling was apparent (possibly from dropping a nail gun).  Otherwise, the boards seemed fairly resilient - no breakage, no splitting of the veneer from the base, no cracking.  I should note that watching the installers cut the boards lengthwise, they did it by simply using a hand held circular saw and they were able to get very clean cuts.  I would think this speaks a bit to the workability of the product.

Overall, in the 60 some boxes that were installed, there were probably only 3-4 boards that had defects in them (hairline cracks in the finish, splintered tongue, etc) and only a few boards which weren't very attractive (only based on the specific tree or the section of the tree that it was cut from - we set these aside or used them in closets).  Otherwise, the boards were remarkably well milled and 99% perfect in terms of being square and straight.  The accessory pieces also matched nicely in terms of color and finish.

The packaging should also be noted as well: the contents of each box were sealed with fairly durable plastic and there were thin layers of foam sheets between each layer of the product inside the box.  I was pretty impressed with the lengths they went to ensure that the product wasn't going to be damaged in transit (although I wasn't too happy about how much waste this generated).

The product itself gets a 5/5.

Overall

I'd say I'd probably order from them again in the future and would recommend them to friends and family.  In the end, I think it's a great value as long as you're willing to plan it out properly and accept the risks with ordering this stuff sight unseen (it can be really hard to judge some types of flooring based on samples).

I'd say it's a solid 4/5.

# Wednesday, September 30, 2009

Chain Of Command And Passing Parameters

Wednesday, September 30, 2009 2:02:46 AM UTC

One of the more useful patterns that I've used quite frequently is a version of Chain of Responsibility that integrates with the Command pattern.  In a classic CoR, the idea is that only one component in the chain handles the request and then execution flows out of the chain.  In a CoC pattern, the idea is that the execution flows through the entire chain.

I like Shahan Khatchadourian's description of this pattern:

When programming, certain sections of code can sometimes be viewed as a workflow or preset sequence of tasks or commands. This can considered to be the design pattern called Chain of Command...

There are two key problems that this pattern solves that make it immensely useful in everyday programming (it's a bit surprising that dofactory's listing of CoR lists the frequency of use as a 2/5).

The first problem that it solves is extensibility.  By implementing the chain as a list of abstract types (Command or Validator or whatever), using reflection (one way or another), you can build a list of concrete commands, giving each element in the chain a chance of working on the input request.  One example of how I use this is to implement validation where I might have an abstract base class called Validator.  To build the chain of validators, one very quick and easy solution is to reflect on the assembly and simply find all of the classes which implement Validator (additional complexity can be added as necessary, such as supporting validators in external assemblies or different groupings of validators).

The second (related) problem that it solves is excessively large blocks of if statements.  In a validation example, you can imagine that if it were written in-line, each validation rule would essentially map to an if statement in a large block.  In a way, it's a very useful pattern for exchanging a tiny bit of performance and memory for more modular organization of logic.  Without the CoC pattern, adding a new validation rule would mean adding another if block - yuck!  Using CoC with reflection, we can simply add another class which inherits Validator to our project and count on the component building the chain to find our class and add it to the chain.

Here is a very simple, barebones implementation:

/// <summary>
/// A simple command chain factory that doesn't do wiring.
/// </summary>
public static class SimpleCommandChainFactory
{
    /// <summary>
    /// Creates a simple list of commands to execute using reflection.
    /// </summary>
    public static List<Command> Create()
    {
        var commands = new List<Command>();

        Type[] types = Assembly.GetExecutingAssembly().GetTypes();
        Type commandType = typeof (Command);

        foreach (Type type in types)
        {
            if (!type.IsSubclassOf(commandType))
            {
                continue;
            }

            MemberInitExpression init = Expression.MemberInit(
                Expression.New(type), new MemberBinding[0]);

            Command command = Expression.Lambda<Func<Command>>(init)
                .Compile().Invoke();

            commands.Add(command);
        }

        commands = commands.OrderBy(c => c.Priority).ToList();

        return commands;
    }
}

The code checks to see if a type if a sub-type of Command and, if so, creates an instance and puts it on the chain.  The commands could then be executed like so:

internal class Program
{
    private static void Main(string[] args)
    {
        List<Command> commands = SimpleCommandChainFactory.Create();

        // Execute each command.
        foreach(Command command in commands)
        {
            command.Execute();
        }
    }
}

In this case, I'm not passing in data or checking for stop conditions (which might be useful in a validation scenario where the first failure stops processing).  To do so, you could simply pass in a single instance of some context class to each command when executing and check to see if the stop condition is true after the execute call (and break out of the for-loop).

While this pattern is immensely useful any time you find a big if or switch block that's particularly volatile, one problem that I've found with this pattern is passing parameters between two elements in the chain.  Ideally, no element in the chain should have a dependency on another element in the chain.  We want to decouple each of the elements to make it easier to build the chain dynamically.  What this means is that no element should directly set values on another element in the chain.  In essensce, to emulate the functionality of DependencyObject and DependencyProperty that we find in WF and WPF.  (Why not just use WF then?  Complexity and performance.)

At least two solutions come to mind.  The first is to pass a context with a dictionary through each element of the chain.  This would allow each element to place an output value into the dictionary and downstream components to pull these values out.  The downside of this approach is that unless you force everything into one value type (i.e. serialize to an XML string?), you can't really pass strongly typed data and now you're keyed by strings (or whatever value type) which you need to have know about in order to retrieve the value. 

A second approach would be to leverage DependencyObject and DependencyProperty.  While this sounds good in principle, it requires a mess of code to accomplish in your own code with your own objects.  Not only that, it seems to be overkill since many times, you don't need the full capabilities of the dependency system - you just want to pass a value downstream in a nice, strongly typed manner.

(There is a third approach using thread local storage, but this is probably an even worse option than the first since it wouldn't be very accessible to most developers and it doesn't really address the issue at hand.)

In the past, I've relied on the dictionary based approach.  While it wasn't ideal, it was the simplest solution that got the job done.  Deep down, I always hated this approach because I didn't like having to know the keys and having to know how to cast the results retrieved from the dictionary.  However, I recently came up with a much better solution to this issue: dynamically wired events.

We introduce two attribute classes which we can use to identify our event publishers and event subscribers.  For brevity, I'll only show the publisher attribute (they are pretty much the same in this implementation):

/// <summary>
/// Attribute used to identify event publishers.
/// </summary>
[AttributeUsage(AttributeTargets.Event)]
public class EventPublisherAttribute : Attribute
{
    private readonly string _eventName;

    /// <summary>
    /// Initializes a new instance of the <see cref="EventPublisherAttribute"/> class.
    /// </summary>
    /// <param name="eventName">Name of the event.</param>
    public EventPublisherAttribute(string eventName)
    {
        _eventName = eventName;
    }

    /// <summary>
    /// Gets the name of the event.
    /// </summary>
    /// <value>The name of the event.</value>
    public string EventName
    {
        get { return _eventName; }
    }
}

The only difference between the two in this case is the AttributeUsageAttribute.  In the case of the subscriber, we want it to apply to methods, not events.  Next, we need to apply these attributes to our concrete command types that we're going to chain.  For this example, let's say that the first item in the chain generates a GUID key that the rest of the items in the chain also need to use:

/// <summary>
/// Generates a GUID that may be needed by the rest of the chain.
/// </summary>
public class GenerateKeyCommand : Command
{
    /// <summary>
    /// Raised when a key is generated;
    /// </summary>
    [EventPublisher(EventNames.KeyGenerated)]
    public event EventHandler<EventArgs<Guid>> KeyGenerated;

    /// <summary>
    /// Executes this instance.
    /// </summary>
    public override void Execute()
    {
        Guid key = Guid.NewGuid();

        Console.Out.WriteLine("From GenerateKeyCommand: {0}", key);

        OnKeyGenerated(key);
    }

    /// <summary>
    /// Gets the priority.  A lower value indicates higher priority.
    /// </summary>
    /// <value>The priority.</value>
    public override int Priority
    {
        get { return 1; }
    }

    /// <summary>
    /// Raises the key generated event.
    /// </summary>
    /// <param name="key">The key.</param>
    private void OnKeyGenerated(Guid key)
    {
        if(KeyGenerated != null)
        {
            KeyGenerated(this, new EventArgs<Guid>(key));
        }
    }
}

As you can see, it's pretty standard stuff, with the exception of the additional attribute on the event.  Downstream, we want to handle these events in other commands:

/// <summary>
/// Simple command just for demonstration purposes.
/// </summary>
public class DoSomethingWithKeyCommand : Command
{
    private Guid _key;

    /// <summary>
    /// Executes this instance.
    /// </summary>
    public override void Execute()
    {
        Console.Out.WriteLine("From DoSomethingWithKeyCommand: {0}", _key);
    }

    /// <summary>
    /// Gets the priority.  A lower value indicates higher priority.
    /// </summary>
    /// <value>The priority.</value>
    public override int Priority
    {
        get { return 100; }
    }

    [EventSubscriber(EventNames.KeyGenerated)]
    private void HandleKeyGenerated(object sender, EventArgs<Guid> e)
    {
        _key = e.Data;
    }
}

You can see that in the event handler method, we just grab the value from the event arguments and set it on a local variable for use when Execute() is called.

Now the trick is to wire these events up using reflection to avoid creating the direct dependency between the different elements in the chain.  Spring.NET offers one way to do this using declarative events, however, it should be noted that it only works with singleton objects (this bit me in the butt until I figured it out).  Depending on the nature of your elements in the chain, that may or may not be sufficient for you.  If you need new instances every time, then we can accomplish this ourselves using a bit of reflection.

(Note that none of the code that follows has been optimized; there are a few caching opportunities to take advantage of to cut down on some of the reflection calls.)

The first step is to modify the factory method:

/// <summary>
/// Creates a command chain using reflection.
/// </summary>
/// <returns></returns>
public static List<Command> Create()
{
    List<Command> commands = new List<Command>();

    Type[] types = Assembly.GetExecutingAssembly().GetTypes();
    Type commandType = typeof (Command);

    Dictionary<string, List<EventCoupling>> eventSources
        = new Dictionary<string, List<EventCoupling>>();

    Dictionary<string, List<MethodCoupling>> eventTargets
        = new Dictionary<string, List<MethodCoupling>>();

    foreach(Type type in types)
    {
        if(!type.IsSubclassOf(commandType))
        {
            continue;
        }

        MemberInitExpression init = Expression.MemberInit(
            Expression.New(type), new MemberBinding[0]);

        Command command = Expression.Lambda<Func<Command>>(init)
            .Compile().Invoke();

        commands.Add(command);

        BuildHandlerCache(command, eventTargets, 
            type.GetMethods(_methodFlags));

        // Parse the events.
        EventInfo[] events = type.GetEvents(
            BindingFlags.Public | BindingFlags.Instance);

        if(events.Length == 0)
        {
            continue; 
        }

        BuildEventCache(command, eventSources, events);
    }

    WireEvents(eventSources, eventTargets);

    commands = commands.OrderBy(c => c.Priority).ToList();

    return commands;
}

We create two caches as we iterate through the types to hold the events and handler methods that we encounter as we iterate the types and as a final step, we wire the events together from the caches.  The cache building logic is fairly straightforward:

/// <summary>
/// Builds the handler cache.
/// </summary>
/// <param name="command">The command.</param>
/// <param name="eventTargets">The event targets.</param>
/// <param name="methods">The methods.</param>
private static void BuildHandlerCache(
    Command command, 
    IDictionary<string, List<MethodCoupling>> eventTargets, 
    IEnumerable<MethodInfo> methods)
{
    foreach(MethodInfo method in methods)
    {
        EventSubscriberAttribute[] subscriberAttributes =
            (EventSubscriberAttribute[])
            method.GetCustomAttributes(
                typeof(EventSubscriberAttribute), false);

        if (subscriberAttributes.Length == 0)
        {
            continue;
        }

        foreach(EventSubscriberAttribute attribute in subscriberAttributes)
        {
            if(!eventTargets.ContainsKey(attribute.EventName))
            {
                eventTargets[attribute.EventName] = new List<MethodCoupling>();
            }

            eventTargets[attribute.EventName].Add(
                new MethodCoupling(method, command));
        }
    }
}

/// <summary>
/// Builds the event caches.
/// </summary>
/// <param name="command">The command.</param>
/// <param name="eventSources">The event sources.</param>
/// <param name="events">The events.</param>
private static void BuildEventCache(
    Command command,
    IDictionary<string, List<EventCoupling>> eventSources,  
    IEnumerable<EventInfo> events)
{
    foreach(EventInfo eventInfo in events)
    {
        EventPublisherAttribute[] publisherAttributes =
            (EventPublisherAttribute[])
            eventInfo.GetCustomAttributes(
                typeof (EventPublisherAttribute), false);

        if(publisherAttributes.Length == 0)
        {
            continue;
        }

        foreach (EventPublisherAttribute attribute in publisherAttributes)
        {
            if(!eventSources.ContainsKey(attribute.EventName))
            {
                eventSources[attribute.EventName] = new List<EventCoupling>();
            }

            eventSources[attribute.EventName].Add(
                new EventCoupling(eventInfo, command));
        }
    }
}

The gist of it is that we want to iterate through the events and the methods, find the ones with the attributes, map them to instances of commands, and throw them into a dictionary.  As you can see, the dictionary values are generic lists on both sides; this means that a single event can fire multiple event names and a single handler method can handle multiple events (as long as the method input types are the same).  This may or may not be useful in any given scenario, but it's easy enough to rewrite this to make it a bit simpler if it's not required.

Finally, we need to wire the events together in the chain after it's created:

private static void WireEvents(
    Dictionary<string, List<EventCoupling>> eventSources, 
    Dictionary<string, List<MethodCoupling>> eventTargets)
{
    foreach(string key in eventSources.Keys)
    {
        if(!eventTargets.ContainsKey(key))
        {
            continue;
        }

        List<MethodCoupling> targets = eventTargets[key];
        List<EventCoupling> sources = eventSources[key];

        foreach(EventCoupling source in sources)
        {
            foreach(MethodCoupling target in targets)
            {
                Delegate d = Delegate.CreateDelegate(
                    source.Event.EventHandlerType, 
                    target.Command, 
                    target.Method);

                source.Event.AddEventHandler(source.Command, d);
            }
        }
    }
}

It's as simple as that: we loop through each event (publishers) and see if there is a list of methods (subscribers) to handle it in the chain.  If so, we add a delegate as a handler to the event.  In my sample project, I created three simple command types to demonstrate; here's the output when I run my program:

You can see, once the key is generated in the first command, the value is available in the downstream commands using the events.  The nice thing is that we can add more steps to our logic without much extra work.  This is particularly handy for something like implementing a chain of validation rules as it means that you don't end up writing a big if if block.  But even in general usage, this pattern is useful for breaking out a large method into smaller, more modular pieces in a much more extensible manner.

One neat thing is that it allows you to not only wire events downstream, but also upstream as well.  This means that if an element in your chain triggers an event, code in a previous even is executed if there is a handler wired for it.

In a more complete implementation, you may consider using Spring.NET or Unity or simply .NET configuration to statically identify the elements of the chain (instead of the basic reflection I've used).  You may also consider more error handling logic ;-) and passing an instance of a context through each element in the chain.

The full sample project is available here: ChainOfCommandSample.zip (11.21 KB)

# Monday, September 28, 2009

SharePoint? Is That You?

Monday, September 28, 2009 6:46:05 PM UTC

Weird discovery of the day: Recovery.gov is SharePoint (check the source or try a search).

Cool.

# Saturday, September 26, 2009

Best Search Hit?

Saturday, September 26, 2009 12:04:39 AM UTC

Might be one of the best ones in the four years that I've had this blog.  I feel your pain dude, but I don't know the answer...sorry bro.
# Monday, September 21, 2009

Yet Another .NET Interview Questions List

Monday, September 21, 2009 5:56:11 PM UTC

There are tons of blog posts on .NET interview questions out there on the 'Net; here's another list...just because I feel like it, okay :-P?

In general, when I am interviewing people, I don't go for the obvious questions.  Not only are they boring because I've answered them so many times, but most of them can be easily studied for.  For most C#/.NET positions, there's a pretty standard set of questions that you'll encounter, most of which can be easily answered by simply reading Troelsen's Pro C# (I know because I picked it up the first time I was burned after a phone screen - I recommend this book to all junior developers looking to move up the payscale).

I do incorporate a few "template" questions, but in general, I like to keep things away from fact based questions and geared towards open-ended questions (I want to see that a candidate has actually done more than just use a feature after looking it up on MSDN or whatever - I want to see that a developer has actually sat there and thought about the technology or feature or whatever). 

Here are a few of my favorite questions for interviews; perhaps you'll find them useful for your own interviews:

[ASP.NET] Discuss the strengths and weaknesses of ASP.NET, out of the box.

I ask this question because I want to see if a developer has thought really thought about ASP.NET as a framework.  Developers who have can answer this pretty easily.  Developers who just write the code and move on will give some really perplexing answers or flat out stumble on this one.  I like this question because it's completely open-ended and a developer is free to use anything in his/her past experience as a context.  Some developers may compare and constrast it to other frameworks they've used (ASP, JSP, Python, Ruby, etc).  Some will describe past frustrations or projects as points of reference for weaknesses.  In general, I like this question because it shows whether an individual can discuss the technology intelligently.

[ASP.NET] What's the difference between an HtmlControl and a WebControl?

What I'm looking to find out with this question is whether a candidate can show some restraint with regards to using web controls on a page where an HTML control would work just as well.

[ASP.NET] Can you describe any approaches or patterns to make ASP.NET web forms programming more manageable?

In the simplest case, I'd like to hear something like "In the past, I've implemented MVC" or any sort of presenter/controller-view style pattern or - at the least - "I've created a custom base class which inherits Page", to show that the developer won't be inclined to just throw a bunch of code in the codebehind and call it a day.  One common answer is some variation of "I use an N-tier approach", but I find this answer to be insufficient since an N-tier approach doesn't mean much in terms of ensuring that your UI code is clean and well organized.  Developers who mostly think of ASP.NET as drag-drop-fill in code will never be able to give any sort of satisfactory answer to this question.

[.NET] How many major versions of the .NET runtime have there been?

Okay, this one is kind of a "gotcha" question, I admit, but it is relevant for a couple of reasons.  Developers who follow up on blogs and stay current will undoubtedly know that there is a new version of the runtime shipping with the next release of VS/.NET.  Developers who have worked extensively with ASP.NET will also know that in IIS, you can only select from 1.x or 2.x versions of the runtime.  I don't think anyone has gotten this one right yet, even though I put a particular emphasis on "runtime" when reading the question.

[.NET] What access modifier does Microsoft recommend for constructors on abstract classes?

What I'm hoping for is to one day hear: "Well, according to Framework Design Guidelines..." (or something equivalent).  The goal of the question is to see if a candidate understands some of the nuances of API and framework design, especially important for senior developer roles.  There are other questions along this vein, one of my other favorites is...

[.NET] What does the following code statement imply?

public readonly List MyStrings;

Again, the goal is to see how well a candidate understands the implications of their design decisions and some basic C#.  I won't give up on a candidate if they get it wrong; I try to coax them the correct conclusion, but few candidates can right the course once they make up their mind on this one.

If they bring up ReadOnlyCollection, they get bonus points.

[.NET] Can you expose abstract classes in an ASMX or WCF service contract?

This question can, answered correctly, indicate an above average level of understanding of .NET web services but, more importantly, I think it offers a peek at whether a candidate embraces object oriented design principals.  Candidates who have designed or worked with systems with rich object models will have undoubtedly encountered this problem (unless there were some specific interoperability scenarios which they had to design around).

[.NET] What is a custom attribute and how can you read one?

This question can reveal a lot about a candidate since there are some design scenarios that can be resolved pretty elegantly by taking advantage of custom attributes.  In addition, a candidate that can answer this question necessarily has experience working with reflection.  With regards to the second part, I'm not looking for specifics in terms of syntax and namespaces and classes, but rather a generic answer like "By using reflection" or something.

[.NET] What is the default() statement used for?

This is more of a textbook question, but candidates who have worked extensively with generics will be able to answer this with ease.  Again, being able to answer this reveals a lot about a candidate, especially when considering one for a senior developer position since being able to leverage generics is a big part of writing a solid API or framework.

[.NET] Desribe your approach to exception handling.

Another open-ended question that allows a candidate to shine - or to flail.  I've heard a wide range of responses to this one, but none that indicate that a developer has put any significant thought into one of the most important aspects of writing code on the .NET platform (especially framework level code).  Most responses fall into the basic structural elements of exception handling (try-catch-finally), but I am looking forward to the day that someone gives a response which addresses it at a much higher level than that.

[GENERAL] What is object oriented programming?

I usually preface this by stating that I'm not looking for the bullet-list textbook definition of it; I'm looking for a candidate to provide a much deeper answer than that.  There's no "right" answer, but there are definitely bad answers or responses (I've heard some wacky ones) that reveal that a candidate hasn't really thought deeply about just what it means to write good object oriented code.  To me?  Aside from the textbook stuff, OOP is about modeling complexity using structural interactions instead of straight-line, imperative logic.

This is just a small slice of my list, but they are perhaps the most important ones with regards to ASP.NET/C#, IMO.  What do you think?  Too abstract?  Too high level?  Too awesome ;-) ?  Hopefully, you've found something useful in here, either as an interviewer or an interviewee.

# Saturday, September 19, 2009

MbUnit CsvDataAttribute

Saturday, September 19, 2009 2:41:44 PM UTC

MbUnit has several cool features which distinguish it from some of the other unit testing frameworks on the .NET platform.  Among them are the RollbackAttribute, PrincipalAttribute, ThreadedRepeatAttribute, and the Csv/XmlDataAttribute.

I hadn't noticed the CsvDataAttribute previously when I've worked with MbUnit, but it's definitely one that I think that most teams can make the most use of.  While the RowAttribute allows developers to externalize and parameterize their unit tests, the CsvDataAttribute takes it to another level by allowing developers to put test parameters in a simple text file.  This is extremely handy since it becomes easier to add more test conditions as you come up with new scenarios without recompiling code.  Theoretically, you could even involve your QA team in getting the right set of test data since they could modify the external CSV file.  I find this extremely handy :-)

The documentation on how to use it was lacking a bit; while it explained that you can add metadata (custom attributes) via the CSV file, it didn't give an example for the ExpectedExceptionAttribute, one of the most common ones, I'd imagine.

Consider the following property which normalizes and validates a phone number (note: this was meant as a simple example):

/// <summary>
/// Gets or sets the number.
/// </summary>
/// <value>The number.</value>
public string Number
{
    get { return _number; }
    set
    {
        if (string.IsNullOrEmpty(value))
        {
            throw new ArgumentException(
                "The phone number cannot be null or empty.");
        }

        // Grab all the digits.
        char[] digits = value.ToCharArray()
            .Where(c => char.IsDigit(c)).ToArray();

        if (digits.Length != 10)
        {
            throw new FormatException(
                "A phone number must contain 10 digits.");
        }

        _number = new string(digits);
    }
}

The test method might look like this:

[Test]
[CsvData(FilePath = "CsvData\\PhoneNumbers.txt", HasHeader = true)]
public void TestPhoneNumberNormalizationWithCsv(
    string type, string number, string expected)
{
    PhoneNumber phoneNumber = new PhoneNumber(0, 0, number, type);

    Assert.AreEqual(expected, phoneNumber.Number);
}

Now we'd like to test our validation logic to gaurd against future refactorings to make sure that anyone refactoring this code throws the appropriate exceptions that our downstream callers expect.

You can see that I've used the FilePath property and the HasHeader property (you have to use this if there is a header, otherwise, it detects the header as a row; it's not true by default it seems).  The text file to go with this test would then look like:

Type, Number, Expected, [ExpectedException]
Home, (732) 555-1012, 7325551012
Home, , , ArgumentException

There are a few things to note here:

  1. If no exceptions are associated with the row, don't include a trailing comma and empty value (see the first line).
  2. The headers are not case sensitive.
  3. Null values can be specified using an empty value.
  4. When specifying exceptions, you do not need to use typeof(ArgumentException), just the type is enough. 

Happy (unit) testing!

# Monday, August 31, 2009

Misc. Adventures

Monday, August 31, 2009 7:45:16 PM UTC

Just some random photos to unload from the camera.

We recently saw this little guy scouring our Mums for prey:

Spent the weekend in Tennessee. Stopped by a BBQ/country music festival in Nashville. Got myself some Bayou Billy's homebrew soda. Love their tagline: "Put Some South In Your Mouth":

You're supposed to keep the mug for lifetime $1 refills. Sandra particularly liked how I carried mine around:

Spent Sunday at Cheekwood Botanical Gardens just outside of Nashville. Amazing place; a shame that I didn't have a better camera, lens, and -- well -- skill to capture it all.

The main mansion was beautiful as well, however, photography wasn't allowed.  The backstory is that it was once owned by one of the early investors in Maxwell House Coffee (The Cheek family).  There's a whole section of the garden, the sculpture trail, that we didn't do...maybe next time.  Also, there's supposed to be a Dale Chihuly exhibit next year if you're planning on making a trip to Tennessee!

If you're ever in Tennessee, don't pass up the chance to stop by Cheekwood.  Bring some comfortable hiking/walking shoes, a bottle of water, a nice camera, and some time.  Oh, and finally: where did this guy come from?

# Saturday, August 29, 2009

SharePoint Design Patterns: Entry 2

Saturday, August 29, 2009 4:58:15 PM UTC

In the previous entry, we explored how to clean up our interaction with instances of SPSite, SPWeb, and SPList objects.  Logically, the next scenario that we'd want to cover is working with the SPListItem that we get back from the list.

What does this look like?  Usually something like this (we'll use some sample code from our last entry):

public string GetLastModifiedBy(int id) {
    string lastModifiedBy;

    using(ProposalsLibrary library = new ProposalsLibrary()) {
        SPListItem item = ...; // Get the instance

        lastModifiedBy = Convert.ToString(item["Modified_x0020_By"]);
    }

    return lastModifiedBy;
}

By itself, this seems innocent enough, but the problem lies in the fact that it's rarely so easy as returning simply one field value.  Even if that were the case, the issue remains that these field name strings leak out across the codebase.  Again, this is typically mitigated through the use of Constants or configuration settings, but it's still messy code, IMO.  Aside from that, it can be difficult to figure out which fields are valid for which lists.  In general, it makes it hard for developers to figure out how to work with the existing code without good documentation.

Enter the Decorator pattern.  The intent of Decorator, as described in Design Patterns, is as follows:

Attach additional responsibilities to an object dynamically.  Decorators provide a flexible alternative to sub-classing for extending functionality.

Basically, what we'd like to do is to make working with SPListItems more intuitive and more domain specific.  Since we can't subclass SPListItem (and even if we could I don't think it would make sense to do so since it would also expose all of the properties and operations on the SPListItem), we'll have to leverage the simplified version of the Decorator pattern to help us instead.  Here is an example:

using System;
using Microsoft.SharePoint;

namespace SharePointExample
{
    /// <summary>
    /// Models a sales proposal.
    /// </summary>
    public class Proposal
    {
        private SPListItem _managedItem;
        private string _title;
        private string _lastModifiedBy;
        private bool _checkedOut;
        private string _checkedOutUserLoginName;
        private string _checkedOutUserDisplayName;
        private string _status;
        private string _customerName;

        /// <summary>
        /// Gets the title.
        /// </summary>
        /// <value>The title.</value>
        public string Title
        {
            get { return _title; }
        }

        /// <summary>
        /// Gets the login name of the check out user.
        /// </summary>
        /// <remarks>
        /// Null or empty if not checked out.
        /// </remarks>
        /// <value>The login name of the checkout user.</value>
        public string CheckedOutUserLoginName
        {
            get { return _checkedOutUserLoginName; }
        }

        // Other properties omitted...

        /// <summary>
        /// Private constructor; use the <see cref="FromSPListItem"/>
        /// call to create an instance.
        /// </summary>
        private Proposal() { }

        /// <summary>
        /// Creates an instance from a SharePoint list item.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <returns>An instance of <c>Proposal</c>.</returns>
        public static Proposal FromSPListItem(SPListItem item)
        {
            Proposal proposal = new Proposal();

            proposal._title = Convert.ToString(item["Title"]);
            proposal._checkedOut = (item["CheckoutUser"] != null
                && item.File.CheckOutStatus != SPFile.SPCheckOutStatus.None);

            if (proposal._checkedOut) {
                string fieldValue = Convert.ToString(item["CheckoutUser"]);

                // NOTE: item.Fields requires the display name.
                SPFieldUserValue checkoutUser = (SPFieldUserValue)
                    item.Fields["Checked Out To"].GetFieldValue(fieldValue);

                proposal._checkedOutUserLoginName = checkoutUser.User.LoginName;
                proposal._checkedOutUserDisplayName = checkoutUser.User.Name;
            }

            // Set other fields here...

            proposal._managedItem = item;

            return proposal;
        }
    }
}

If nothing else, we now have a clean, domain specific way to access the SharePoint list item.  Team members and new developers don't have to guess which fields are valid for this items retrieved from this list; it would be hidden from them by the properties on the Proposal class instead.  If you look at the code to determine the check-out user, you can see that we now also have a single location to encapsulate this parsing logic and any associated error handling we may want to add; other developers don't need to duplicate this code when they use the list item.

It's debatable whether you should use a constructor to initialize the instance, some sort of implicit conversion operation, or a static method like I've used here.  I would rule out an implicit conversion operator since it may be hard for users to understand at first blush (i.e. XName and string).  As for a "natural" constructor that takes the SPListItem instance?  While it's slightly more discoverable (XDocument.Parse() is pretty hard to find for new users), I feel like that's a bit misleading and the intent isn't clear, however, YMMV.  Another possible improvement is to pull the abstraction up yet another level since many of the fields are common (like title and last modified by); this exercise is left to the reader ;-).

If we go back to our first example, we can now write this as:

public string GetLastModifiedBy(int id) {
    string lastModifiedBy;

    using(ProposalsLibrary library = new ProposalsLibrary()) {
        SPListItem item = ...; // Get the instance

        lastModifiedBy = Proposal.FromSPListItem(item).LastModifiedBy;
    }

    return lastModifiedBy;
}

That's pretty cool.  While this example is intentionally simple, you now have a nice object oriented way of working the the fields.  In addition, you have a very logical place to put all of your domain specific operations on specific list item types (or should that be content types?).  For example, it may make perfect sense to add a method here called CompressAndEmail(string recipientEmailAddress) which would compress the file contents of the proposal and email it to the specified recipient.

Admittedly, this still isn't ideal since 1) we still have to deal with SPListItem on some level ("leakage", if you will) and 2) what about the other side of this equation: updating the item?  With regards to (1), we can simply hide this by adding a method to the ProposalsLibrary class as discussed in the previous entry:

using Microsoft.SharePoint;

namespace SharePointExample
{
    /// <summary>
    /// Concrete implementation of <see cref="Library"/>
    /// </summary>
    public class ProposalsLibrary : Library
    {
        protected override string ListName
        {
            get { return "<listNameHere>"; }
        }

        protected override string WebName
        {
            get { return "<webNameHere>"; }
        }

        /// <summary>
        /// Finds and instance of <see cref="Proposal"/> the by ID.
        /// </summary>
        /// <param name="id">The ID.</param>
        /// <returns>An instance of <see cref="Proposal"/>.</returns>
        public Proposal FindById(int id)
        {
            SPListItem item = ...; // Find the list item

            return Proposal.FromSPListItem(item);
        }
    }
}

Now our method looks like this instead:

public string GetLastModifiedBy(int id) {
    string lastModifiedBy;

    using(ProposalsLibrary library = new ProposalsLibrary()) {
        Proposal proposal = library.FindById(id);

       lastModifiedBy = proposal.LastModifiedBy;
    }

    return lastModifiedBy;
}

That's pretty awesome since it means that we can now remove all references to and knowledge of SharePoint from a whole layer of our application code. 

Now onto point (2).  For a moment, let's say our proposals have properties called "Status" and "CustomerName".  From time to time, we may want to update these (from a source other than the web form, like through a custom web service call or if we have a batch process that runs on our SharePoint environment).  One way we can handle this is by implementing the set operation on the properties which can be updated and a Save() method:

using System;
using Microsoft.SharePoint;

namespace SharePointExample
{
    /// <summary>
    /// Models a sales proposal.
    /// </summary>
    public class Proposal
    {
        private SPListItem _managedItem;
        private string _title;
        private string _lastModifiedBy;
        private bool _checkedOut;
        private string _checkedOutUserLoginName;
        private string _checkedOutUserDisplayName;
        private string _status;
        private string _customerName;

        /// <summary>
        /// Gets or sets the status.
        /// </summary>
        /// <value>The status.</value>
        public string Status
        {
            get { return _status; }
            set { _status = value; }
        }

        /// <summary>
        /// Gets or sets the name of the customer.
        /// </summary>
        /// <value>The name of the customer.</value>
        public string CustomerName
        {
            get { return _customerName; }
            set { _customerName = value; }
        }

        // Other properties omitted...

        /// <summary>
        /// Private constructor; use the <see cref="FromSPListItem"/>
        /// call to create an instance.
        /// </summary>
        private Proposal() { }

        /// <summary>
        /// Saves this instance back to SharePoint.
        /// </summary>
        public void Save()
        {
            // Error checking first; i.e check if user has item checked out

            // Save
            _managedItem["Customer_x0020_Name"] = CustomerName;
            _managedItem["Status"] = Status;
            _managedItem.SystemUpdate(false);
        }

        /// <summary>
        /// Creates an instance from a SharePoint list item.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <returns>An instance of <c>Proposal</c>.</returns>
        public static Proposal FromSPListItem(SPListItem item)
        {
            // Same as before; omitted
        }
    }
}

Nice.  In doing this, we've basically hidden most knowledge of the SharePoint list item from the users of our object and framework.  Downstream developers only need to know about the domain specific objects.  It's now far easier for a member of our team or a new developer to reuse this code and it cuts down on error prone duplication and leakage of field names across our codebase.  We also now have a nice place to put field validation logic (for example, in the Save() method).  If we wanted to, we can also add logic here (through some references to services or DAOs or something) to load additional metadata from external systems like databases.

If you want to get fancier, you could also add a dictionary keyed by string (internal field name) and with value type object and use that to hold your "set" properties until Save() is called.  In the save, you'd simply iterate the keys and set the values then update, only changing the values that were actually set by the caller.

Updating the status of a proposal would go from looking like this:

public void UpdateStatus(int id, string status) {
    using(SPSite site = new SPSite(_siteUrl)) {
        using(SPWeb web = site.OpenWeb(_web)) {
            SPList list = web.Lists[_list];

            SPListItem item = ...; // Get the list item
            
            // Perhaps add error checking.

            item["Status"] = status;

            item.SystemUpdate();
        }
    }
}

To this instead:

public void UpdateStatus(int id, string status) {
    using(ProposalsLibrary library = new ProposalsLibrary()) {
        Proposal proposal = library.FindById(id);

        proposal.Status = status;
        proposal.Save();
    }
}

It becomes even more compelling once we consider that in a real world implementation of an UpdateStatus() operation, it would probably involve checking to see if a user has the object checked out in the first place!  This would make our first example explode into a giant mess of code while in our second one, it would be one line (or integrated into the Save() method which could throw a custom NotCheckedOutException or framework InvalidOperationException or try to check it out automatically).

All this with relatively little work involved.  It's better in every way: less nesting of code, much more readable and natural, less error prone, better discoverability, and more domain specific (and less SharePoint-centric).  In the end, I think it dramatically improves usability and reuse of your SharePoint application code.  It also leads to a nice, logical place to encapsulate much of the common, repetitive, and error prone code that would otherwise be littered among your application (or worse, UI) code.

In future installments, we'll examine how flesh out the FindById() method on the ProposalsLibrary.  We'll also examine a GUI pattern to help promote reuse, improve tesatability, and cut down on duplication of business logic.

Design Patterns For SharePoint : Entry 1

Saturday, August 29, 2009 2:02:35 AM UTC

One thing that I've discovered is that it's easy to write sloppy, hard to read code when SharePoint is involved.  A lot of it may be due to the quick-and-dirty code samples out on the 'Net and a general lack of thought put into the structural details of the examples on MSDN (I can understand why as it can make example code much longer to write it well).  I have a whole series of blog post ideas on how to mitigate this (based on my own real world experiences) that I've been meaning to put together that kind of put together a big picture of how to make your team's SharePoint development experience less crappy.

While I have some general workflow related tips and tricks, I'll start off this series with a code based sample.  Something small that I think will make a big impact in cleaning up code.

One of the most common things that developers need to do when working with SharePoint is to create an instance of SPSite, open an instance of SPWeb and then get a list (or do something else against the web).

Typically, this takes on the following pattern:

private static readonly string  _siteUrl;
private static readonly string  _web;
private static readonly string  _library;
private static readonly string  _otherLibrary;
static MySharePointDAO() {
    _siteUrl = ...; // Hard coded, from a Constants class, or from config.
    _web = ...;
    _library = ...;
    _otherLibrary = ...;
}

public void DoSomething() {
    using(SPSite site = new SPSite(_siteUrl)) {
        using(SPWeb web = site.OpenWeb(_web)) {
            SPList list = web.Lists[_library];

            // Code goes here.            
        }
    }
}
public void DoSomethingElse() {
    using(SPSite site = new SPSite(_siteUrl)) {
        using(SPWeb web = site.OpenWeb(_web)) {
            SPList list = web.Lists[_otherLibrary];

            // Code goes here.            
        }
    }
}

There are many different variations of this basic pattern (in the worst case, this code is written right into layout pages or webpart code which I imagine to be quite a common practice...).  Sometimes, the strings are hard coded inline, sometimes the strings are from a Constants class, sometimes the strings are read from a configuration file.  No matter how it's done, it tends to muck up the code and it ends up all over the place.  One of my beefs with this is that it's not very aesthetically pleasing; deep nesting does that (plus, depending on how deep the rest of the code nests, it may start to scroll horizontally...yuck!).  You can also stack the using statements (instead of nesting), but that still leaves it a mess (my other beef) since you need to specify the site URL, the web, and potentially a list - it creates a lot of useless, duplicated code all over your codebase.

Yet there is a very simple solution: abstract this logic into a class that implements IDisposable:

using System;
using Microsoft.SharePoint;

namespace SharePointExample
{
    /// <summary>
    /// Abstraction for a SharePoint list.
    /// </summary>
    public abstract class Library : IDisposable
    {
        private static readonly string _siteUrl;

        private readonly SPSite _site;
        private readonly SPWeb _web;
        private SPList _list;

        /// <summary>
        /// Static initializer that sets the site URL.
        /// </summary>
        static Library()
        {
            _siteUrl = ...; // Hard coded, from config, or Constants
        }

        /// <summary>
        /// Default constructor that creates the site and web.
        /// </summary>
        protected Library()
        {
            _site = new SPSite(_siteUrl);
            _web = _site.OpenWeb(WebName);
            _list = _web.GetList(ListName);
        }

        /// <summary>
        /// Gets the name of the list.  Inheriting classes
        /// implement this property for a specific list.
        /// </summary>
        /// <value>The name of the list.</value>
        protected abstract string ListName { get; }

        /// <summary>
        /// Gets the name of the web.
        /// </summary>
        /// <value>The name of the web.</value>
        protected abstract string WebName { get; }

        #region IDisposable Members

        /// <summary>
        /// Performs application-defined tasks associated with freeing, 
        /// releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        #endregion

        /// <summary>
        /// Releases unmanaged and - optionally - managed resources
        /// </summary>
        /// <remarks>
        /// See Cwalina, Abrams; Framework Design Guidelines, 1st Ed., p. 251
        /// </remarks>
        /// <param name="disposing"><c>true</c> to release both managed and 
        /// unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (_web != null) _web.Dispose();
                if (_site != null) _site.Dispose();
            }
        }
    }
}

(It's generally not advised to call abstract/virtual members in a constructor, but in this case, they should simply return strings -- should be safe.)

And that's it; so simple.  What do our calls look like now?  From our presenters/controllers/views, we have something like this instead:

using(ConcreteLibrary library = new ConcreteLibrary()) {
	// Do stuff here with the library.
}

If we only consider this as a win, then it's a very small win.  But in the bigger picture, our downstream callers don't need to know about the SPSite, SPWeb, or the SPList if we take the abstraction far enough -- we'll examine this in future installments.  Furthermore, we now have a convenient place to put common list based operations and if our business requirements dictate that there are specific lists for specific types of documents, this abstraction becomes even more useful.

I've intentionally left the "Do stuff here..." empty.  In the simplest case, at this point, you can simply expose the list, web, and/or site as public properties to inheriting/calling classes and just access library.List or library.Web and use it as you normally would.  If nothing else, you've cleaned up the nesting and the leakage of the site URL, web names, and list names.  (To make this abstraction more useful, IMO, we would have you hide all of our operations in the ConcreteLibrary and expose the site, web, and list as protected properties to the inheriting classes only.)

This design assumes that you have specific, well known libraries in your SharePoint deployoment in which case it makes sense to create library specific methods and a concrete library sub-type for each library.  For example, if a library holds proposals that your sales department has written, you may want to have a class called ProposalsLibrary with a method called FindPendingProposals()which is specific to that library.  Of course, common functionality across all lists can be easily added to the base abstract Library class (and this usually involves a "find" operation of some sort).

In general, it's a very small, easy, and intuitive change that makes the code that implements your business logic (as it pertains to SharePoint) much easier to read and to maintain.  As we'll explore in future entries in this series, if we take the abstraction far enough, we can completely eliminate the Microsoft.SharePoint namespace from our business/presentation logic.

See also: SharePoint Design Patterns: Entry 2

RSS 2.0 Atom 1.0 CDF