Development Blog

 Friday, June 01, 2007

OK, let me start out by saying that I'm hardly an expert on the subject of ORMs. I've only really used NHibernate, and I've still got plenty to learn about it. That said, I do know a bit about being lazy...

When working in any real application you quickly realize that actually exercising lazy loading is something that should probably be avoided at all costs. You want to fetch everything you need to handle a request in one go. Scott Bellware recently struggled with this issue as we all have, and the general consensus in the comments is that you should load what you need and not lazy load anything (unless of course that lazy load happens in a branch less often traveled). Udi Dahan provided a link to a rather elegant solution to the problem that I think I'm definitely going to explore.

Even with Udi's solution, you're still required to keep the fetching strategy in sync with the domain. This isn't an incredibly difficult thing to do, but it requires discipline and effort. I've been thinking for a while about this and a few other possible performance tweaks that can make ORMs easier to use and even a bit quicker than they are today.

Let's talk about another performance issue briefly. Say you want to Display an email list containing all of your users. The list should display just their username and email address, nothing more. The easy way to do fetch the data would be to do a "from UserEntity". But is that the most efficient? What if UserEntity has 10 fields? 20? 30? Is it worth explicitly querying for "u.Username u.Email from UserEntity u"? Let's find out. Being lazy, I simply borrowed James Avery's Northwind NHibernate example, ported it to NHibernate 1.2 and added a new mapping and class called TrimmedOrder. The class is exactly the same, but the mapping only contains the Freight and ShipName properties. I also commented out the many-to-ones in both Order and TrimmedOrder. You can get the source here.

The rest was just a few simple tests. One for reading, and one for writing. Here are the results:

  Order TrimmedOrder Performance Gain
Read Test 18.62 ms 11.66 ms 1.6 times faster
Write Test 227.13 ms 161.24 ms 1.41 times faster

Notes: The Read Test was averaged over 50 iterations and the Write test was averaged over 20 iterations. The db was queried for all orders once before anything was tested because I found the first query was always slower, but after that all queries seemed level. In the writing test, I wrote random values to the two properties mapped by TrimmedOrder. The Write Test values were adjusted by subtracting the read test values so that only the write itself was timed (in theory). 

Now, these gains aren't anything to write home about, but they are significant. Some of you may wonder why the Write Test sees any gain given that the database calls would be exactly the same, as NHibernate doesn't update columns that didn't change. Well, without profiling, I'm willing to bet that the reason is simply that NHibernate has discover which fields changed while flushing by testing them against their old values.

So now we have two problems: we have to know exactly what collections the domain needs ahead of time or we lazy load and unless we use projection queries, we're not getting the best performance we can get. Also, NHibernate doesn't currently support deferred (lazy) property loading, so projection queries return object[]'s rather than partially initialized entities, which means that as far as I know, you cannot use projection queries to do updates, so we just can't solve the Write problem without additional mappings or something else hackish.

What do we do? Yes, I'm finally nearing the point of this post. What if we taught the ORM to learn? Bear with me here. Let's go back to the email list example. If we were using Udi's Fetching Strategy approach, we'd do something like this:

IList<UserEntity> users = Repository<IEmailListService>.FindAll();

Which would ultimately get a FetchingStrategy associated with this service. Now what if every object fetched by this FetchingStrategy was proxied and would communicate back to the FetchingStrategy about its usages... and the FetchingStrategy remembered how its objects were used, so the next time it got invoked it would only return what was used last time... and it would keep learning, and refining so that it would simply return all the fields and collections that were ever accessed via that particular FetchingStrategy. If ever it returned too little, it would just lazy load what needed to be loaded and make note that it should provide that field next time (of course this would be configurable so that it would just load everything on a miss so you're not hitting the db several times in these cases). This would mean the first time your FetchingStrategy was invoked it would be "slower" but most every time after that it will have adapted and improved... all for mostly free. And yes, I know that Udi's FetchingStrategy would just return an hql query and live closer to the domain objects, but mine would live closer to NHibernate and it would probably be responsible for actually querying from NHibernate (so it's probably really a Dao, but you get my point.)

There are probably a few other things this could help with too, such as marking queries as readonly automatically so as to avoid an unnecessary and costly flush (unfortunately this is not currently supported any ways).

Caveats? Plenty I'm sure. Here's some I can think of:

  • There's the fact that it's not implemented. Jacob and I started to work on it and quickly decided it would be best to get lazy loaded properties hashed out first. It's probably a decent ways off the NHibernate radar, but maybe we can change that.
  • Then there's the proxy element. The Entities are often proxied normally to lazy load collections and such, so I don't think adding in reporting back to the FetchingStrategy would be a huge burden.
  • Complexity... yeah, this is complicated, but so are ORMs and software :)
  • Increased startup cost (kind of). You could always persist the strategies to help alleviate this a little...
  • Instance variables, access strategies, etc. Much like lazy loaded collections, restrictions would apply to lazy loaded properties and such. You'd probably need to just always load things that are field accessed, and you'd certainly have to avoid accessing the instance variables that back deferred properties.
  • Objects fetched by multiple strategies. This is a big one. There will be tons of questions when it comes time to solve this problem. If I access property A after an object is fetched from two different strategies, do I notify both strategies? Will data be fetched twice if both strategies need it?
  • Caching complications. Most of these would come from the lazy loaded properties, so that's something that'd have to be tackled eventually.

Now I'm just waiting for Gavin King or Sergey Koshcheyev to come tell me why this is a horrible idea, but until then, what do you all think?

by Aaron on Friday, June 01, 2007 9:12:39 AM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [5]  |  Trackback
 Wednesday, April 11, 2007

We were on the phone today with Scott Bellware and he asked us if we could toss together a simple MonoRail project that showed the Castle.Tools.CodeGenerator stuff in action. A few other people have been asking us to do something similar for the other things we've been discussing on our blog. So, Aaron and I sat down and pair-programmed out some source that does just that. It shows several things:

  • An Action/View map is generated using Castle.Tools.CodeGenerator, and used to do view renderings, and (if desired) action redirections and the like.
  • PropertyBag wrappers are created using the IDictionaryAdapterFactory code that Lee contributed.
  • We show how we test our controllers using the test fixture code Aaron has been posting about.
  • It has a small service layer that roughly shows how we architecture our code around here.
  • It uses Windsor integration, so you can see how that's used if you've been curious.

Please keep in mind this took us about an hour to whip up and we did not actually practice full TDD with it. Enjoy!

Source

by Jacob on Wednesday, April 11, 2007 5:41:13 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [13]  |  Trackback
 Monday, April 09, 2007

We use a ton of Visual Studio plugins. CodeRush is one of them. Its templates are awesome, and its got a few nice refactorings that Resharper doesn't have. They're a little tricky to get working together, but not impossible. One thing I use a ton is GotoType under Resharper. One thing I find myself doing all the time is going between a class and it's fixture. Because I couldn't see anything else that would do it, I tossed together a CodeRush plugin for that very thing.

You just bind a key (I use Ctrl+Shift+X) to Eleutian Goto Fixture. All it does is do a regular expression search through type names in your solution. Say you're on HomeController. Because the class name doesn't end in "Tests" it will look for one that ends with "HomeControllerTests". If the class were HomeControllerTests, it would strip the suffix off and look for a class with a name that ends with "HomeController". It's missing a few things I still want. For example, for multiple matches I would like a menu, or the ability to cycle through them a la tab completion. I'm not sure of the best way to do the menu and haven't decided if I'll just do the cycling. If there's interest I'll think more about it.

There are a few optimizations, the first is that it will only look in projects ending with ".Tests" when looking for the test fixture. Also, it will keep a rolling queue of the last 10 types you jumped from and check those first. Both are handy when you have 40 projects and tons of classes. I started off with other loftier goals for the plugin, which might explain why there's so much other code.

In order to install this thing drop the binaries into your C:\Program Files\Developer Express Inc\DXCore for Visual Studio .NET\2.0\Bin\Plugins directory. Start Visual Studio and choose DevExpress | Options and then find the Shortcuts tab. In there, you can add a new shortcut (there's an icon in the lop left) and bind the keystroke you desire to the Eleutian Goto Fixture command.

It's my first CR plugin, so forgive me.

Source and Binaries

by Jacob on Monday, April 09, 2007 10:29:41 AM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Wednesday, March 28, 2007

Shortly after I posted my original EleutianControllerTests, Scott Bellware, Hamilton and myself chatted about the subject, and Hamilton decided to help out by making some of the previously internal methods public. Context still isn't settable directly however (maybe we should have him change that too...), so you still have to do a little bit of magic to get your context in there. There's actually more code to do this now then when I just set it w/ reflection, but it's arguably more "correct" and doesn't involve "reflection" and you can mock more (like logging and such). Anyways, here's the code:

 

using System;
using System.Collections;
using System.Collections.Specialized;
using System.IO;
using System.ComponentModel.Design;
using System.Security.Principal;
using System.Web;

using Castle.MonoRail.Framework;
using Castle.MonoRail.Framework.Internal;
using Castle.Core.Logging;
using Castle.MonoRail.Framework.Services;

using NUnit.Framework;

using Rhino.Mocks;

public class EleutianControllerTests
{
  #region Constants
  private const string ApplicationPhysicalPath = "Q:\\PhysicalPath";
  #endregion

  #region Member Data
  protected string _virtualDirectory = String.Empty;
  protected MockRepository _mocks;
  protected IRailsEngineContext _context;
  protected IRequest _request;
  protected IResponse _response;
  protected IServerUtility _serverUtility;
  protected IDictionary _session;
  protected Flash _flash;
  protected IViewEngineManager _viewEngineManager;
  protected ControllerMetaDescriptor _descriptor;
  protected NameValueCollection _parameters;
  #endregion

  #region Test Setup and Teardown Methods
  [SetUp]
  public virtual void Setup()
  {
    _mocks = new MockRepository();
    _viewEngineManager = _mocks.CreateMock<IViewEngineManager>();
    _descriptor = _mocks.CreateMock<ControllerMetaDescriptor>();
    _context = _mocks.CreateMock<MockRailsEngineContext>(_viewEngineManager, _descriptor);
    _request = _mocks.DynamicMock<IRequest>();
    _response = _mocks.DynamicMock<IResponse>();
    _serverUtility = _mocks.DynamicMock<IServerUtility>();
    _session = _mocks.DynamicMock<IDictionary>();
    _flash = new Flash();
    _parameters = new NameValueCollection();
  }

  protected void InitializeController(Controller controller, string areaName, string controllerName, string actionName)
  {
    controller.InitializeControllerState(areaName, controllerName, actionName);
    controller.InitializeFieldsFromServiceProvider(_context);

    InitializeRailsContext(areaName, controllerName, actionName);
  }

  protected void InitializeRailsContext(string areaName, string controllerName, string actionName)
  {
    SetupResult.For(_context.UrlInfo).Return(
      new UrlInfo("eleutian.com", "www", _virtualDirectory, "http", 80,
                  Path.Combine(Path.Combine(areaName, controllerName), actionName), areaName, controllerName,
                  actionName, "rails"));
    SetupResult.For(_context.Server).Return(_serverUtility);
    SetupResult.For(_context.Flash).Return(_flash);
    SetupResult.For(_context.ApplicationPath).Return("/");
    SetupResult.For(_context.Request).Return(_request);
    SetupResult.For(_context.Response).Return(_response);
    SetupResult.For(_context.ApplicationPhysicalPath).Return(ApplicationPhysicalPath);
    SetupResult.For(_request.Params).Return(_parameters);
    SetupResult.For(_context.Session).Return(_session);
  }
  #endregion
}

public abstract class MockRailsEngineContext : IRailsEngineContext
{
  #region Member Data
  private IViewEngineManager _viewEngineManager;
  private IControllerDescriptorProvider _controllerDescriptorProvider;
  #endregion

  #region Properties
  public abstract void Transfer(string path, bool preserveForm);
  public abstract string RequestType { get; }
  public abstract string Url { get; }
  public abstract string UrlReferrer { get; }
  public abstract HttpContext UnderlyingContext { get; }
  public abstract NameValueCollection Params { get; }
  public abstract IDictionary Session { get; }
  public abstract IRequest Request { get; }
  public abstract IResponse Response { get; }
  public abstract ITrace Trace { get; }
  public abstract ICacheProvider Cache { get; }
  public abstract Flash Flash { get; }
  public abstract IPrincipal CurrentUser { get; set; }
  public abstract Exception LastException { get; set; }
  public abstract string ApplicationPath { get; }
  public abstract string ApplicationPhysicalPath { get; }
  public abstract UrlInfo UrlInfo { get; }
  public abstract IServerUtility Server { get; }
  public abstract IDictionary Items { get; }
  public abstract Controller CurrentController { get; set; }
  #endregion


  #region Constructors
  public MockRailsEngineContext(IViewEngineManager viewEngineManager, ControllerMetaDescriptor descriptor)
  {
    _viewEngineManager = viewEngineManager;
    _controllerDescriptorProvider = new ControllerDescriptorProviderStub(descriptor);
  }
  #endregion

  #region Methods
  public abstract void AddService(Type serviceType, object serviceInstance);
  public abstract void AddService(Type serviceType, object serviceInstance, bool promote);
  public abstract void AddService(Type serviceType, ServiceCreatorCallback callback);
  public abstract void AddService(Type serviceType, ServiceCreatorCallback callback, bool promote);
  public abstract void RemoveService(Type serviceType);
  public abstract void RemoveService(Type serviceType, bool promote);
  public object GetService(Type serviceType)
  {
    if (typeof(IViewEngineManager).Equals(serviceType))
    {
      return _viewEngineManager;
    }
    else if (typeof(IControllerDescriptorProvider).Equals(serviceType))
    {
      return _controllerDescriptorProvider;
    }
    else if (typeof(ILoggerFactory).Equals(serviceType))
    {
      return null;
    }
    else if (typeof(IUrlBuilder).Equals(serviceType))
    {
      return new DefaultUrlBuilder();
    }

    return null;
  }
  #endregion

  #region Classes
  private class ControllerDescriptorProviderStub : IControllerDescriptorProvider
  {
    private ControllerMetaDescriptor _descriptor;

    public ControllerDescriptorProviderStub(ControllerMetaDescriptor descriptor)
    {
      _descriptor = descriptor;
    }

    public ControllerMetaDescriptor BuildDescriptor(Controller controller)
    {
      return _descriptor;
    }

    public ControllerMetaDescriptor BuildDescriptor(Type controllerType)
    {
      return _descriptor;
    }

    public void Service(IServiceProvider provider)
    {
    }
  }
  #endregion
}

Source
by Aaron on Wednesday, March 28, 2007 2:51:30 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [6]  |  Trackback

First, a small introduction...

I'm glad to see Ruby has gotten the respect it deserves. I've always felt it was a far prettier language than Python or Perl. While it's still prone to some of the line noise problems of Perl, it's definitely not as bad. I need to say that it's been several years before I've done any serious work with an interpreted language. When I worked at UCR we were almost entirely a PHP/Python/Ruby outfit. In that order, unfortunately. About four and half years ago I did a technical seminar on Ruby. There was a huge push in the department to go to Python and I felt compelled to be the voice of reason. Since my time at UCR I've neglected scripting languages. I feel that one has to be extremely careful when using a scripting language to make a large, enterprise application. I can't stress that enough, careful. I'm sure it's possible, maybe. So yeah, I'm skeptical. But I also encourage people to use the best language for them and the project.

Lately I've been hearing more and more about Ruby and I've been wondering about its applications in the context of someone doing large scale .NET development. My Journey Through The Languages took me through Java before C#. Scenarios I pictured kept reminding me of the Cocoon project and their Flow language. They employ an embedded javascript engine to allow developers to "glue" the backend and the view together. It's an interesting idea. You create your backend, the service layer for the heavy lifting and all that. Then, you let another developer create the features and build the UI using this scripting language. Via their use of continuations (see sendPageAndWait it's neat) they made a lot of the server side programming for the web feel very natural. Oddly enough, the project we used it for moved towards Tapestry. Cocoon is primarily for XML processing, with some other stuff kind of tacked on, that's another blog though.

So, to the point. I started looking back at Ruby and playing with this. I mostly focused on the .NET related stuff in the Ruby community. I looked at a few, but one that really caught my eye was RubyCLR (and this one). So, RubyCLR is a bridge. It's primary goal is to allow calling .NET from Ruby. You can create .NET objects and call methods, properties etc... What it does is generate DynamicMethods (IL at runtime) and return .NET delegates as the Ruby functions. Obviously, there's more to it. It's a pretty elegant solution and very slick. I like stuff that's slick. So, after the weekend, here's what I have:

First we define our MonoRail Controller, it looks like this:

using System;
using MonoRuby.Extension;
    
namespace MonoRuby.Site.Controllers
{
  public class HomeController : MonoRubyController
  {
  }
}

Next, the ruby controller:

class HomeController < Controller
  def index()
    @name = "Jacob Lewallen"
    @today = System::DateTime.Now.ToString()
    render_view 'Index'
  end
end

We can then make our NVelocity (or Brail) view:

<p>
  Hello, $Name! It's $Today...
</p>

Compile, point IIS at it and away you go. Right now it's a very rudimentary implementation. It's got a few quirks, mostly with reloading the web application and how RubyCLR manages its cache of shadow classes and type maps. I did have to dig a little deeper than I'd hoped:

  1. Because RubyCLR is for the opposite "direction", calling .NET from Ruby. I'm embedding Ruby and had to add some stuff that RubyCLR was missing. Mostly bootstrapping code and that sort of thing for starting the Ruby interpreter.
  2. I developed against the RubyCLR trunk because I'm bleeding edge. So, I had to compile RubyCLR and Ruby myself. For some reason, it appeared the Ruby I built used a different calling convention than RubyCLR was developed against. Discovering this ate up most of my time. I attributed the delegate used to pass Ruby managed function pointers and all my weird segfaults went away. I chose this over a /Gz when compiling Ruby.

So here's what's happening:

Application Startup

  1. MyHttpApplication creates a very simple container to store the services and starts them.
  2. DefaultRubyService initializes the RUBYLIB environment variable and then initializes ruby.
  3. DefaultMonoRubyService loads the Ruby sources that we'll be delegating to later (bootstrap.rb, monoruby.rb, and engine.rb)
    1. Ruby sources declare some classes and such we'll be needing and setup IO redirection so I can print stuff to OutputDebugString from Ruby.
    2. We create our MonoRubyGateway (a Ruby implementation of the .NET interface IMonoRubyGateway)
    3. We pass that gateway reference back to the container, so the .NET code can talk to us.
  4. We're done initializing.

Request Handling

  1. [C#]
  2. It all begins when MonoRail calls our MonoRubyController SelectMethod override. Usually SelectMethod determines and returns the reflected MethodInfo for our action. Instead, we save the action name and return the same MethodInfo every time.
  3. MonoRail then calls our InvokeMethod override with the MethodInfo from earlier, which we ignore. We then ask the DefaultMonoRubyService to dispatch the action, giving it a reference to our controller, the action name, and the action arguments.
  4. DefaultMonoRubyService then asks the IRubyService to run a block of code "protected". All this means is that Ruby exceptions won't kill the application. IRubyService will turn them into RubyExceptions and pass them up along with a Ruby stacktrace.
  5. Our MonoRubyGateway from earlier is called, passing the baton to Ruby.
  6. [Ruby] - Lots of RubyCLR Hand Waving
  7. Build the path to the ruby controller sources (home_controller.rb) and load them.
  8. Create a new ActionDispatcher and call it.
  9. We get the class for our controller, create a new instance, and then initialize it:
    1. To prepare, we set the controller reference and other stuff as instance variables.
    2. Action arguments become @params, etc...
  10. We get the method for the action, and call it.
  11. Methods on Ruby's Controller base class call into the C# Controller. (It's actually an adapter, we're passing IController references around, and ControllerAdapter wraps MonoRail's Controller)
    1. render_view does a @controller.render_view
    2. render_text does a @controller.render_text
    3. We can access the PropertyBag via @controller.PropertyBag, which we do in the next step.
  12. Afterwards, we gather up all of our instance variables, and toss them in the property bag.

All done! Request served. I'm curious to know what people think. Obviously, this code is experimental... in fact, use a VM :)  If while playing around you get errors about duplicate types and such from RubyCLR, do an iisreset.

In hindsight, it wasn't necessary to use RubyCLR. When it comes to interoperability, that was the best bet. You can easily do this kind of thing with one of the Ruby .NET based interpreters once their interoperability support is further along. It would certainly be cleaner. I just didn't find one I liked. This just felt very appropriate. Also, I'm sure it's faster. Performance has always been a concern of mine when it came to using Ruby, but there seems to be some work in that area. I also didn't need to use MonoRail, but it made things much more straightforward. This was a side project. I'll probably play around a little more. I'm mostly concerned about what the community thinks. Thumbs up? Down?

Enjoy.... It's been a week since I looked at this source, so if anybody has any trouble building they can IM me or email me.

Source and Binaries

by Jacob on Wednesday, March 28, 2007 9:46:41 AM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [4]  |  Trackback
 Tuesday, March 27, 2007
using NUnit.Framework;

[TestFixture]
public class OperatorExplicitTests
{
  public interface IFoo { }
  public sealed class Foo : IFoo { }

  public class Bar : IFoo
  {
    private Foo _foo = new Foo();

    public static explicit operator Foo(Bar bar)
    {
      return bar._foo;
    }
  }

  [Test] // Passes
  public void TestCastFromClass()
  {
    Bar bar = new Bar();
    Foo foo = (Foo)bar;
  }

  [Test] // Passes
  public void TestCastFromInterfaceToNormal()
  {
    IFoo bar = new Bar();
    Foo foo = (Foo)(Bar)bar;
  }

  [Test] // Throws System.InvalidCastException: Unable to cast object of type 'Bar' to type 'Foo'.
  public void TestCastFromInterface()
  {
    IFoo bar = new Bar();
    Foo foo = (Foo)bar;
  }

}

Ugh, see what happens when you try to be sneaky? Is this a bug? A limitation?

If you're curious why I care, we have a custom implentation of IDbCommand that wraps a SqlCommand and this code expects it to be a SqlCommand... no fooling it I guess :/

by Aaron on Tuesday, March 27, 2007 11:15:48 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [1]  |  Trackback

Update: Hamilton committed my patch, so if you're running off trunk you won't need to do this anymore. Thanks Hamilton!

Currently MonoRail uses ResourceSets wrapped up in an implementation of IResource to provide Resources to views. Unfortunately, ResourceSets do not cascade when it comes to resource resolution.  ResourceManager.GetResourceSet does have a flag called tryParents, but all that does is try less and less specific cultures until it finds a match. In other words, if you have two resource files:
Foo.ko.resx: defines A, B
Foo.resx: defines A, B, C

If you locale is ko-KR, calling RseourceManager.GetResourceSet will yield a ResourceSet that maps to Foo.ko.resx, so asking for B will work, but asking for C will not. A ResourceSet only knows about itself.

ResourceManagers on the other hand, are perfectly capable of handling this cascade, which is quite necessary in at least our localized app, as we do not want to have to define strings in all languages for everything. So with ResourceManagers, asking for A, B, and C all behave as expected, preferring the most specified culture and cascading down as necessary.

Below is an implementation of an IResourceFactory that spits out wrapped ResourceManagers instead of ResourceSets.

To use it you'll need to add this to your web.config:

<monorail>
  <services>
    <service id="ResourceFactory" type="Eleutian.Shared.MonoRail.ResourceManagerFactory, Eleutian.Shared" />
  </services>
</monorail>

Source

by Aaron on Tuesday, March 27, 2007 4:13:12 AM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Saturday, March 24, 2007

If you're reading this, you're probably interested in some of the stuff we're doing, which means we're probably interested in you. We're currently looking for Software Developers and Testers in Seattle. If you'd like to join our team, drop us an email with your resume to jobs@eleutian.com.

by Aaron on Saturday, March 24, 2007 4:11:15 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  |  Trackback

I'm sure you've all heard that the best developers are lazy and/or dumb. I certainly agree with that. Also, you know that writing repetitive code and repeatedly following tedious steps is bad and a waste of time.

OK, so we know that those things are bad... but how do you identify them? Often tedious steps and repetitive code is taught as the way you do things... so you just do them in that way without asking questions. Other times, we just don't know that there is an easier way. We're developers--we're creative, smart, inventive, and we should be able to tell when something can and should be done in a better way. 

Let me give you an example of some code I came across some time ago:

Public userstring As String = "U000000"

Private Function GetUserIDStr(ByVal userid As Integer) As String
  'Returns String:
  '   User Info FileName
  Dim source As String = userid.ToString()
  'Dim destination As Char() = {"U"c, "0"c, "0"c, "0"c, "0"c, "0"c, "0"c}
  Dim destination As String = Me.userstring
  Dim sourcelen As Integer = source.Length()
  Dim destindex As Integer = 7 - sourcelen
  source.CopyTo(0, destination, destindex, sourcelen)
  Return destination
End Function

Obviously there are several things... off... with this code. Let's discuss some of them. The first is the original Dim destination that is now commented out. This is an example of someone partially applying what I'm talking about. They realized that typing out that array was unnecessarily complicated so they did something about it. That's great, that's what we need to do. Unfortunately, they stopped there. I don't think we should stop there. We should look at the code, say, wow, this isn't really doing anything but formatting a number into a string. There has to be a better way to do this! In this case there certainly is... all of that code can be replaced with something along the lines of:

Return String.Format('U{0:D06}', userid)

Which is easier to read? Write? Maintain? The answer is obvious... even if you didn't know about String.Format, you should know when you're writing that code that there has to be a better way. Google is your friend.

What if there isn't a better way? Can you think of a better way? Ask yourself these questions:

  • Are you going to be doing this more than a few times?
  • Are other people on your team going to be doing it?
  • Are you looking for a break from your normal work?

If the answer to any of those questions is yes, take some time to write or come up with a better way. You're a developer. You develop. Remember, you can write software for yourself and your teams that will make writing software more enjoyable and faster. And then, when you're done, share it! Share it with the world, make development easier for everyone.

I'll give you another example. I've already blogged about it, but it's relevant to this post so I'll rehash here. In order to localize a string in MonoRail you must do the following:

  1. Create a new resx file.
  2. Add a Resource attribute to your controller mapping the resx to a key in the PropertyBag.
  3. For each string you want to localize, add a key/value pair to the resx file.
  4. Replace the strings in your view template with your resource key.

Hm. Every controller? Every string? Talk about context switching. Not to mention the fact that you can't see the actual English string in your view template, so any modifications to the English string require the same context switching. It didn't take me long to decide that was far too tedious for myself and my team.

It probably took me 2-3 days overall to write both the ASP.NET preprocessor and adapt it to MonoRail/Brail. Will I ever get 2-3 whole days back? Who knows. Will my entire team in total? More likely. Does it save us countless context switches, speed up our development, and make localization trivial? Absolutely. So was it worth it? Absolutely.

And before you ask, yes I plan on taking my own advice and sharing it... eventually. Oh and keep in mind, this sort of thinking can and should be applied to everything you do, including but not limited to code, tools, and process.

by Aaron on Saturday, March 24, 2007 3:46:12 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, March 22, 2007

In a previous post I talked about how we use automatically generated interfaces to wrap our PropertyBag's. You can also use this technique in your ViewComponents to clean up that code quite a lot. We used to have code like this:

public class SomeComponent : ViewComponent
{
  public override void Initialize()
  {
    base.Initialize();
    User user = (User)this.ComponentParams["User"];
    float value = this.Service.CalculateThatValue(user);
    this.ComponentParams["SomeCalculatedValue"] = value;
  }
}

Now we can make an interface:

public interface ISomeComponentView
{
  User User { get; set; }
  float SomeCalculatedValue { get; set; }
}

And ask the code we wrote for the PropertyBag to give us a hand in cleaning things up:

public class SomeComponent : ViewComponent
{
  public override void Initialize()
  {
    base.Initialize();
    ISomeComponentView view = this.ViewFactory.ResolveView<ISomeComponentView>(this.ComponentParams);
    float value = this.Service.CalculateThatValue(view.User);
    view.SomeCalculatedValue = value;
  }
}

Much better! If you're looking for an implementation of that ViewFactory service, Lee Henson tossed one together shortly after our post. He's also added some other, pretty nitfy features since then. Like the ability to specify a prefix with an attribute for the generated dictionary keys. As he mentions in his post, you can use it for anything (and probably should) that uses strings as keys into an IDictionary (Session, Flash, etc..). I'm planning on checking it in to CastleContrib soon, until then, grab it off the list. Thanks Lee!

by Jacob on Thursday, March 22, 2007 9:21:27 AM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  |  Trackback