NHibernate dynamic-update Disadvantages?
Asked Answered
O

2

17

Is there a reason not to use dynamic-insert/dynamic-update for NHibernate? The only reason I ask is that it seems to be something that I would want enabled as the default, not as something I would have to configure.

Are there any gotchas to be aware of when using these dynamic properties?

Orphrey answered 19/12, 2012 at 14:49 Comment(0)
R
35

For some entities, you may create an invalid state though dynamic updates. Let's say you've got a Class with a Boolean Property A and a logically dependent Integer Property B. If Property A is True, then Property B can only be a negative number, while if Property A is False, then Property B can only be a positive number.

Let's say that two users both interact with an instance of this class within a given time span. To start off, users Alice and Bob both materialize this class from the database, with the initial values of A = True and B = -50

Database   Alice       Bob
A: True    A: True     A: True
B: -50     B: -50      B: -50
VALID      VALID       VALID

User Alice changes A to False and B to 125, and commits it to the database. Now we have this situation:

Database   Alice       Bob
A: False   A: False    A: True
B: 125     B: 125      B: -50
VALID      VALID       VALID

User Bob doesn't change A, but changes B to -75, then commits it to the database. If Dynamic updates are on, then NHibernate sees that Bob has only changed B to -75, and issues a dynamic update that only edits the value of B. If you had SQL validation on the server to prevent B from being negative unless A was true, you'd get a SQL error here, but let's say that you haven't reproduced all your business logic on your SQL tables. Here is the resulting data:

Database   Alice       Bob
A: False   A: False    A: True
B: -75     B: 125      B: -75
INVALID    VALID       VALID

Both Alice and Bob have valid states, but the Database is now in an invalid state! User Charlie comes along and tries to materialize this record:

Database   Alice       Bob       Charlie
A: False   A: False    A: True   A: False
B: -75     B: 125      B: -75    B: -75 
INVALID    VALID       VALID     INVALID

Charlie would likely get a validation error from your application when NHibernate tried to set the B property of the new instance of your class.

So, when you have logically dependent properties, you must have some strategy in place for avoiding this situation. One possibility is to simply enable select-before-update for this entity. This can result in some additional database calls and a little slower performance. Another is to utilize versioning in NHibernate, which would mean that when Bob tries to save his record, NHibernate's insert query would not trigger any writes and throw a stale data exception (that can be gracefully handled). You also could codify the logical requirements of your class in the database, however you'll then have to be cautious to make sure the database and the program both have the same codified requirements as time goes on, and you'll have multiple places to make changes when requirements change, which isn't always a worthwhile overhead.

So in short, in many circumstances a developer must handle the details of dynamic-updates carefully, which is why it is not on by default. When you turn it on, consider if partial updates to your entity could cause problems, and if so, use one of the mitigating strategies I've recommended to protect against that issue.

Rank answered 19/12, 2012 at 15:11 Comment(9)
In this example the application allowed the user to commit changes while the entity was in an invalid state. The class should encapsulate access to those properties so that the business rule is enforced, and/or the entity should be validated before the transaction is committed. I don't see that this has anything to do with dynamic update.Teledu
You're missing the lesson here: Bob's copy of the entity was not in an invalid state. The entity only became invalid when the dynamic update changed the value of property B without having the information that the persisted value of Property A would make that value invalid.Rank
I did miss the point and I'd retract my downvote if I could (I upvoted a few of your other excellent answers as compensation). Sorry, I read it more than twice and still missed the point.Teledu
Seems like versioning could be a good solution here. Select-before-update spoils all advantages of dynamic-update, as well as optimistic-lock="all", because the database has to read the entire row first and compare values This is especially bad for long strings/varchars and clob/varchar(max) objects.Sane
I think this rule should be enforced at least at database level, through a check constraint, if it's so important. However, I think it must be validated on entity level before save, because if it's known it is not a valid state, it shouldn't even have to hit the database before finding it out.Luxemburg
@ErikHart Versioning is another method that could help eliminate this gotcha. Good suggestion.Rank
@guillegr123 You can enforce the logic at the database level, but then you've got a database exception to handle when Bob tries to save his entity. Keep in mind that as far as Bob's application and Bob's NHibernate is concerned, Bob's entity is valid. You have to have some mechanism by which NHibernate discovers that he's operating from stale data before attempting an dynamic-insert.Rank
I do not, at all, understand this explanation. Are you saying that the getter of Property A in the code returns true or false depending on the value of B, and that's not picked up by the dynamic update (or, I suppose, the setter of Property B also changes the value of Property A)?Sanmiguel
@Sanmiguel no, in this example both A and B would be columns in the database mapped to properties on the class. This is a very nonsensically trivial example, the point is just that if you have logically dependent properties at all, you should be careful about using dynamic-updates without having a mitigation strategy in place to avoid the data getting into an invalid state when partially updating a record with potentially stale data.Rank
T
6

Dynamic insert and update have a slight performance cost because NHibernate has to construct the SQL every time instead of caching it. I don't know if there is a cost on the database side. Dynamic insert and update are very handy for troubleshooting and profiling. If I were able to measure a meaningful performance hit I would turn it on in development and off in production.

You may run into issues with listeners and interceptors, see this question.

I have it turned on in my NHibernate applications with no problems. My answer is no, there is no good reason not to use it.

Teledu answered 19/12, 2012 at 15:38 Comment(2)
Curious on your comments about caching. When I look at the SQL generated using log4net, it still appears to be using parameterized queries. Won't those still be cached somewhere along the line? Also, doesn't leaving dynamic queries off also have a performance hit by updating fields that have not changed? Or is that cost negligible? Thanks for the feedback.Orphrey
NHibernate can cache a static query format so that creating a query is just filling in the blanks. With dynamic queries, the query must be constructed every time. The performance hit of updating all fields, if any, is database dependent. There are a number of factors but my opinion and experience makes me recommend using dynamic insert and update.Teledu

© 2022 - 2024 — McMap. All rights reserved.