1
votes

I'm trying to use a session-per-request pattern and I'm having trouble with getting a record right after it's been saved. The reason for doing that being that I need to get the records that the foreign keys relate to.

Some (simplified) code:

    // UnitOfWork Attribute

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        SessionFactory.Begin();
    }

    public override void OnResultExecuted(ResultExecutedContext filterContext)
    {
        if (filterContext.Exception == null) {
            try {
                SessionFactory.Commit();
            } catch {
                SessionFactory.Rollback();
                throw;
            }
        } else {
            SessionFactory.Rollback();
            throw filterContext.Exception;
        }
    }

    // Service layer

    public void Save(Form form)
    {
        _repository.Save(form);

        var savedForm = _repository.Get(form.Id);
        SendEmail(savedForm);
    }

    // Repository

    public void Save(Form form)
    {
        var session = SessionFactory.CurrentSession;

        session.SaveOrUpdate(form);
    }

The problem is that when I try to get the record, the transaction hasn't yet been committed, so it just gives me what's already in the session. Am I just going to have to commit the transaction after saving, and open a new transaction for getting it?

Thanks

Update:

I've implemented the Agathas Storefront way of doing things, giving the service layer control over the transactions, i.e.:

public class UnitOfWork : IUnitOfWork
{
    public void Commit()
    {
        var session = SessionFactory.CurrentSession;

        using (ITransaction transaction = session.BeginTransaction())
        {
            try { 
                transaction.Commit(); 
            } catch {
                transaction.Rollback();
                throw;
            }
        }
    }


    public void Clear()
    {
        var session = SessionFactory.CurrentSession;

        session.Clear();
    }
}

Then in the service layer:

public void SaveForm(Form form)
{
    _repository.Save(form);

    _uow.Commit();
    _uow.Clear();

    var savedForm = _repository.Get(form.Id);
    SendEmail(savedForm);
}

Update 2

OK, I think I've found a suitable solution. I've gone back to the transaction-per-request pattern, and after saving the form I'm now flushing it and then evicting the form from the session in order to force NH to get it from the DB.

// Service layer
public void SaveForm(Form form)
{
    _repository.Save(form);

    var savedForm = _repository.Get(form.Id);
    SendEmail(savedForm);
}

// Repository

public void Save(Form form)
{
    var session = SessionFactory.CurrentSession;

    session.SaveOrUpdate(form);
    session.Flush();
    session.Evict(form);
}
2
Are you column ID's identity columns? - Rippo
I am, but that's wasn't really my main issue (even though it would still be an issue). My main issue was not being able to load the foreign key records. Perhaps lazy loading would have dealt with that though? - Tom
It is your issue, Identities are you enemy especially when you use a unit of work. How does the entity get its ID if has not been sent to the database first? - Rippo
I understand what you're saying, however as I said to Fourth, this is a project I'm converting to NH, so changing how I generate IDs now would be a bad idea. Anyway, I've posted a second update which resolves this issue. - Tom
I am not saying you should change nor have I ever said you should, I am just saying that identities are not so good for uow implementations. - Rippo

2 Answers

1
votes

Until you flush the session or reduce your transaction scope you won't have an ID because nhibernate will not have inserted the record.

1
votes

One option, change your id strategy from an identity generated in the database to one that is managed by NHibernate (see options at http://nhibernate.info/doc/nh/en/index.html#mapping-declaration-id-generator) or another option change your Session's flushmode to FlushMode.Auto.