Fluent NHibernate: A great project

I have to admit, I stopped lurking to the Fluent NHibernate mailing list a while ago. Ironically, it wasn’t because I thought nothing was going on, simply that the sheer volume of emails was producing massive amounts of noise in my inbox.  The unfortunate aspect of this is that often with .NET projects, there’s very little evidence of life outside of their mailing lists (for example, a lot of activity on the Castle project is focusing on Mono and Silverlight, try finding that information on their web site).

Now, I know from personal experience just quite how painful it is to produce a patch for an open source project, never mind actually maintain one.  The people doing this are typically doing it because they find the end product useful in its own right.  End users, who don’t contribute to the improvement of the project, are necessarily second class citizens in amateur open source projects.

Now, take a look at this bug report and comments #4 and #5.  #4 was me in a pretty bad mood, I’d just lost a day trying to work out what was going on.  #5 is a completely reasonable response from someone who has put a lot of work in to the project.  It does two things

  • It explains the current priorities and why the issue is not fixed.
  • It reassures the end user that his concerns are appreciated.

As I say, no-one forces people to work on amateur open source projects, and trawling through bug lists is definitely pretty far down most developers list of favourite things to do.  That’s what I mean when I say that FH is a great project.  (The code’s quite useful too…)

The aspect that I haven’t mentioned is that projects that don’t take end users seriously tend to wither, if only because they run out of new contributors.  Whereas I’m not qualified to help with the code, I can produce a decent bug report.  I’m going to go do that right now…

Technorati Tags:

Doing a self join with Fluent NHibernate AutoMapping

This is definitely in the category of posts “write it down before you stop understanding the solution”.  Let’s say that you’re trying to track your servers.  Servers are related to other servers.  So, for instance, a live server has a corresponding DR server and a corresponding UAT server.  Let’s say also you have a simple table with two foreign key references that implements this relationship.  You can implement this in fairly short order:

mapping.HasManyToMany(s => s.RelatedSites)
mapping.IgnoreProperty(s => s.RelatedSites);

The only problem with this is that it doesn’t work.  Now, if you automatically export your mapping files (and you should) you’ll see that it’s ignoring the key columns you specify.  The automatic configuration is doing its own thing.

The problem is that you also added a foreign key convention by using the helper.  Take a look at the code that implements this.  You’ll see that the Accept code looks a bit dodgy when you compare it to the Apply method.  Instead, you can try the following code:

class ForeignKey : ForeignKeyConvention, IRelationshipConvention
    private readonly Func<PropertyInfo, Type, string> standardRule;

    public ForeignKey(Func<PropertyInfo, Type, string> standardRule)
        this.standardRule = standardRule;

    protected override string GetKeyName(PropertyInfo property, Type type)
        return standardRule(property, type);

    public bool Accept(IRelationship target)
        if (target is IManyToManyPart) return base.Accept((IManyToManyPart)target);
        return true;

    public void Apply(IRelationship target)
        if (target is IOneToManyPart) base.Apply((IOneToManyPart)target);
        if (target is IManyToManyPart) base.Apply((IManyToManyPart)target);
        if (target is IManyToOnePart) base.Apply((IManyToOnePart)target);

This code fixes the problem.

Anatomy of a Bug

This is a fix that raises as many questions as it answers.  One is why Accept still doesn’t do the obvious thing, the other is why I’m not going to submit this as a patch.  The second is pretty easy to answer: this is nothing like a solution to the underlying problem.  The first takes a bit longer…

If you make Accept do the obvious thing, it stops applying your foreign key rules anywhere.  The problem is that the rules for one to many and many to many check to see if they’ve had their keys set already.  And they already have, by the factory foreign key convention.  So the user rule does the wrong thing in order to make up for that.  So two wrongs nearly make a right, until you introduce a many to many part.  The fix I’ve described above fixes it for me, but it’s far from obvious that the underlying problem is fixable.

This is the second time I’ve found the factory conventions broke my code through no fault of my own (the other is the well documented, but still present, nullable enum bug) and I have to admit that I’m starting to wonder if the conventions code is more trouble than it’s worth.  Certainly the interaction between the unremovable factory conventions and your own conventions is at best hard to understand and at worst an unsolvable problem.  Well, I’m still learning…

Technorati Tags:

AutoGen: Extending the Convention Approach to Event Wiring

I couldn’t quite leave this code alone.  I have to admit, the whole reason I wrote the AutoGen stuff in the first place was out of frustration at the amount of plumbing factories I was having to write in a recent project.  Well, the plumbing of event wiring was getting at me as well, so I decided to do something about that as well.

Let me explain the problem I wanted to solve, first.  I recently had to modify a program that took one fix connection to handle one or two, depending on the deployment scenario.  Now, obviously, handling forty is pretty much like handling two from an API perspective, but going from one to two tends to involve refactoring.  The annoyance was, in this particular case, it was absolutely obvious what I wanted to do.  I wanted both to open, close and reset at the same time.  This was done through the obvious interface.

interface IFixConnection {
    void Open();
    void Close();
    void Reset();

Now, here, there’s no complex API.  It’s just a series of commands.  All I want is for it to pass the commands on to the implementors of the interface.  So, I wanted to be able to write.

container.Register(Component.For<IFixConnection>().Named("AggregateConnection").AutoWire("principalConnection", "controlConnection"));

and for it to “just work”.  Actually, I wanted to go further than that.  Large parts of my system need to know when a reset occurs, but I didn’t have to have to wire them up by hand.  So actually the interface looked like this:

internal interface IResettable {
    void Reset();
interface IFixConnection2 : IResettable {
    void Open();
    void Close();

Where quite a lot of objects implemented IResettable.  Now, I wanted the following:

  • The controller object shouldn’t need to know that some people only implemented part of the interface.
  • I shouldn’t have to wire up the objects to the AutoWire aggregate by hand.
  • It should keep track of creation and release.

So, I added in the following syntax:

container.Register(Component.For<IFixConnection2>().Named("AggregateConnection").AutoWire(s => s.WithCurrentObjects().UseWeakReferences()));

This supports the above behaviour.  It also has the advantage of requiring the minimum rewiring for different deployment scenarios.  The linkage works on the basis of whether or not the interface is implemented, not whether it is exposed.  This is a deliberate design decision to sacrifice a small amount of control for ease of use.  It certainly didn’t simplify the implementation.  Finally, the aggregate object exposes an IBroadcaster interface, defined as follows:

public interface IBroadcaster {
    void Add(object listener);
    bool Remove(object listener);
    void Clear();
    IEnumerable<object> GetListeners();

which can be used to dynamically modify the list at run-time.  It also implements a chain of responsibility pattern if the target interface method returns bool or a class.  Finally, it’s worth noting that the AutoWire does the opposite of AutoGen when presented with a Dispose method.  A dispose method clears the listeners, it doesn’t dispose of the listeners.  This is a special rule, but I think a useful one.  Anyone who really did want to dispose of the listeners can always get the list through GetListeners.

Anyway, it’s on the patch list in donjon.

Technorati Tags: ,