NHibernate Guid generator if new - nhibernate

I'd like NHibernate to generate guids for entities only if they are not set manually by the user or application. Basically, when saving objects with new Guid() (all zeros), NHibernate should generate one. When saving an object that has a non-zero Guid, it should use that instead.
Is my only option to write my own generator?
edit Folks, I'm aware of 'assigned'. I should have specified I was aware of it. Since it doesn't do what I want it to do, it's not the option I'm looking for. Writing my own generator is an option that works, but I'd like something else. I'm suspecting there is nothing else.

The problem here is that NH needs to know if the object is new or if it already exists. It does this usually by setting the ID.
If you wrote your own generator, it doesn't solve the problem, because it is only called if the object is new.
You could use the assigned generator.
You could use a version column to indicate if the object is new. I never tried it this way, but it should work. NOT having any indication for NH if the object is new causes quite some troubles. Believe me.
You could also have a integer as primary key and the GUID as regular unique field.
I whould generate the id in the class' constructor
class Entity
{
Guid id;
Entity(Guid id = Guid.Empty)
{
if (id == Guid.Empty) this.id = Guid.NewGuid();
else this.id = id;
}
}

Have you tried setting the unsaved-value attribute?
<id name="Id" column="Id" type="Guid" unsaved-value="00000000-0000-0000-0000-000000000000">
<generator class="guid.comb" />
</id>
edit now I understand your question fully another option instead of rolling out your own generator is to use
<generator class="assigned" />
However you cannot use SaveOrUpdate(). Instead you have to explicitly specify to NHibernate if the object should be saved or updated by calling either the Save() or Update() method of the ISession. Also you will always need to set the GUID manually on all your NEW entities. Its an option.

Related

How to map an NHibernate entity property using both a formula and a column specification

I'm trying to map an entity property in such way that it writes its value to a database column but retrieves its value using a formula.
To focus on the actual issue, I have simplified the example. In reality the formula is a bit more complex and is using an NHibernate filter.
<many-to-one cascade="all" class="Thing" lazy="false" name="MyThing"
formula="(SELECT Things.Value FROM Things WHERE Things.Id = MyThingId)">
<column name="MyThingId" />
</many-to-one>
The formula is ignored however, unless I remove the <column name="MyThingId" /> line.
How would I fix this mapping in order to have NHibernate use the formula?
I don't think it's possible to do exactly what you are trying.
Why not split the property in two? One readonly for the formula and the other read/write with a direct column mapping...
If you still want a single access point, you can map a third ignored propery that implements it's get and set accessors with the two first properties.

nhibernate table per subclass strategy problem when getting objects

I am working on an ASP.NET Web application that uses NHibernate and I have noticed something funny.
So I have an object Document which I would make into an abstract class and 2 concrete implementations Document1 and Document2.
I tried to write the mappings for them applying the table-per-subclass strategy as described in the documentation(link text):
<class name="Document" abstract="true">
<id name="Id">
<generator class="identity"/>
</id>
...
<class>
<joined-subclass
name="Document1"
extends="Document" >
<key column="ParentId"/>
...
</joined-subclass>
<joined-subclass
name="Document2"
extends="Document" >
<key column="ParentId"/>
...
</joined-subclass>
Now this is how I obtain objects from the session in my application:
public TEntity GetById<TEntity>(object id) {
return Session.Get<TEntity>(id);
}
Now my problem is that when I do:
GetById<Document>(1)
for example, I don't get a Document object I get an object of type Document1 or Document2 depending on which type that object is.
I tryed using the table-per-subclass with discriminators strategy as mentioned in the documentation(link above) and I set join=select and lazy=false on the abstract object to get it to return an object of type Document but nothing worked.
The code works but it doesn't seem right. I have a left join where I could not use one.
Isn't there a way to just get the abstract object or does nhibernate actually instantiate the objects it returns which would make this impossible? Is it possible?
I have the feeling that I am getting more info than I need.
You'll never get the abstract class because the whole point of an abstract class is that you can't instantiate one. Besides your code will return only the Document facade to the rest of your code anyway. So I'm not sure what the problem really is.
It is not possible to retrieve an instance of Document1 or Document2 that only populates the properties that are defined in the abstract class. In .NET, you are always working with an instance of a concrete class even if the type is declared as an interface or abstract class.

Why does Id altered from 1 to 1 happen using NHibernate

I have a mapping like this
<class name="UserFileSummary" table="UserFile" lazy="false">
<id name="FileId" column="UserFileId" type="int">
<generator class="identity" />
</id>...
and a property in the c# object like this
public long FileId { get; set; }
What I don't understand is why when I get an instance using
var myFile = session.Get<UserFileSummary>(id)
change a field value and then save it like this
myFile.myProperty = newValue
session.Save(myFile)
I get an error saying the the Id have been altered from 1 to 1. There are some posts around about this, but this is a simple int column (identity 1, 1). I must have made some basic error, please can anyone help out. Thanks
I've added this bit as an edit as the question turns out to be a non question
The FileId property is type long, and the mapping is type int, that is why altered from 1 to 1 is a problem.
Please give me some feedback if you want me to delete this question, thanks everyone :)
The only thing I can point out from your code is the use of Save for updating an existing entity.
You could avoid calling explicitly the Save method for an entity that's alredy present on the session. Your changes will be automatically persisted when you flush the session.
If you insist to explicitly call the update method I'd say you should use SaveOrUpdate.
First, make the setter on the property private; the project will fail to compile if you have any code that mistakenly sets it. This is a good practice anyway. But it's not foolproof because there could be code inside the class that sets it, so you'll have to check for that.
public int FileId { get; private set; }
Second, closely examine the mapping file to make sure that you haven't mapped the field twice.
The error in my case was due to the data type being a long (int64) in the database and being mapped to an int (int32) in the mapping. This meant that the real value changed whenever the object was saved, thus breaking the identifier. Thanks to everyone who helped out.

NHibernate: How to disable virtual for class properties?

how exactly I can use public methods (non-virtual) with NHibernate?
I have this code:
public string crewNumber
{
get
{
return this.crewNumberField;
}
set
{
this.crewNumberField = value;
}
}
Note all my classes, properties, methods and interfaces are auto-generated and I do not want to change them manually.
Above code is producing this error:
The following types may not be used as
proxies: ... method get_crewNumber
should be 'public/protected virtual'
I see that it shold be possible to use simple public only properties here:
In our example above, we've made the
properties and the constructor public
- but that's not a requirement for NHibernate - it can use public,
protected, internal, or even private
properties to persist your data.
How do I turn off this virtual by default?
It's driving me crazy. I am really tempted here to drag one data adapter in visual studio and to end this ridiculous situation once and for all ;-)
Thanks
Specify that dynamic proxies should not be used for that class, by specifying lazy=false on the class-mapping.
Like this:
<class name="MyClass" table="MyTable" lazy="false">
</class>
This means offcourse that you cannot use dynamic proxies with NHibernate.
To be more clear:
- when you retrieve an instance of your class, which is able to use dynamic proxies, you'll recieve an 'empty instance'. That is, NHibernate will not fetch the data from the DB yet. You'll get an object who'se Id will be populated, but the other properties are not. Only when you access a property, then NHibernate will load the data from the DB. That's the reason why the properties need to be virtual, because NHibernate will create a subclass of your class internally, and override the properties so that it can achieve this behaviour.
I always specify 'lazy=false' on my class-mapping, since I don't want to have virtual properties for a reason that is infrastructure-related, instead of 'domain-related'.
(Note that this has nothing to do with lazy loading of associations; it is still possible to have them lazy loaded when you do not use dynamic proxies).
Put lazy="false" at the class mapping:
<class name="MyClass" table="MY_TABLE" lazy="false">

NHibernate - Changing sub-types

How do you go about changing the subtype of a row in NHibernate? For example if I have a Customer entity and a subclass of TierOneCustomer, I have a case where I need to change a Customer to a TierOneCustomer but the TierOneCustomer should have the same Id (PK) as the original Customer entity.
The mapping looks something like this:
<class name="Customer" table="SiteCustomer" discriminator-value="C">
<id name="Id" column="Id" type="Int64">
<generator class="identity" />
</id>
<discriminator column="CustomerType" />
... properties snipped ...
<subclass name="TierOneCustomer" discriminator-value="P">
... more properties ...
</subclass>
</class>
I'm using the one-table per class hierarchy model, so using plain-sql, it'd be just a matter of a sql update of the discriminator (CustomerType) and set the appropriate columns relevant for the type. I can't find the solution in NHibernate, so would appreciate any pointers.
I'm also thinking whether the model is correct considering this use-case, but before I go down that route, I want to make sure doing as described above is actually possible in the first place. If not, I'll almost certainly think about changing the model.
Short answer is yes, you can change the discriminator value for the particular row(s) using native SQL.
However, I don't think NHibernate is intended to work this way, since the discriminator is generally "invisible" to the Java layer, where its value is supposed to be set initially according to the class of the persisted object and never changed.
I recommend looking into a cleaner approach. From the standpoint of the object model, you're trying to convert a superclass object into one of its subclass types while not changing the identity of its persisted instance, and that's where the conflict is (the converted object isn't really supposed to be the same thing). Two alternative approaches are:
Create a new instance of TierOneCustomer based on the information in the original Customer object, then delete the original object. If you were relying on the Customer's Primary Key for retrieval, you'll need to take note of the new PK.
or
Change your approach so the object type (discriminator) doesn't need to change. Instead of relying on a subclass to distinguish TierOneCustomer from Customer, you can use a property that you can modify freely at any time, i.e. Customer.Tier = 1.
Here are some related discussions on the Hibernate Forums that may be of interest:
Can we update the discriminator column in Hibernate
Table-per-Class Problem: Discriminator and Property
Converting a persisted instance into a subclass
You're doing something wrong.
What you are trying to do is to change the type of an object. You can't do that in .NET or in Java. That simply doesn't make sense. An object is of exactly one concrete type, and its concrete type cannot be changed from the time the object is created until the time the object is destroyed (black magic notwithstanding). In order to accomplish what you are trying to do, but with the class hierarchy you laid out, you would have to destroy the customer object which you want to turn into a tier-one customer object, create a new tier-one customer object, and copy all the relevant properties from the customer object to the tier-one customer object. That is how you do it with objects, in object-oriented languages, with your class hierarchy.
Obviously, the class hierarchy you have isn't working for you. You don't destroy customers in real life when they become tier-one customers! So don't do it with objects either. Instead, come up with a class hierarchy that makes sense, given the scenarios you need to implement. Your use scenarios include:
A customer who previously is not tier-one status now becomes tier-one status.
That means you need a class hierarchy which can accurately capture this scenario. As a hint, you should favor composition over inheritance. That means, it may be a better idea to have a property named IsTierOne, or a property named DiscountStrategy, etc., depending on what works best.
The entire purpose of NHibernate (and Hibernate for Java) is to make the database invisible. To allow you to work with objects natively, with the database magically there behind the scenes to make your objects persistent. NHibernate will let you work with the database natively, but that's not the type of scenario which NHibernate is built for.
This is REALLY late, but may be of use to the next person looking to do something similar:
While the other answers are correct that you shouldn't change the discriminator in most cases, you can do it purely within the scope of NH (no native SQL), with some clever use of mapped properties. Here's the gist of it using FluentNH:
public enum CustomerType //not sure it's needed
{
Customer,
TierOneCustomer
}
public class Customer
{
//You should be able to use the Type name instead,
//but I know this enum-based approach works
public virtual CustomerType Type
{
get {return CustomerType.Customer;}
set {} //small code smell; setter exists, no error, but it doesn't do anything.
}
...
}
public class TierOneCustomer:Customer
{
public override CustomerType Type {get {return CustomerType.TierOneCustomer;} set{}}
...
}
public class CustomerMap:ClassMap<Customer>
{
public CustomerMap()
{
...
DiscriminateSubClassesOnColumn<string>("CustomerType");
DiscriminatorValue(CustomerType.Customer.ToString());
//here's the magic; make the discriminator updatable
//"Not.Insert()" is required to prevent the discriminator column
//showing up twice in an insert statement
Map(x => x.Type).Column("CustomerType").Update().Not.Insert();
}
}
public class TierOneCustomerMap:SubclassMap<TierOneCustomer>
{
public CustomerMap()
{
//same idea, different discriminator value
...
DiscriminatorValue(CustomerType.TierOneCustomer.ToString());
...
}
}
The end result is that the discriminator value is specified for inserts, and used to determine the instantiated type on retrieval, but then if a record of a different subtype with the same Id is saved (as if the record was cloned or un-bound from the UI to a new type), the discriminator value is updated on the existing record with that ID as an object property, so that future retrievals of that type are as the new object. The setter is required on the properties because AFAIK NHibernate can't be told that a property is read-only (and thus "write-only" to the DB); in NHibernate's world, if you write something to the DB, why wouldn't you want it back?
I used this pattern recently to allow users to change the basic type of a "tour", which is in reality a set of rules governing the scheduling of the actual "tour" (a single digital "visit" to a client's on-site equipment to ensure it all works properly). While they're all "tour schedules" and need to be collectable in lists/queues etc as such, the different types of schedules require very different data and very different processing, calling for a similar data structure as the OP has. I therefore completely understand the OP's desire to treat a TierOneCustomer in a substantially different way while minimizing the effect at the data layer, so, here ya go.
If you're doing it offline (e.g. in a DB upgrade script), just use SQL and ensure consistency yourself.
If this is something you plan will happen in while the app is running, I think your requirements are wrong, just like keeping the same pointer address for a different object is wrong.
If you save the ID and use it to access the customer again (e.g. in a URL) consider making a new field that contains a token for this that will be the business key. Since it's not the ID, it's easy to create a new entity instance and copy over the token (you'll probably need to remove the token from the old one).