How do I get NHibernate to save an entity if I assign it an ID, but generate one otherwise? - nhibernate

According to the REST philosophy, a PUT request should update the resource at a URL if it exists, and create it if it doesn't exist. So in other words, if I use the following URL:
PUT http://server/item/5
If an Item exists with an ID of 5, it will be updated. If an Item doesn't exist with an ID of 5, a new Item will be created with an ID of 5.
However, I'm using NHibernate for persistence, and I mapped my IDs as Identity. This means that no matter what value I assign the ID, NHibernate will replace it with its own when I save a new Item.
How do I get NHibernate to save an Item with the ID that I assign it, without changing the ID mapping to Assigned?

If you use Identity, the DB won't allow you to enter a value.
That said, if your DB has some special syntax to allow inserting with explicit values in Identity fields, you can implement your own generator, which I guarantee will be error prone, hard to debug, and not very useful. But it's possible.
Study everything in https://nhibernate.svn.sourceforge.net/svnroot/nhibernate/trunk/nhibernate/src/NHibernate/Id and start creating your Frankenstein :-)

Related

Get a hash of old attribute values from rails active record

I want to generically access the old attributes that have changed in a model - that is, I want to get a hash of the old attributes values. My code is interested in all attributes that have changed, which may be a different set each time it's run.
I know you can get an array of changed attribute names with
model.changed
and I know you can do
model.attribute_was
to get the old value of an attribute if you know the name, but I can't find a way to programatically combine the two or to otherwise get the set of old values
I'm using it to create news stories about objects, eg
User 'Bob' changed x from a to b
You can use the attributes hash to generate this array:
old = model.changed.map{|attr| model.send("#{attr}_was".to_sym) }

NHibernate: If two calls are made to CreateCriteria, which list is Get<T> going to retrieve the object from?

Under one UnitOfWork (session) I may call CreateCriteria twice. My first call is to populate a grid for data editing. Let's say the data has been edited and flushed (saved) to the database and the grid is still open. After the data is edited, I may call CreateCriteria a second time to retrieve a list of objects that are validated and found in error.
Lets say ObjectA was retrieved by both calls to session.CreateCriteria. It was edited in the grid but found in error within the second list.
The first question would be: Considering first level cache, is ObjectA--that was retrieved from the second call to CreateCriteri--represent the one retrieved from the first call? or, better yet, did NHibernate "detect and reuse" ObjectA from the first call assuming the keys did not change?
To my final point in question: I want to edit ObjectA which was found in error, and let's say it was brought up in a ListBox. Therefore, I want to highlight that object, call session.Get()(key) in order to retrieve it from cache, then bring up a change form to change ObjectA's properties. Which object am I changing? The one from the first call to CreateCriteria or the second call? Are they the same?
Thank you in advance.
Second level cache
Take a look at http://ayende.com/Blog/archive/2006/07/24/DeepDivingIntoNHibernateTheSecondLevelCache.aspx and http://www.javalobby.org/java/forums/t48846.html
From the former:
The second level cache does not hold
entities, but collections of values
So, with caching setup properly, NHibernate will be able to recreate your object without having to get the actual values from the database. In other words, the object will be created the same as when it wasn't in cache, except that since the values are cached, NHibernate won't actually query the database since it already knows what's in there.
I'm not quite sure what you mean by "validation" and "found in error". Are you validating before insert? Typically my entities are validated before the insert/update and won't actually be inserted/updated if invalid.
Validation aside, what I think you're asking is that if you:
save something
do a flush
retrieve an item (from a new session) with the same key as the one saved in step 1
will you be retrieving the same reference to the object you saved in step 1(?). And the answer is no since NHibernate does not cache the OBJECT but rather the values so it can create a new entity populated with the cached values (instead of actually performing a DB query).
However, does that really matter? If you overload Equals such that equality of 2 entities are based on their ID, then finding the same (not reference equal, but same) item in a grid (or a hash of any kind) should be a snap.
First level cache
I didn't realize you were talking about 1st level cache. 1st level cache works as an identity map and does cache the instance of the object. Therefore, if you do 2 selects from the db based on the same ID, you will retrieve the same instance of the object.

NHibernate update reference

Entities
We have an entity called Product which is loaded using NHibernate.
Product has a category which NHibernate happily populates for me.
Database
In the database, Product has a foreign key for category.
Scenario
User edits this Product (via a web interface) and chooses a different category (say instead of "Fish" we select "Veg").
This is probably a dropdown list, with each category shown. When they choose a different category we get an int key.
Problem
Obviously we now want to save the changes to Product but in effect the only change is to save a new int (say 2, instead of 1).
So we retrieve the existing Product, and now comes the problem.
We don't have a "CategoryID" field on Product, we only have a Category property.
But we don't really want to retrieve the category (by id) just to assign it to the Product.
So I guess what I want to know is should we...
a) Add a CategoryID property to Product
b) Create a new category, assign it the relevant id and attach that to Product (but surely that will cause errors, or overwrite the existing category)
c) Retrieve (lookup) the category from the system (by id) and attach that to the Product
d) Do something else entirely!
It looks like you might be able to using the Session.Load(id) functionality.
Session.Load is a special method that returns a proxy with the ID until you request another property at which point it loads. It throws an error if there is no item matching the ID. Try something like:
product.Category = Session.Load<Category>(2); //2 being the new category ID
Session.SaveOrUpdate(product);
I just did a little testing and it did not seem to pull back the entire Category.
Updated: Session.Load is the correct answer
product.Category = session.Load<Category>(2);
session.Save(product);
Use NH's EnumStringType<T> to map your Category as an enum to the respective database value (which can be a string or a number). You'll find quite a few usage examples, if you google for it.
HTH!

In a datacontext are inserted values not available within the datacontext until after submitchanges?

I'm going through an XML file of articles and the journalist(s) that wrote them. As we are adding the articles into _Data our datacontext we may come across a journalist that needs adding so we do this:
newJourno = New journalist With {.name = strJournalist}
_Data.journalists.InsertOnSubmit(newJourno)
.articles_journalists.Add(New articles_journalist With {.id_journalist = newJourno.id, .id_article = .id})
However subsequently we may come across this same journalist again and nothing is returned when we do this:
Dim journo = _Data.journalists.Where(Function(s) s.name = strJournalist).SingleOrDefault
So it uses the code above again to insert the same journalist again.
Once all of our inserts are done we do a submitchanges. At this point it has a head fit:
INSERT statement conflicted with COLUMN FOREIGN KEY constraint 'FK_articles_journalists_journalists'. The conflict occurred in database 'blah', table 'journalists', column 'id'. The statement has been terminated.
From looking through the sql generated in sql profiler you can see that it is trying to add some journalists more than once, this will fail as the name must be distinct. The subsequent records that are trying to be inserted with these journalists are failing as the journalist wasn't updated.
Surely if I have a collection of journalists, add some to it and then look in my collection I should see all of them and not just the original ones. I can fudge it I guess by doing a submitchanges but that seems a bit silly.
Thanks in advance,
Dave.
If you want to add two child-parent rows to the database, you must assign the entity, instead of the Id column, the Id will be autogenerated and will be available only after the submit changes.
You have to do a articles_journalist object, and then assign the newJourno entity to this:
articles_journalist.journalist = newJourno;
CMS is right about needing to assign the object, not the id.
However this doesn't seem to get around the problem of the datacontext not realising that it has had new stuff added to it until you submitchanges. I can only presume this is by design and therefore I am now calling submitchanges as and when the code inserts objects that we later search for.
"the name must be distinct."
This is a serious design flaw. Person names are never unique.

Adding more promoted properties after a first one in BizTalk

I've got a BizTalk solution where I added a schema, and promoted a field
- this added the default PropertySchema.xsd with the element in
A bit later, added another schema, but when I try and promote a couple of fields, but when I select Show Promotions, I end up mapping the new field to one of the existing fields in the PropertySchema
My bad ... I should have used the Quick Promote
You can manually add items to the property schema as you would with correlation and then associate the promoted property.