Development Blog

 Friday, November 02, 2007
by Aaron on Friday, November 02, 2007 2:31:28 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Monday, October 15, 2007

So everyone's been talking about the ALT.NET conference, Microsoft's take on MVC, and such, so I thought I'd briefly chime in with my thoughts. Photo 18 First of all, the ALT.NET conference rocked and I'm very much looking forward to the next one. Secondly, I must disclose that Scott Bellware paid me to say that ($1, signed) but I would have said it anyway.

OK, so then there's the whole MVC thing. This too was awesome and I should try and collect a signed dollar from Scott Guthrie. It's obvious he and the team working on MVC are doing their research. That said, as many of you know we're a MonoRail shop here. We've been using it heavily for about a year now and we've got a lot of code written on it. So what will the impending release of the MS MVC framework mean for us? What will it mean for MonoRail?

I can tell you that for us, it will very likely mean a week of exploration and quite possibly followed by a week or two of porting. That's right, it looked that good. Everything about it was done right or flexible enough to be made right so there really isn't any room to complain about it. MonoRail has more features at the moment, but the ones we use wouldn't be difficult to implement on top of MS MVC.

So what does this mean for MonoRail? I can't speak to that, but I will tell you what I would like for it to mean. What I'd like to see is MonoRail become more like Rails. I want to see something built on top of MS MVC that even more-so favors Convention over Configuration--including but not limited to generators and such. I want it to take it to the next level and be exactly what the community wants for a C# web platform. Dave Laribee says that "MonoRail will remain a viable option for smaller or more edgy shops." I think that's true, but I want to see it built on top of MS MVC.

Why throw away all of the framework code built into MonoRail? A few reasons:

  1. Castle has already been experimenting with throwing most of it away and starting over anyway.
  2. MS MVC will be built into the .NET Framework. This means  easier sells to big shops, and MonoRail would be just a free supplement rather than a replacement/paradigm shift, making it an easier sell.
  3. MS MVC appears to be more modular. There is no massive Controller or ginormous SmartDispatcherController. You can even start with a one method interface and implement your controller however you want.
  4. Routing.
  5. It was built with testability in mind. MonoRail is now mostly testable, but it is not nearly as clean as it could be... or as MS MVC's testability looks.
  6. We can probably all but throw away our codegenerator and use lambdas. We could reinvent this for MonoRail in .NET 3.5 but then we'd be... reinventing... it...

Whatever we decide to do when it comes out, we'll talk about our experiences so you can judge for yourself after we judge for ourselves how viable it will be to port from MonoRail to MS MVC.

by Aaron on Monday, October 15, 2007 10:59:24 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [10]  |  Trackback
 Saturday, September 29, 2007

First some code:

UserEntity user = New.User("bob").In(New.Company("ACME")).With(
  New.PurchaseOf("ProductA")
    .WithAcademicSlot(1).FilledBy(New.EnrollmentIn(101).ThatIsCanceled)
    .WithPlacementSlot(1).FilledBy(New.EnrollmentIn(1)));

This code actually creates and saves (with NHibernate) about 12 or so entities. We can use code like this to set up one off sample data for tests in a way that's easy to read, understand and change. There is a decent amount of magic that goes into making this work and I wanted to talk about how I did it.

First we have a FixtureContext, which is just a hub for the DaoFactory, the current Session, and three helper classes, New, Current and Existing. New is the class responsible for the beginning of the syntax you see above. Each method on New returns a Creator. There's some magic in Creator, so here's the code:

public class Creator<T> : FixtureContextAware where T: class, new()
{
  private T _creation;
  
  protected T Creation
  {
    get { return _creation; }
    private set 
    {
      if (Current.Get<T>() != value)
      {
        Current.Push(value);
      }
      _creation = value; 
    }
  }

  public Creator(IFixtureContext context) : base(context)
  {
    Creation = new T();
  }

  public Creator(IFixtureContext context, T creation) : base(context)
  {
    Creation = creation;
  }

  public static implicit operator T(Creator<T> creator)
  {
    if (creator._creation == null) throw new Exception(
      String.Format("Creation of {0} is null, it probably shouldn't be.", typeof(T)));
    creator.Current.Pop<T>();
    return creator._creation;
  }
}

The first thing is that Creator is a subclass of FixtureContextAware, which is just a helper base class that provides access to FixtureContext's children. Next there are a few references to Current which is simply a collection of stacks of entities so that Creators can refer to other entities that are being created so they don't have to be passed around. This is better explained with an example. In the beginning example you see New.EnrollmentIn(101). An enrollment requires a User to be created, so because there is a User in this creation context, we can do this:

public CourseEnrollmentCreator(IFixtureContext context, short number) : base(context)
{
  Creation.User = Current.User;
  Creation.Course = Existing.Course(number);
  Creation.StartDate = DateTime.Now;
  Creation.EndDate = DateTime.Now.AddMonths(1);

  Session.Save(Creation);
}

The next thing is that the creation itself is stored as Creation in the Creator. This can either be new'd up or can be passed in to the constructor.

The coolest part (at least in my opinion) is the implicit operator. This allows you do to do things like: UserEntity user = New.User().Foo(), where each of those methods returns a UserCreator, but at the end of all of it the Creator is implicitly cast to a UserEntity, the thing actually being created. This also serves as an excellent time to pop the entity from the Current stack.

Next we have the Existing class. This is essentially just a wrapper for your Daos/Repositories so you can fetch things that are already in your database (like Existing.Course(number) or Existing.User("bob")).

With this simple framework in place, the next step is to start writing your domain specific Creators. Here's an example:

public class PurchaseCreator : Creator<PurchasedProductEntity>
{
  protected PurchaseCreator(IFixtureContext context, PurchasedProductEntity creation) : base(context, creation)
  {
  }

  public PurchaseCreator(IFixtureContext context, string productName) : base(context)
  {
    Creation.Product = New.Product(productName);
    // Initialize Purchase...
  }

  public PurchaseCreatorWithSlot WithAcademicSlot(int credits)
  {
    // Create and add slot...
   
    return new PurchaseCreatorWithSlot(Context, this, slot);
  }

  public PurchaseCreatorWithSlot WithPlacementSlot(int credits)
  {
    // Create and add slot...

    return new PurchaseCreatorWithSlot(Context, this, slot);
  }

  public PurchaseCreator WithSessions(int credits)
  {
    // Create and add sessions...

    return this;
  }
}

public class PurchaseCreatorWithSlot : PurchaseCreator
{
  private readonly PurchasedCourseEnrollmentSlotEntity _slot;

  public PurchaseCreatorWithSlot(IFixtureContext context, PurchasedProductEntity creation, PurchasedCourseEnrollmentSlotEntity slot) : base(context, creation)
  {
    _slot = slot;
  }

  public PurchaseCreatorWithSlot FilledBy(CourseEnrollmentEntity enrollment)
  {
    // Fill slot...
  }
}

Then we add a PurchaseOf(string productName) method to New that will create and return a new PurchaseCreator. We can also add a PurchaseOf(ProductEntity product) and pass a New.Product("Product") in instead. You can make it as granular or magic as you want. Also, Notice that WithAcademicSlot and WithPlacementSlot return a subclass of PurchaseCreator that adds another method. Using techniques like this you can make some very verbose and context sensitive fixtures.

We also make our HibernateTests base class FixtureContextAware so that we can use nice syntax in our tests.

Here is the source for the basic framework. Let me know what you think. 

by Aaron on Saturday, September 29, 2007 3:48:54 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Friday, September 14, 2007

I just had to try the typing speed test that Mitch and Darren tried. Here are my results:

typingspeed

Wanting to try and be a bit more accurate since Darren said it's important, I tried it again (and yes, it was different text, so I didn't have any additional advantage other than the burning in my forearms... which isn't quite an advantage):

typingspeed2

I just can't seem to break 100 WPM and I don't see myself ever getting over 98% accuracy, ah well.

by Aaron on Friday, September 14, 2007 7:33:58 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [2]  |  Trackback
 Monday, September 10, 2007
mount19

OK, so it's not 100% free as you'll need a few drywall anchors (if you're not on a stud) and screws, but you probably have those laying around, and other than that it's free. When you're done, you'll be able to move the monitor up and down, tilt it up and down, and rotate it, just like you can on its stand.

I started taking it apart to see what I could do with it and I didn't start taking pictures until I was almost done, so you're going to have to use your imagination a bit on some of the pics and some of the steps may be off a bit. It looks like a lot of steps, but they're tiny and somewhat detailed... all in all it's pretty easy. Any ways, here you go:

  1. Pull off the monitor from the stand by pushing the black button and rotating it off.
    mount1
  2. Unscrew the four screws that mount the connecting plate to the stand and pull the connecting plate off.
    mount2
  3. This may be optional, but it's easy: unscrew the two little screws on the gray plastic circle and pull it off.
    mount3
  4. This is also probably optional: unscrew the two screws holding the plastic hinge cover on and pull off the cover.
  5. Pull off the bottom black plastic piece from the track on the stand.
    mount5
  6. Use all of your might to push the mount down to the bottom of the track until it locks.
  7. Pull off the rest of the black track cover (this is a little tricky, and I broke some of the tabs doing it, but who cares, right?) Basically you have to pull the sides out from each other and go all the way up to the top, then slide the middle part out.
  8. Push the button so that the mount slides (very quickly, watch your hands) to the top.
  9. Unscrew the three screws holding the long metal piece down to the track assembly and pull it out. Things will fall out, don't worry that's fine.
    mount9
  10. Unscrew the four tiny screws holding the track assembly to the stand. These screws were a pain because they're in there pretty tight and they're so small. I stripped one of them and had to drill it out.
  11. Unscrew the four screws holding the rear bracket to the two track rails.
    mount11
  12. Rotate the rails up and pop the metal coil out of the plastic thing.
    mount12
  13. Mount the rear bracket using the incredibly convenient four screw holes to the wall. I used some 2"x1/4" plastic drywall anchors and 1.5" screws. If you have a stud there, all the better, but it may be tricky getting all 4 screws into the stud.
    mount13
  14. Pop the metal coil into the plastic thing at the top of the bracket. Check to make sure it got in there straight or you may have some annoying problems.
    mount14
  15. Replace the four screws to secure the tracks to the bracket. In order to do this I needed to hold the mount a few inches down from the top and have my girlfriend screw the screws into the rails. That last sentence may get me some unexpected traffic from google.
    mount11
  16. Purely optional: Replace the two plastic covers and the four screws.
  17. Replace the mounting plate and secure it with all four screws.
  18. Play with the plate/mounting a bit to make sure its secure before putting the monitor on it.
  19. Put the monitor onto the mounting plate.
    mount19 mount20
  20. Hook up your cables and enjoy--if you're feeling adventurous, feel free to drill some holes and fish those cables through the wall :)
diy | hardware | tips
by Aaron on Monday, September 10, 2007 6:38:28 AM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, August 30, 2007

Don't ever put a Photo directly in your User entity (or any entity commonly used for anything but displaying a photo). It's not only arguably bad from a normalization standpoint, but NHibernate's flushes will absolutely kill you. Don't say I didn't warn you.

by Aaron on Wednesday, August 29, 2007 11:39:26 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Monday, August 27, 2007

James Kovacs replied to one of my many NHibernate Optimization Ramblings:

In the first example, how do you know that most customers are unimportant until you fetch them from the database? You have an additional problem that generally NHibernate sessions are short - especially in web apps or web services. So when do you reset your fetching strategy. What if one portion of your code uses different customer properties than another? The adaptive fetcher needs to do a lot of analysis of your post-fetch code paths or monitor the behaviour of the application as it executes. As it stands, NHibernate has a lot of options besides adaptive queries, which I believe are better including projections (since you as a programmer know the data you need) and lazy-loading of properties - both collections and objects. There are probably others. We're talking about saving milliseconds on DB queries when a round-trip to the DB is at least an order of magnitude greater. I personally feel that adaptive queries would require a lot of work for little gain. I call YAGNI.

This is probably my fault, but his first question tells me he doesn't quite understand what I'm trying to propose. It doesn't matter if customers are important or unimportant. Once the both code paths are hit and the strategy fully adapts, it is adapted and it will fetch a superset of fields that are required for that query in the context it is called. There's no reason to ever reset that strategy unless the code changes. 

Context is another important aspect of the adaptive queries, and I'm not sure how I'd implement it. At the moment I'm thinking that something along the line of scopes (nested or single level) so for each scope/query combination there would be a strategy. That's the answer to his second issue. The only analysis it needs to do is it needs to pay attention to what properties are hit on the entities it fetched by proxying that entity. That's it. No instrumentation, parsing, or any other crazy stuff.

 

using (Query.Scope("Print Customer Stuff"))
{
  customers = LoadCustomers();
  ...
}

As for the YAGNI assertion, I understand why you'd call YAGNI on the 2-12ms standard savings I showed in my tests, but You Already Do Need It at times (we use projections for just this) it's just that this would be automatic and require less maintenance and you wouldn't have to choose to do it. It would be free savings and require less manual optimization. If someone further down your chain decides they need to log customer.Name, you don't have to climb back up, find the original query and add it there. With projections at least you'd know you'd need to add it, but you'd have to change the query and change your DTO (anonymous types will help with this... I guess).

My point is, You don't need an inversion of control container, it's just easier. You don't need auto mocking containers for tests, it's just easier and you don't have to change your code when you change your constructor. You don't even need Mocks, you could write those by hand too. Well, with adaptive queries you don't have to change your code when you decide to access another field... or even another collection....

You also have to look further than field adapting. There's the potential to adapt collection initialization as well. No more select N+1's for those devs that don't pay attention, they'd just go away like magic. And yes, I realize hand optimized queries will generally prevail, but adaptively optimized queries will be free.

Today, Jacob had another great idea for a use of adaptive queries. We often do this in our MonoRail actions:

public void ShowCourseEnrollments([EntityParameter] User user)
{
  foreach (CourseEnrollment ce in user.CourseEnrollments)
  {
    // Do something with ce.Course.Number
  }
}

This probably requires a bit of explanation. Basically, when someone goes to the url /Controller/ShowCourseEnrollments.rails?user=1, MR's databinding (and our EntityParameter binder) will do an NHibernate session.Load<User>(1). At this time, the db hasn't been hit. As soon as we start enumerating user.CourseEnrollments, we're select N+1ing. Furthermore, we could potentially be doing another fetch for ce.Course. The solution to this is to either change your mapping to always fetch these things (bad idea anyone?) or to do something like this:

user = UserDao.FetchUserWithEnrollments(user.Id);

Well, what if adaptive queries kicked in at the databind, and instead of adding that method to your dao, you just got what you needed? Sure, YAGNI, but You Are Gonna Want It... if I or someone ever implements it.

by Aaron on Monday, August 27, 2007 9:11:52 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Sunday, August 26, 2007

Ayende posted a great comment with some questions about adaptive fetching. Here are his questions and my responses:

Let us assume this:
customers = LoadCustomers();
for customer in customers:
if customer.IsImportant:
print customer.Birthday
else:
print customer.CurrentCharge
What would the adaptive fetching do in this case?
Assume that you have started with mostly unimportant customers and then moved to important customers?
The amount of queries that would be generated is prohibitive.

In this scenario, adaptive fetching would generate a query that pulled IsImportant and CurrentCharge for unimportant customers. As soon as a single Important customer ran through this code, the query would change to fetch IsImportant, CurrentCharge and Birthday. It would also immediately lazy load *all* missing properties from the original query for all customers originally queried, and continue to track accessed properties. That's only one additional query for each differing codepath, and that's only the first time its hit. From that point on, until the query was reset (version upgrade, app restart if it's not persisted, manually, etc), then you would have all you needed for all codepaths.

Another problem that you have here is that you do a query like:
"select u.Name, u.Email from Customer" and the query actually returns you Name,Email, Address, Photo.
That violates the law of least surprise fairly drastically.

I don't see adaptive queries having that syntax. I'm thinking more along the lines of "select ??? from customer"  or "select what  i need from customer", etc. Specifying what you're looking for puts you right back into projections. If you want to be specific, use projections.

Finally, I think that a much easier solution than trying to eek a few more milliseconds from a DB query is not to go to the DB at all. Utilize NH's caching abilities, and you'll get a significant performance benefit for little cost.

I agree that most of the time you'd get more benefit from caching than squeezing some time out of the db. I just think it gets us one step closer to making mapping between objects and the db more friendly to both sides of the map, and gives developers a tool that works automatically "for free". I'm sure it'd also make DBAs happier that their devs aren't just doing select * all the time.

Now, if you still want adaptive fetching, the best way to get it is to help build the HQL Parser, which would generate a human workable AST.

Agreed. I guess if we work on rewriting chunks of NHibernate the code would become manageable eh? So yeah, I'll stop blathering and try and start contributing.

by Aaron on Sunday, August 26, 2007 10:44:50 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [1]  |  Trackback