NHibernate : No update with composite key

1k views Asked by At

I have an object that is saved using Nhibernate. This object use a composite key and is declared like this :

        CompositeId()
          .KeyProperty(x => x.CreditorName)
          .KeyProperty(x => x.CreditorIBAN)
          .KeyReference(x => x.Config, "ProfileName");
        Map(x => x.ID, "ID").ReadOnly();
        Map(x => x.CreationDate, "CreationDate").Default(null);
        Map(x => x.ContractReference, "ContractReference");
        Map(x => x.CreditorBIC, "CreditorBIC");

        References<C_ContractType>(x => x.ContractType, "ContractType_ID");
        References<C_Country>(x => x.CreditorCountry, "CreditorCountry"); 

        Table("Creditor");
        Cache.ReadWrite();

We can save on without any problem but when we try to update a field (no matter which one) with Session.SaveOrUpdate(entity); nothing is done (no update) with no error message.

Did we missed something ? Thanks for your help !

-- EDIT --

I add Session.Flush() after the update method and it's a little better as I can update any value except the ones that are in the key (CreditorName and CreditorIBAN). Any idea ?

2

There are 2 answers

3
Radim Köhler On BEST ANSWER

I. PERSISTING, executing sql write command

as discussed in the comments, the first thing we need to make that all happened is to extend the our code with a ISession.Flush()

9.6. Flush

(some cite)

...
Except when you explicity Flush(), there are absolutely no guarantees about when the Session executes the ADO.NET calls, only the order in which they are executed. ...

And as discussed here

How to change default FlushMode to Commit in C#?

We can assign the default flush mode during the session creation

var session = SessionFactory.OpenSession();
session.FlushMode = FlushMode.Commit;

And be sure that it will be called once transaction is committed or call it explicitly anytime we need to

Session.SaveOrUpdate(entity);
Session.Flush();

II. Key/Id change for already persisted, not transient entity

This is by design, by intention IMPOSSIBLE. Because the DB Key, the C# runtime entity Id is a data layer pointer to the instance. We should not work with these, we should not try to think about them

Our entities should just be managed with references:

var entity = ...;
entity.ContractType = someContractType;
entity.CreditorCountry = defaultCountry;
...

This way we assign db Keys, represented as Ids... on a Flush(), without mentioning them.

And that's also why we should not think about ability to change them.

The best we can do - is to not use Composite Key - and use

Surrogate Key

A surrogate key in a database is a unique identifier for either an entity in the modeled world or an object in the database. The surrogate key is not derived from application data, unlike a natural (or business) key which is derived from application data.

With SQL Server it is very easy to introduce new column with IDENTITY(1,1)... just use ALTER TABLE TheID INT NOT NULL IDENTITY(1,1) ... and its there

Re-map all your composite parts to properties (.Map() or .References()) and then you can change anything you want

I can understand that this is not desired answer, but I would strongly suggest at least to think about it. Simply,

Key should be generated only once. Then left unchanged for ever ... or deleted with its row/instance

1
Stefan Steinegger On

You have to Flush or Commit the session before the changes are written to the database.

Also look at this: