Is there a way to use the NHibernate session to figure out if changes need to be written to db? - nhibernate

I'm using NHibernate here with C#. I have a cache of nhibernate objects that have lazily loaded objects inside of those that might have changes written to them. I need a way to determine if there are changes that need to be saved. It's quite a lot of effort to set flags when one little thing changes and also quite annoying to compare a cache with a copy of an original (because of the lazy loading).
Just wondering if there's a way I can use the current session object to know if it has pending changes that are about to be written to the db, this is so that I can have a 'do you want to save' prompt if in fact there are changes. I can't autosave, the customer demands a save button.

NHibernate.ISession exposes an IsDirty() method so you should be able to check that.

Related

When should we use the advanced parameters of save()?

Normally we save an instance into the database simply with inst.save(), but Django uses user.save(using=self._db) in its source code. Also, it uses user.save(update_fields=['last_login']) elsewhere.
This somewhat confuses me. To make things worse, the document for the save() method is extremely simple:
Model.save(force_insert=False, force_update=False,
using=DEFAULT_DB_ALIAS, update_fields=None)[source]
If you want customized saving behavior, you can override this save()
method. See Overriding predefined model methods for more details.
The model save process also has some subtleties; see the sections
below.
It doesn't even contain the explanation of those parameters!
My question is: how do I know when I should use the advanced parameters of save()? If I'm implementing a custom model, I would definitely write user.save().
I've done a couple of experiments myself, like change user.save(using=self._db) to user.save(), and nothing went wrong, but I don't want to be surprised someday. Also, the parameters must be passed for some reasons, right?
The answer is you will know when you need to :)
For now resort to this practice
class MyModel(models.Model):
def save(self,*args, **kwargs):
# do whatever
super(MyModel,self).save(*args,**kwarags)
This way you make sure that you don't accidentally drop any of those mysterious, parameters. But let's try to demystify some of them.
using=self._db
This is to facilitate the use of multible databases in a single django app. Which most apps don't really need.
update_fields
If save() is passed a list of field names in keyword argument
update_fields, only the fields named in that list will be updated.
This may be desirable if you want to update just one or a few fields
on an object. There will be a slight performance benefit from
preventing all of the model fields from being updated in the database
https://docs.djangoproject.com/en/1.11/ref/models/instances/
So the link to the source code is a specific instance where they have used this feature. Quite useful to keep track of when a user logged in for the last time without updating the entire record.
force_insert vs force_update
These tell django to try forcing one or the other operation. Also explained to some extent in https://docs.djangoproject.com/en/1.11/ref/models/instances/
The example of user.save(using=self._db) I believe is redundant when you only have one db, usually defined as "default
. This example simply points out that if you have multiple dbs, you can pass in which of multiple dbs to use when saving.
update_fields is also handy when you keep a reference to an instance for a long time (for example in a middleware) and the data might be changed from somewhere else. In these cases you either have to perform a costly refresh_from_db() on the instance to get the newest data from the database, or when you only want to override specific attributes you can omit the refresh_from_db() call and just use save(update_fields=['attr1', 'attr2']). This will leave all other attributes unchanged in the database except for the ones you specified. If you omit update_fields in this case all values would be overwritten in the database with the values of your cached reference to the instance, leading to information loss.

Have NHibernate ignore not set properties?

I have a huge object, it has a lot of lazy loadable properties.
I want to enable a quick edit of a very small subset of its property.
How can I, when I just have a few values, tell NHibernate: don't touch anything else?
Because now, when I save, everything not set gets lost.
Have you tried dynamic-update option on your class mapping?
<class name="SomeEntity" dynamic-update="true">
But check if the flush does not cause the unloaded lazy properties to get loaded first, just in case.
In your question, you state you lose other properties. I have never witnessed such a behavior. Are you attaching (using ISession.Update or ISession.Merge) a detached entity in your current code?
What I am suggesting will not work in such a case. It should instead work with an entity loaded from the current ISession, touched on some properties then saved to db only using ISession.Flush (or preferably, ITransaction.Commit, since it is not a good practice to work without transactions).

iOS synchronization - what is a good way for a Core Data object to know it has to be pushed to server?

I am building sync functionality between an iPad and a web server. I'm using an approach pretty close to the one described here. I only have one type of object, let's called it a Story, that has to be synchronized. It is a Core Data entity (managed object).
The remaining problem I have to solve is knowing "whenever something changes and needs to be synchronized to the server." One approach would be to go find every piece of code that modifies a Story and have it also set some needsSyncing flag. That does not seem elegant and it seems that over time, developers could forget to update the flag for new types of modification.
Do Core Data objects have a way to observe themselves, so any time any change is made, a particular method is executed? That would make this pretty easy.
Another option might be using the isUpdated method right before doing a save operation on the managed object context. You'd either have to have save called in only one place or do this at every place you save (sounds like the first option). I guess I could make a helper method that goes through all Story objects right before saving and see if any of them need their flag to be set. The only drawback to that is that I'd be traversing all Story objects in the system for any save, even for saves that have nothing to do with a Story.
Anyway I'll stop trying to guess the solution out loud - does anyone have experience with a good way to do this?
SDK has you covered. See the NSManagedObjectContext class reference, at the very end of the page, the MOC will post notifications that you can subscribe to, including NSManagedObjectContextObjectsDidChangeNotification. You can listen for these and do the update call pretty much coincident with saving the MOC.

Get rid of UoW's implicitness

NHibernate's Session and EF's ObjectContext are implementations of Unit of Work pattern and suggest similar approaches for change tracking: you retrieve some entities, then modify them somehow and after that call SaveChanges/SubmitChanges/Save/etc.
I don't like the implicitness of this approach. I don't like that entity modification automatically means it will be saved. I would like to explicitly mark entities that should be saved. What are the best ways to achieve this kind of control in NHibernate or EF?
(note: fortunately I've never dealt with EF; my answer is about NH only)
I think your initial assumption is wrong:
entity modification automatically
means it will be saved
that's not true; in order to persist the changes you've made you need to either:
call Session.Flush() yourself
set Session's flush mode to AutoCommit (highly not recommended)
use an ITransaction and commit it (by far the best approach).
unless you do any of the above your changes would not be persisted.
Personally I feel that NH gives me complete control over what goes into my DB.
here's a good article.

NHibernate FlushMode - Why Can't NH Evict Everything Not Saved?

I've spent some time searching around how to configure NHibernate's FlushMode so it could only save objects that I've explicity called Save/Update/Delete, but I've figured out that I can't do that. Instead of it, I have to evict every object that I've modified (even without calling Save/Update/Delete) as I'm using NHibernate transaction management.
I understand perfectly why NHibernate have to Flush some objects before some Find operations, but I'm not worried about stale data. I see that, maybe, in some situation, flush everything that was modified and not explicity saved can be usefull, but it's not my case.
I simply want that, after commiting my session, NHibernate inserts/updates/deletes everything that I have explicitly demanded it to, and evict everything else. My question is: is this behavior just a question of "nobody stopped to implement this yet" or are there another points that would fail if this kind of behavior existed?
Thank you in advance.
Filipe
Nhibernate doesn't think that way. The distinction is between transient and persistent objects, and persistent objects are synchronized with the database when the session is flushed (an possibly at other times). Objects that are retrieved using NH are persistent and will be saved when the session is flushed without calling Save (or SaveOrUpdate) because they are already persistent. There are several options for controlling the FlushMode but nothing that would make it work the way you desire.
A potential workaround might be to retrieve objects using an IStatelessSession and handle operations through a separate ISession.
What problem are you trying to solve?
You are basically asking: "why doesn't my hammer work more like a screwdriver?"
The whole idea of the Session (among other things) is to allow automatic dirty tracking, so you don't need to worry about what was changed; only additions and non-cascaded deletions are manual.
As Jamie mentioned you can use IStateLessSession instead of ISession. It does not track anything automatically and it does not support lazy loading. You have to tell it explicitly what to insert, update and delete. It's more used for read-only and batch contexts, but it's probably what you're looking for,