NHibernate Proxy Id Value - nhibernate

Seeing a scenario where upon loading an entity by id, NH is loading via proxy (which is expected), however, backing fields for id = 0, but virtual property (getter), is the id of the entity from the db.
Example:
private int _id;
public virtual int Id { get { return _id; } }
and mapping
...
<id name="Id" access="nosetter.camelcase-underscore">
...
Upon loading the entity, the _id = 0, but Id = 4 for example.
Is it normal behaviour for the backing fields to not be initialised while entity is proxied?

I would say: do not worry. There is nothing wrong. And it is normal behaviour.
You can try to create another property, e.g.
public virtual int MyTestId
{
get { return _id + 1; }
}
And you'll see that MyTestId is 5 (to continue the case study above, when the Id == 4). Simply, you've most likely faced the VS debugger... which is just a human.

Related

Fluid NHibernate, Custom Types and Id mapping

I have an object in C# that I want to use as a primary key in a database that auto-increments when new objects are added. The object is basically a wrapper of a ulong value that uses some bits of the value for additional hints. I want to store it as a 'pure' ulong value in a database but I would like get an automatic conversion when the value is loaded / unloaded from DB. IE, apply the 'hint' bits to the value based on the table they come from.
I went on a journey of implementing my own IUserType object based on number of examples I found online ( tons of help on this forum ).
I have an ObjectId class that acts is an object ID
class ObjectIdType: IUserType
{
private static readonly NHibernate.SqlTypes.SqlType[] SQL_TYPES = { NHibernateUtil.UInt64.SqlType };
public NHibernate.SqlTypes.SqlType[] SqlTypes
{
get { return SQL_TYPES; }
}
public Type ReturnedType
{
get { return typeof(ObjectId); }
}
...
}
I have a mapping class that looks like this:
public class ObjectTableMap()
{
Id(x => x.Id)
.Column("instance_id")
.CustomType<ObjectIdType>()
.GeneratedBy.Native();
}
At this point I get an exception at config that Id can only be an integer. I guess that makes sense but I was half expecting that having the custom type implemented, the native ulong database type would take over and work.
I've tried to go down the path of creating a custom generator but its still a bit out of my skill level so I am stumbling though it.
My question is, is it possible for me to accomplish what I am trying to do with the mapping?
I think, it is not possible, because your mapping uses the native generator for the Id. This can only be used for integral types (and GUIDs). You can try to use assigned Ids with your custom type, so you are responsible for assigning the values to your Id property.
There is another alternative: Why not set your information bits on class level, instead depending on your table? Your entities represent the tables, so you should have the same information in your entity classes. Example:
class Entity
{
protected virtual ulong InternalId { get; set; } // Mapped as Id
public virtual ulong Id // This property is not mapped
{
get
{
var retVal = InternalId;
// Flip your hint bits here based on class information
return retVal;
}
}
}
You could also turn InternalId into a public property and make the setter protected.

NHibernate query by Transient instance results in "save the transient instance"-exception

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 ?
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>();
}
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
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?

in fluent-nhibernate,saving a many-to-one entity, why i should give a version to the referenced entity

i have the following entities:
public class Worker
{
public int WorkerID {get;set;}
public string Name { get;set;}
public int version { get;set;}
}
public class TransferOrder
{
public int TransferOrderID { get;set;}
public Worker workerTobeTransfered{get;set;}
public int version { get;set;}
}
and i am using the Auto mapping, in fluent nhibernate.
when i try to save the TransferOrder like this:
TransferOrder order = new TransferOrder();
order.Worker = new Worker(){WorkerID = 1};
Session.Save(order);
but in database, the workerID in the TransferOrder table is NULL???
but when i give a version to the worker, it is saved as normal?
TransferOrder order = new TransferOrder();
order.Worker = new Worker(){WorkerID = 1,Version = 1};
Session.Save(order);
notice that it is not important what version number is given to the worker as long as it is not 0.
and i have a worker saved in the database with workerID = 1.
how can i handle this ? why should i give a version to the worker???is the nhibernate making sure that worker is saved?? and why it should do that ?
Maybe this is the scenerio:
Your Work.WorkerID is an identity column within your Work table and, you cannot insert a row into your table that consists of only an identity column entry.
But when you provide a value for Work.Version, as well, then you are creating a valid insert.
The "version" is probably going to be auto mapped to a NHibernate attribute for optimistic concurrency checking. I would use Fluent NHibernate to generate the mapping XML to see what it's using, as I can't find anything via Google about the default settings for auto mapped .

StaleStateException when changing id in nhibernate

Very new to NHibernate so I expect there will be a quick fix here.
The code I'm looking at has an unorthodox design.
public class Partner
{
public virtual int Id { get; set;} //note the set is not private
public virtual String Name { get; set;}
}
and is mapped with FluentNhibernate's Automappings
When Partner is created its Id is 0 but when
I call Flush to persist to the db:
CurrentSession.SaveOrUpdate(obj);
CurrentSession.Flush();
I get a Unexpected row count: 0; expected: 1 StaleStateException
I'm assuming its because NHibernate doesn't like me changing the Primary Key, Id.
In this case I need to. How can I configure NH to let me achieve this abomination?
You can do the update with HQL:
session.CreateQuery("update Partner set Id = :newId where Id = :oldId")
.SetParameter("newId", newId)
.SetParameter("oldId", oldId)
.ExecuteUpdate();
I suggest that you Evict the entity from the session, or use a stateless one if you are going to update the Id.

NHibernate Mapping with No Id

Is it possible to do a mapping in NHibernate without using an Id?
What I'm trying to do is, call a stored procedure using
session.CreateSqlQuery( myQuery ).AddEntity( typeof( MyEntity ) );
The procedure is an aggregate and there is no Id field being returned, but I would still like to take advantage of NHibernate mapping the data returned into an entity. The only thing I have come up with so far is having the procedure add a field "O as Id" or something, and have the entity have a fake Id field.
Your entity must be "unique" in some way, even if it doesn't have an Id field.
Decide which properties, when taken together, must be unique, and use a composite key over them...
<composite-id>
<key-property name="Property1" column="Column1"/>
<key-property name="Property2" column="Column2"/>
</composite-id>
I found this really isn't possible. The data returned from the stored procedure has to have an Id also. It worked to just create a dummy column with an Id, and a dummy property on the object, but I just decided to convert the data returned by NHibernate to an object by hand.
Basically, you're not supposed to use stored procedures with NHibernate. It IS an ORM afterall. In my case, I had no choice but to use the procedure.
From #Andy McCluggage's post:
You can very much do this in the mapping:
<composite-id>
<key-property name="Property1" column="Column1"/>
<key-property name="Property2" column="Column2"/>
</composite-id>
But, you must override Equals() and GetHashCode() in your model:
public class MyClass
{
public virtual DateTime Property1;
public virtual string Property2;
public virtual int SomeOtherProperty;
public override bool Equals(object obj)
{
if(obj == null)
return false;
var t = obj as MyClass;
if(t == null)
return false;
if (this.Property1 == t.Property1 && this.Property2 == t.Property2)
return true;
return false;
}
public override int GetHashCode()
{
return (this.Property1 + "|" + this.Property2).GetHashCode();
}
}
I need to double-check the xsd, but I believe either id or composite-id are required. However, per the docs, name isn't required. So, you should be able to specify an almost-blank id section.
In a similar situation, I've set the class to mutable="false" and mapped the id to the row index and set the generator in the id mapping to assigned.