1

I have some old code which is performing a query where a model can be transient. That is, a model with some fields populated from user input, which are then used as part of the query. It worked under NH 2.1.x, but is failing under the latest version.

The exception raised is "object references an unsaved transient instance - save the transient instance before flushing". This happens when NH attempts to perform a query using a non-persisted object as part of the query.

A simplified version to illustrate the problem.

abstract class BaseModel
   public virtual long Id { get; set; }

class Car : BaseModel
    public virtual Engine Engine { get;set; }

class Engine : BaseModel
    public virtual string Kind { get; set; }


public static IList<Car> GetByEngine(Engine eng) {
  ICriteria c = Session.CreateCriteria<Car>();
  c.Add(Expression.Eq("Engine", eng));
  return c.List<Car>(); // <--- Error occurs here
}

And calling code is equivalent to this:

    Engine obj = new Engine { Id = 42 }; // Transient instance
    var x = GetByEngine(obj);

What I expected to happen (Which appears to be the behaviour of the old NHibernate version), is that the Engine passed is used only for getting the Id. That is, generating SQl like select .... from Cars where Engine = 42

But with the new version NHibernate seems to check that the engine used in the Expression is actually persisted.

Is there a way to avoid having to load a persisted Engine before performing the query ?

3 Answers 3

2

yes using Session.Load() which returns the object if already in the session or a lazyLoadingProxy if not present.

public static IList<Car> GetByEngine(Engine eng) {
    ICriteria c = Session.CreateCriteria<Car>();
    c.Add(Expression.Eq("Engine", Session.Load<Engine>(eng.Id)));
    return c.List<Car>();
}
Sign up to request clarification or add additional context in comments.

1 Comment

Sorry for the late reply. This would do the trick, and answers my question in the OP. It solved the issue at hand.
1

You can use the Session.Load method, which exist for this kind of scenarios.
The Load method will return a Proxy to the Entity and won't hit the Data Base untill you access one of it's properties, (except the Primary key property which won't hit the DB at all).

Usage:

Engine obj = session.Load<Engine>(42);
var x = GetByEngine(obj);

check this article about Session.Get and Session.Load

Comments

0

I think you could do something like this:

public static IList<Car> GetByEngine(Engine eng) {
    ICriteria c = Session.CreateCriteria<Car>().CreateCriteria("Engine");
    c.Add(Expression.Eq("Id", eng.Id));
    return c.List<Car>();
}

Anyway... how it's possible that a car with that engine exists if you haven't saved it yet?

1 Comment

The example is contrived because the actual application is too complex to post here. This was a minimum scenario that reproduced the behaviour.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.