Do I have to Load/Get an entity before SaveOrUpdate in Nhibernate?
Asked Answered
A

2

6

in my ASP.NET MVC application I've separated the domain model from the view model.
I transform my entity in a viewmodel object so I can "feed" my views with only the data needed (I used valueinjecter for this purpose).
During the save process my controller gets back the viewmodel object, transforms it into a domain model entity and try to persist it with SaveOrUpdate. I've noticed that if I try to update an existing record, Nhibernate considers it as a new object and forces an INSERT, even if my entity has a proper ID.
I haven't loaded (Get/Load) the entity before cause I would like to avoid to re-map all the fields again.
Is there anything I am doing wrong? What's the best way to achieve that?

***** - UPDATE - ***

My controller receives a User (ViewModel), validates it and tries to save it through a service layer as an entity:

public ActionResult Edit(Guid id, Models.User User)
{
...
var user = new Domain.User();
user.InjectFrom(User);
user.SetId(id);

user = this._SecurityService.SaveUser(user);

}

This is the service:

public Domain.User SaveUser(Domain.User User)
        {
            bool Result = false;

            if (this._ValidationEngine.IsValid(User))
            {
                using (_UnitOfWork)
                {
                    if (User.Code != Guid.Empty)
                    {
                        var user = this._UserRepository.Load(User.Code);
                        user.InjectFrom(User);
                        User = this._UserRepository.Update(user);
                    }
                    else {
                        User = this._UserRepository.Save(User);
                    }
                    Result = _UnitOfWork.Commit();
                }
            }
            return (User);
        }

I've got a concern about the fact that I have to transform my viewmodel/entity so many times. Now when I try to save a new User I got this message: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)
Probably this is in some ways related to what Darin was telling me.
Are there any better ways to do what I am trying to do?

UPDATE

I've noticed that the error "Row was updated or deleted..." is caused by the fact that I have a version defined for my mappings. What I can understand is I need to have the Id and the version matching the entity I want to update.
I would like to understand how other people do these things with DDD + ASP.NET MVC + NHibernate ???

SOLUTION

All my entities have a version defined (I forgot to say, sorry):

<version name="Version" type="System.Int32" unsaved-value="0">
   <column name="Version" not-null="true" />
</version>

As Ayende explained here, Nhibernate tries to update my entity with a query like this:

UPDATE Table SET field1 = 'bla', Version = (y+1) WHERE id = x AND Version = y

where y should be the version of my entity. Since I wasn't populating my entity with a version, it would generate an exception StaleObjectException.
I've added a version field to my viewmodel. I save it in a hidden field of my view together with the id.
My controller now receives a viewmodel with an Id and a version. I inject these fields in my domain entity and let the repository save it (I don't reload my entity):

User = this._UserRepository.Update(user);

If I get a StaleObjectException exception it means that someone else has updated the DB row and I'll provide the user with some feedback.

Alexisaley answered 26/1, 2011 at 12:47 Comment(2)
try loading the entity and after re-map it (I know you don't want it this way), just try this and tell us if it works this wayThistly
I guess my problem is with the version.I've got <version name="Version" type="System.Int32 unsaved-value="0"> <column name="Version" not-null="true" /> </version> defined in my mapping. I guess I have to find a better way to do this.Alexisaley
V
0

Since you already have an Id and, therefore, know this is an update, use session.Update instead of SaveOrUpdate.

Verenaverene answered 26/1, 2011 at 14:51 Comment(3)
I am afraid I can't do that cause if I try to save my entity I get this message: a different object with the same identifier value was already associated with the session: caedb8a4-abce-4c6f-b171-9e7200cc7a37, of entity: BpReminders.Domain.UserAlexisaley
That means the object is already loaded. Why aren't you updating the already-loaded object then?Verenaverene
sorry that was a mistake. What I am doing now is loading an entity from the repository and merging it with the one obtained from the controller. I still get an error "Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)" when I try to insert. I'll update my original question with more infos.Alexisaley
G
4

For SaveOrUpdate to work as expected you need to explicitly specify the unsaved-value attribute on your <id> mapping. So for example you would have:

<id unsaved-value="0" ...

and then issue a SaveOrUpdate call on a model which has the Id property assigned to a value different than 0 and it is going to UPDATE instead of INSERT. So to answer your question, no, you don't need to manually issue a SELECT before UPDATE. SaveOrUpdate should do this behind the scenes.

Glassco answered 26/1, 2011 at 12:53 Comment(3)
Thanks Daring. I guess that I have to use an empty Guid (00000000-0000-0000-0000-000000000000) if my id is type Guid, right?Alexisaley
@vandalo, try this: unsaved-value="00000000-0000-0000-0000-000000000000" (untested).Glassco
Thanks, again. What would you do, anyway? Should I rather load the entity from the DB and populate each field with the new entity in case I am trying to update? Are there any best-practices? Thanks again for your help.Alexisaley
V
0

Since you already have an Id and, therefore, know this is an update, use session.Update instead of SaveOrUpdate.

Verenaverene answered 26/1, 2011 at 14:51 Comment(3)
I am afraid I can't do that cause if I try to save my entity I get this message: a different object with the same identifier value was already associated with the session: caedb8a4-abce-4c6f-b171-9e7200cc7a37, of entity: BpReminders.Domain.UserAlexisaley
That means the object is already loaded. Why aren't you updating the already-loaded object then?Verenaverene
sorry that was a mistake. What I am doing now is loading an entity from the repository and merging it with the one obtained from the controller. I still get an error "Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)" when I try to insert. I'll update my original question with more infos.Alexisaley

© 2022 - 2024 — McMap. All rights reserved.