5

I have a domain class that looks like this. I want NHibernate to save the current value of LastUpdate when inserting/updating so that I can use it in queries, but to ignore it when retrieving a Foo from the database and let the object itself recalculate the value when I actually access it.

public class Foo {
    public DateTime LastUpdate {
        get {
            /* Complex logic to determine last update by inspecting History */
            return value;
        }
    }
    public IEnumerable<History> History { get; set; }
    /* etc. */
}

My mapping for Foo looks like this:

public class FooMap : ClassMap<Foo> {
    Map(x => x.LastUpdate)
        .ReadOnly();
    HasMany(x => x.History);
    // etc...
}

I thought that ReadOnly() was what I wanted to accomplish this, but when I try to create a SessionFactory I get the following exception:

Error: FluentNHibernate.Cfg.FluentConfigurationException: An invalid or incomplete configuration was used while creating a SessionFactory. Check PotentialReasons collection, and InnerException for more detail.
---> NHibernate.PropertyNotFoundException: Could not find a setter for property 'LastUpdate' in class 'Foo'.

The property doesn't have a setter because it shouldn't be set, only read from. Is ReadOnly() the correct thing to do here? If not, what?

(NHibernate v3.0b1, Fluent NHibernate v1.1)

Brant Bobby
  • 14,956
  • 14
  • 78
  • 115
  • The ReadOnly(); only sets in the generated mapping xml streams the insert="false" and the update="false" attributes of the LastUpdate property. – tolism7 Nov 07 '10 at 00:13

3 Answers3

14

ReadOnly instructs Fluent NHibernate to not look for changes on this property, this does not equate to a read-only property in compiler-world. Your property isn't read-only in NHibernate's eyes because you're expecting it to be populated from your database. What you need to do is tell NHibernate that it should access the value of that property through a private field with the same name (lowercased) as the property.

Map(x => x.LastUpdate)
  .Access.Field();

There are several alternatives to using Field, which one you use will depend on how you name your private fields.

ESV
  • 7,620
  • 4
  • 39
  • 29
James Gregory
  • 14,173
  • 2
  • 42
  • 60
  • 2
    I don't actually want the property to be populated from the database, it is calculated dynamically in the getter. I just want it to be saved to the database so that I can query against it. – Brant Bobby Nov 08 '10 at 15:58
  • 6
    If Nhibernate does not populate it, it will not know to save it because it has no idea if and when the value changes. Both solutions offered are exactly the same and do what you described. NHibernate can access the variable by field so you don't need a setter. When you access the getter the value will be calculated dynamically. When you update NHibernate will save the value. The fact that NHibernate populates it from the db should be irrelevant - until you access you don't know/care, when you do access it the value will be calculated. Look at as "initialized" by Nhibernate if it helps. – Sisyphus Nov 09 '10 at 06:30
  • @Sisyphus That explanation helps a lot. Thank you. – Brant Bobby Nov 12 '10 at 19:06
  • This would be access="field" for mapping XML, just in case someone is looking for that. – falstaff Jan 25 '13 at 15:03
  • 4
    I would use Access.ReadOnly() – Mike Rowley Jun 30 '14 at 15:49
  • Same effect but `Access.ReadOnly()` is more intuitive. – Rafael Pizao Oct 14 '20 at 21:13
2

As far as NHibernate goes, you can map to a field, i.e member variable so Nhibernate can access the member variable directly. So you can create a member variable like _lastUpdate that can be mapped directly. Nhibernate will now have a variable to use and you can control the value separately in your getter because NHibernate will no longer use the property getter. It will save the value, and retrieve it too, but the retrieved value should not matter because as soon as you access through your getter you can recalculate it. Ditto for private variables with no getters or setters.

In a regular hbm you would just map access=field. Everyone does it. Apparently Fluent is not a simple. I don't use Fluent ...

EDIT ...

find whatever the seemingly always moving target for mapping private backing fields is in your version and use that ...

Sisyphus
  • 4,181
  • 1
  • 22
  • 15
0

Just add an empty private setter to your class:

        private set { }

I also add an attribute and a comment to document my intent (and make ReSharper happy):

        [UsedImplicitly]
        // ReSharper disable once ValueParameterNotUsed
        private set { }