15

I have a mapped entity, Matter, that has a mapped component, Injury.

The only property on the Injury is DateOfInjury which is a nullable datetime.

When I retrieve the Matter, if the DateOfInjury is null, the component is null.

Thus something like this matter.Injury.DateOfInjury will throw.

Could someone explain if I am doing something obvious to cause this behaviour?

I would have expected that the Injury component gets initialized by nHibernate as an object and that the DateOfinjury property is null.

This would be more flexible i would think?

  • 2
    Might be worth leveraging the NHibernate Interceptor/Event system to call a PostLoad initializer on your Matter object to initialise an Injury member if it is null. This does bleed into your business layer a little bit but it can be minimised (can make the initialiser an internal static method on Matter, for instance) – fostandy Mar 20 '11 at 05:45

5 Answers5

11

I think that's the default behavior for a component mapping. The NHibernate docs for component say that if all elements of the component are null, the component itself will just be null.

If you only have a single property in the component, it might make sense to just map it as a nullable DateTime property on the Matter class.

Andy White
  • 86,444
  • 48
  • 176
  • 211
  • Fair enough, the component will ultimately have more properties. I think it would be nice if the component came back not-null, if all properties are optional data, id have to do a null check to cater for nhibernates behavior. –  Apr 21 '09 at 01:53
  • I agree, that behavior is a little strange. I would have expected it to do what you were saying. I guess it makes sense though, it saves Hibernate from creating an extra object when there is nothing to put in it. – Andy White Apr 21 '09 at 01:56
6

I also ran into the same problem of expecting NHibernate to initialize my component even if all its members are null in the DB. My motivation behind this implementation was to move as much logic concerning my component into the component, not having to deal with it being null or not.

Thanks to this post my search for an explanation why my unit tests were failing for all null values inside the component was short. I fixed this piece in the puzzle by extending the auto-property of my component class ArrivalDay and assigning a fresh instance myself when null is assigned:

private ArrivalDay _arrivalDay;
public ArrivalDay ArrivalDay
{
    get { return _arrivalDay; }
    set { _arrivalDay = value ?? new ArrivalDay(); }
}

This works like a charm and means very little overhead on the containing class.

Oliver
  • 9,239
  • 9
  • 69
  • 100
  • 5
    Only problem here: This creates troubles for NHibernate when tracking the components. You'll get an 'object references an unsaved transient instance' once you try to flush the instance – Tigraine Jun 10 '10 at 11:45
2

I've resolved this by adding this property to my component class

public virtual bool _LoadAlways { get { return true; } set { } }
  • this doesnt seem to work for me, must this property be mapped as well? – sawe Jul 22 '13 at 11:37
  • That's pretty darn smart. You might want to also make sure in the mapping that it doesn't get saved to the database. – richard Dec 02 '19 at 06:56
2

https://stackoverflow.com/a/11187173/206297 didn't work for me, but building on it:

public class Injury
{
    // ...
    private bool dummyFieldToLoadEmptyComponent { get; set; }
}

public class MatterMap : ClassMap<Matter>
{
    // ...
    Component(x => x.Injury, m =>
    {
        // ...
        m.Map(Reveal.Member<Injury>("dummyFieldToLoadEmptyComponent")).Formula("1=1").ReadOnly();
    });
}

The Reveal.Member bit is just to map a private field in Fluent NHibernate. We want the field private because we don't want that property exposed as part of our public interface to the component. See https://github.com/jagregory/fluent-nhibernate/wiki/Mapping-private-properties. If you don't mind having it public, you could use the less verbose mapping of:

m.Map(x => x.DummyFieldToLoadEmptyComponent).Formula("1=1").ReadOnly();

The Formula part is because we don't actually want a column in our DB for this. NHibernate will execute that formula when loading the component, and it'll always evaluate to true. I chose 1=1 as I would imagine that's reasonably cross-DB.

Undoubtedly a hack, but seems to work so far for loading empty components and hasn't caused any errors when persisting. Use with discretion though.

Community
  • 1
  • 1
ngm
  • 7,277
  • 1
  • 53
  • 62
  • 2
    "I chose 1=1 as I would imagine that's reasonably cross-DB" - turns out that's a bad assumption, as it doesn't work on SQL Server. Just using `.Formula("1")` works on SQL Server. – ngm Jul 28 '15 at 15:48
1

This is a technically workable solution. I have tested it with persistance and havent produced transient related issues.

protected internal virtual Injury NullableInjury {get;set;}
public virtual Injury Injury 
{
   get{return NullableInjury ?? (NullableInjury = new Injury()); 
}

In Nhibernate map your component to the NullableInjury. This solution allows you to persist without the transient issue reported in @Oliver solution.

Jonathan
  • 2,318
  • 7
  • 25
  • 44