Ayende @ Rahien

It's a girl

Those are the rules, even when you don’t like them

Originally posted at 11/4/2010

Recently I had an interesting support call for NHibernate. The problem was a bit complex when explained to me, but we got it simplified to something like:

When we have a component that contains a set, that component is not null, even when all the members are null.

The problem is actually an intersection of two separate rules in NHibernate:

  • When all the members of a component are null, the component itself will be null.
  • A set is never null, an empty set is still a valid instance of a set.

When you add a set to a component, you add something that is never null. Hence, the behavior of NHibernate in this case is also valid, since we have a non null member value, there will be an instance of that component.

The problem is that the users conceptually thought of the empty set as null as well. I had some hard time explaining that sets are never null, and that no, this isn’t a bug or unexpected combination of the two features. Both behave exactly as expected, and the intersection of both worked as expected. In fact, trying to make it not work in this fashion would introduced a lot of work, complexity and additional queries.

Comments

configurator
11/05/2010 11:09 AM by
configurator

This feature makes sense from an NHibernate developer's point of view - as you said yourself: "trying to make it not work in this fashion would introduce a lot of work, complexity and additional queries."

But from the user perspective it could be puzzling. Perhaps the fact that you needed to explain this means it's doing something that's unintuitive to someone not as fluent in the source as you are.

Or maybe not.

Peter Morris
11/05/2010 11:31 AM by
Peter Morris

It seems they probably didn't know the difference between null and empty.

Empty = Known collection of no values

Null = No idea what the collection is, might be empty or might not; we just don't yet have the data.

Jason Meckley
11/05/2010 12:11 PM by
Jason Meckley

I agree with Peter. Many people have a difficult time understanding null. null doesn't exists, empty exists, there just isn't anything in it.

Dmitry
11/05/2010 01:54 PM by
Dmitry

It makes sense because usually it is a bad idea to allow collections/sets to be null.

I do think that it would be more intuitive to initialize a new component instance even if all the properties are truely null.

Steve
11/05/2010 02:11 PM by
Steve

I guess if you don't try to remotely understand the concept of a set it could be confusing.

It's just one of the fundamentals of Discrete Math.

Dave
11/05/2010 02:13 PM by
Dave

Any collection should never be null. A collection can be empty, but not null. It's not even NHibernate related if you ask me.

It's just bad practice if one of your types contains a collection that can be null.

I also urge my developers to also initialize strings to string.empty so you don't have to use object.Equals to do string comparisments without prevalidating (string.IsNullOrXxx) you're strings. But a string is the only class that I use as if it we're a structure..

Alex Simkin
11/05/2010 06:35 PM by
Alex Simkin

Assume you have Parent having collection of Children.

Now, how you represent the following facts?

  1. We didn't yet collect information about children.

  2. We did collect information and there is no children,

  3. We did collect information and there is exactly n children.

  4. We did collect information and there is at least n chldren (but maybe more).

Dmitry
11/06/2010 02:20 AM by
Dmitry

What happens if you have a struct property in a component? Is the default value considered NULL or the component will be initialized?

Rob Kent
11/06/2010 09:09 AM by
Rob Kent

@Dave, "I also urge my developers to also initialize strings to string.empty "

I guess that if you obey that convention throughout your code, it might be safe. But what if you actually want a string to have an empty value and that is significant?

The purpose of null is to inform you that a field's value has not been set yet. I just have an extension method IsNullOrEmpty which tests for both in one go. In fact, I use IsNullOrEmtptyTrimmed most of the time because I am normally not interested in blank strings.

James
11/06/2010 10:19 PM by
James

Reminds me of the vigorous discussion we had with our Oracle DBA who was of the opinion empty strings should be treated as nulls, due to the mistake Oracle made in their SQL implementation.

Happily, he lost :)

Roy
11/07/2010 08:08 AM by
Roy

Ignoring the bullshit comments about "the difference between null and empty" and "Set Theory", The question still remains:

How do I define a nullable component if that component has a collection in it? It's a perfectly valid OO notion and if NH can't translate that to DB terms it's lacking a feature IMO.

Frans Bouma
11/07/2010 11:07 AM by
Frans Bouma

@Roy

however, considering the edge case when one wants a set inside a component (which is a valuetype DDD wise), I don't see why one ever needs it.

Roy
11/07/2010 02:44 PM by
Roy

@Frans

How about an email component, with its own data and behavior. This can be a value type reused by multiple entities in the system, and it can certainly be nullable.

It works quite well until I add a "UsedFor" collection to the email. NH breaks the nullability silently - with no warning, instead of null Email components we start getting non-null instances with null Address properties. This breaks the model because Address was defined as not-null in the component.

class Email

{

public string Address {get; set;} // not nullable

public void Validate() {...}

public void Send(string message) {...}

public ISet

<emailusagecategory UsedFor {get;set;}

}

// Categories are not hard coded

class EmailUsageCategory

{

public string Name {get;set;}

...

}

Rafal
11/07/2010 03:44 PM by
Rafal

Roy, thank you for the example,your example made this post clear to me . And surely something is broken when non-nullable fields are allowed to be null - or at least it shouldn't be called the 'expected' behavior.

Richard Dingwall
11/07/2010 10:36 PM by
Richard Dingwall

I always thought components were a bit of a strange way to map something, so I'm not entirely surprised by this behaviour. I guess if you hit this problem, your options are:

  1. Model your code in such a way that the component is never null (e.g. invariants, default populated values), or

  2. Make the component an entity and reference it via a one-to-one (not nullable) or one-to-many relationship (nullable), or

  3. Write a custom IValueType

@Roy: unless I am mistaken, your Email class cannot be mapped as a component if it is reused by multiple entities?

Roy
11/08/2010 07:39 AM by
Roy

@Richard: We reuse components all the time. I consider it one of the best uses of this feature.

As for the workaround, we'll probably end up masquerading the NH behavior using a property that wraps the NH-initialized component and returns null instead of default values.

sinm
11/11/2010 01:06 PM by
sinm

All these 'rules' are just complex ORM-technology-related excuses in their nature.

Comments have been closed on this topic.