Updating an object in nHibernate - nhibernate

I'm quite new to nHibernate, so this'll probably be a pretty stupid question. But anyway, saving an object works fine, updating it doesn't work.
This is what I'm doing:
using (ISession session = _sessionFactory.OpenSession())
{
session.SaveOrUpdate(schemaChange);
schemaChange.ScriptName = "New one";
session.SaveOrUpdate(schemaChange);
}
The first SaveOrUpdate inserts a schemaChange in the database. The second one should update the same object, but nHibernate doesn't do that.
In the output I get:
DEBUG - persistent instance of: DotNetMigrations.Core.Domain.SchemaChange
DEBUG - ignoring persistent instance
DEBUG - object already associated with session: [DotNetMigrations.Core.Domain.SchemaChange#129]
UPDATE
Well, as it was so simple (and a stupid question), I found it myself:
session.SaveOrUpdate(schemaChange);
schemaChange.ScriptName = "New one";
session.Flush();
Makes sence. nHibernate is really superior to any orm tool I've worked with.
What I can't seem to figure out is why there's a SaveOrUpdate method... If you're updating, then isn't the object automatically already associated with the session? (because you got it out of the db in the first way. Unless off course you sent it to for example a Flex RIA and it came back)
So, how do you know if the object is already associated, to either do a SaveOrUpdate() or just a Flush()?

You can use the Method Session.Contains(object) to figure this out.
The difference between Save() and Update() is that Save assigns an Id if necessary, wheather Update() fails if the object has no key or the Key does not exist in database.
SaveOrUpdate fixes some issues regarding working with transient objects, if you don't care what the state of the object is you may call it, but isn't the holy grail at all.
If you know what state the object has i would suggest to call the exact method.
Hopefully this helps a little bit to clarify.

Related

VB.NET How to "Refresh" data DbContext in winform

I have a DataDridview filled with objects queried with my DbContext.
I show some basic informations about my objects in this form.
I would like to query the most recent infos from the BD every time I select a different object in the DataDridview. I also want to be able to modify these objects.
I'm already able to do that. To modify an object, for example, I will do these steps:
-create a new DbContext
-get the object from my Datagridview using .Selectedrows(0).DatabountItem
-with that object's id I will query the (most recent) record in the DB using my new DbContext
-assign the old object (modified) properties to the new ones, one by one
-.SaveChanges on my new Dbcontext.
But there has to a better way, right? :/
If I understand correctly, by doing it this way I end up with a ton of unused Dbcontext and I doubt it's best practices.
But whenever I .Dispose my context, all EF navigation properties are broken and I get exceptions popping all over the place... So the ideal solution seems to me to just, refresh the data in a unique DbContext that I would use for the entire form. But is it even possible?
Maybe the solutions is obvious but I just can't see it.
If I'm unclear, please let me know I'll do my best to reformulate.
Any help would be appreciated, thanks!
** Edit **
In the end, here's the solution that worked best for me:
I do create a new DbContext for every logical operation, then immediatly .Dispose it...
Except for when I show informations about a specific row in my datagridview.
Instead, I create a new Context and leave it open. I will .Dispose only when the datagridview.SelectionChanged event fires.
The reason for not Disposing this context immediatly is: If a user saves his changes, but in the meantime someone else also saved changes on the same record, the (not-synced) context will hit a concurrency issue.. and I can let the user know about it, instead of overriding that row, which would be bad.
If I need these navigations properties from EF elsewhere, I can simply do eager loding by .Include("MyOtherTable") everything I need.( because navigation properties stop working when a context is Disposed)
I would use only one context in this case. You must just pay attention for accesing context from another threads - it can result in transaction errors ("New transaction is not allowed because there are other threads running in the session")
If you use DataGridView with DataSource from EF Context, all you have to do is just reload entities.
I wrote extension method in my context:
public partial class MyContext : testEntities1
{
public void Refresh(IEnumerable entities)
{
var ctx = ((IObjectContextAdapter)this).ObjectContext;
ctx.Refresh(System.Data.Entity.Core.Objects.RefreshMode.StoreWins, entities);
}
}
it is much faster than entry.Reload();
F.E.:
List<MyEntities> items = new List<MyEntities>();
foreach(DataGridViewRow row in dataGridView1.SelectedRows)
{
items.Add((MyEntities)row.DataBoundItem);
}
context.Refresh(items);

In castle active record, why do I have to use CreateAndFlush when my sessionscope should have ended?

I have a very simple asp.net mvc web app which uses castle active record, on top of MySql.
The users of the app want to now define the primary key for one of the entities manually (it was the default of autonumber). No problem I thought, I will simple change the primary key attribute from [PrimaryKey] to [PrimaryKey (PrimaryKeyType.Assigned)] and modify the database schema (okay I know this could be considered a flawed approach but that is not the point of this question)
After trying this, new entities would never get persisted to the database when calling their .Create() method, even though I create a sessionscope per request in OnBeginRequest and OnEndRequest using code identical to here. The same code worked fine before I altered the [Primary Key] attribute.
If I call .CreateAndFlush() instead of Create(), the entities are persisted to the db. I thought the changes in Create() would be persisted when the sessionscope ends. Why aren't they... am I misunderstanding how this should work?
For a generated Key ActiveRecord knows that 0/NULL/{0000-0000-0000-0000}/etc. are the empyt values and that the object is transient and needs to be created. It does not know it the PrimaryKey is Assigened. But you can tell ActiveRecord what the empty value is, just use the following named parameter for your PrimaryKeyAttribute.
[PrimaryKey(PrimaryKeyType.Assigned, UnsavedValue="")]
Greeetings
Juy Juka
Turned out to be pebcak. I had forgotten that I'd written some code which checks that the new primary key does not exist. That code of course needed its own sessionscope. As soon as I did that everything worked, i.e. calling .create would persist the object to the DB.
Two lessons learned from this:
Next time post my code on SO.
Don't make late night code changes that I may otherwise forget about...

How should NHibernate update properties mapped as Version

Using fluent NHibernate I have a property on a class mapped using Version
Version(x => x.Version);
When I save the object, the Version property gets incremented in the database as I would expect, but the value of the property on the object only seems to change sometimes.
using (var tx = session.BeginTransaction())
{
session.Merge(item);
tx.Commit();
item.Version; // Sometimes this is still 1, when I expect it to be 2.
}
The problem is then that if it remains as 1 and I make more changes and save again I get a StaleObjectStateException.
What's weird is that sometimes it works fine and the item.Version value does get correctly incremented, but I can't figure out the difference between the cases where it does and the cases where it doesn't.
I've tried searching but can't seem to find any documentation on this. Can anyone explain what NHibernates expected behaviour is with the Version mapping?
[NHibernate version 2.1.2]
From the ISession.Merge documentation:
Copy the state of the given object onto the persistent object with the same identifier. If there is no persistent instance currently associated with the session, it will be loaded. Return the persistent instance. If the given instance is unsaved, save a copy of and return it as a newly persistent instance. The given instance does not become associated with the session.
So, it will not modify item.
(I might add I have never used Merge in my apps. You might want to review how you are dealing with attached and detached entities)
Did you try
item = session.Merge(item);
tx.Commit();
?
You need to flush the session before the updated version will propagate up to your entities. Unless you flush the session, you are responsible for keeping the entities up to date yourself.
You should TYPICALLY let the session flush on its own when its closed. However, in some instances where you rely on database updates that happen via nhibernate and not settings you make to the entity itself, you might need to flush the session yourself after a commit. In this case be aware that when you flush the session ANY entities that are dirty will be committed. This may not be desirable so be sure that the scope is very limited.

SaveOrUpdate Vs Update and Save in NHibernate

What is the difference between SaveOrUpdate and Save/Update in NHibernate. Why wouldnt you just always use SaveOrUpdate? Also, what is the point of SaveOrUpdateCopy?
Chapter 9 covers all of this better than I can:
http://nhibernate.info/doc/nh/en/index.html
But cliff notes:
Save() takes a new object without an identifier and attaches it to the session. The object will be INSERT'd.
Update() takes an existing object that has an identifier but is not in the session and attaches it to the session. The object will be UPDATE'd.
SaveOrUpdate() looks at the identifier and decides what is necessary in the above.
SaveOrUpdateCopy() is special in that say you have two objects with the same identifier -- one in the session and one not. If you try and update the one not in the session an exception is thrown normally (you are now trying to attach two objects that represent the same persistent object to the session). SaveOrUpdateCopy() copies the non-session object state to the session object state.
I'm not sure how you are going to use NH, but for a lot of cases all you need is Save(). The session is doing ALL of the work necessary to know what has to be updated and simply Flush() or a Commit() does everything you need.

Unloading a data reference in Entity Framework

Currently I am struggling with a Entity Framework issue. I have a wcf service that sits on top of the ef framework, and allows queries to the framework. At some point the user is able to request a file from the framework. The files are referenced by solution entries, so when you request a file from a solution, the reference is loaded to gain access to the file store.
That all works fine, but from that point onward, whenever you do another query that returns that solution entry, the whole file gets attached to the return result. I need some way of detaching or unloading the reference, such that the result entries will only contain an unloaded reference to the file store again.
I have tried to create a new context and query that context to retrieve information from, but when I do that, the entity in the original context is also changed.
I have tried to detach the entity from the original context and then query from the new context. That does not work either.
I have found one way of doing this. For all the non file-download queries, I detach the result entity, and send that over the wire. I am not sure if that is the best way to go about it though.
I hope someone might be able to provide some insight, thanks for the effort.
The issue you are experiencing is probably do to Change Tracking, which is on by default.
Possible Solution:
Disable Change Tracking with MergeOption.NoTracking
using (MyEntities _context = new MyEntities())
{
_context.Widgets.MergeOption = MergeOption.NoTracking;
return _context.Widgets.ToList();
}
This article may help to point you in the right direction on how to handle this issue if the solution above does not work.
I struggled with a similar issue recently. The problem was the result of the context maintaining a reference to the object I was using (obviously). Every time I made a change to an object of the same type, even when obtained with a new context (so I thought), the object was being changed.
With help from one of my peers, we determined the context was hanging around due to the way I was registering it with my IoC container (lifestyle per web request). When I changed the lifestyle to transient (which definitively provided a new instance) then changes to objects of the same type were unaffected.
Hope this helps.