Real World NHibernate Screen Cast: Planning
Did you noticed? Most of the guidance about tools are about how to get you started, but much more light on how to get going in the face of real world scenarios. NHibernate is no exception in this regard, so you can find a lot of information about getting started, but far less on how to actually deal with it.
So this is a official solicitation of interesting NHibernate issues that you have run into. When I first thought about it, I figured out that querying was where most of the interest lay (that is certainly where I mostly sit back and say "I can't believe I just did that so easily"), but mapping issues, or dealing with general problems are also welcomed.
A question should include the problem statement and the scenario and a description of the entities / database in question, if they are relevant. I may contact you for more information regarding this problem.
Here is an example of a question that I can't really do much about:
How can I make NHibernate skip persisting unsaved entities?
This is problematic because you have already arrived to the conclusion of what you want to do, but I have no idea what you are trying to do, or why. Often, there are other ways to do the same thing, but without the scenario, I can't tell. A better way to ask this question is:
We have a "Simulation Mode" in our applications, in which a user can do things to the application without really affecting the system, for trying things out. We want to use the same code as we use for the real thing, but we must not save to the database, obviously. How can I stop NHibernate stop persisting my entities?
This gives me enough background to know to tell you to go to set session.FlushMode to FlushMode.Never.
So, post those problems here, and I'll try to give coherent solutions to them.
Comments
Here is one:
In our application we have a requirement to add a MAC to information in the database. The MAC is calculated by a Hardware Security Module (HSM), so this ensures that an attacker with access to the database, but not to the HSM, is unable to add or change information undetected (although this will not prevent him from deletion information).
In other applications (based on another ORM) we have been able to hook into the ORM right before it generates the actual INSERT or UPDATE and calculate a MAC for each row based on the actual values in each cell.
The solution we have implemented with NHibernate is to implement IInterceptor and hook into OnSave and OnFlushDirty (and OnLoad for verifying the MAC). (It was loosely based on http://www.hibernate.org/164.html)
Unfortunately this solution has not proven particularly robust.
Most of the problems we have had have been related to the MAC being calculated over an entity instead of over a row.
For instance, one entity contains a list of objects. Think Order/OrderLine. We calculate the MAC on the values of "Order" combined with the values of all the "OrderLine"s. But if we only change a value in one of the "OrderLine"s, OnFlushDirty will of course not be called on the parent "Order", which means that the MAC will not be updated. So we have had to jump through all sorts of hoops to make this work.
How would you have chosen to solve this?
In my monster application (400000+) lines of code. I use nHibernate and the attributes for mapping. I have a hundred+ domain objects wich are decorated with the attributes all works well.
This is a windowsforms application written in VB.NET 2005 framework 2.0 with nHibernate 1.2.1 (modified) DAL is on the clientside.
Except ;-) (you asked for it)
-Then I made a single mapping file from the attributes at designtime and loaded it at runtime but still it takes 111 seconds to load and that is the fastest I could get it.
How can this be solved. Performance can also be an issue.
Visibility on Data
Let's you want to handle 'visibility' on data in big organizations where there are many departments. Each department has workers with different roles. Each role has the right to see only a subset of the data stored for whole organization.
A way to solve this problem is to store records in an additional table to define which role has access to which data, e.g. if you have an Invoice entity stored in an INVOICE table, and you want to handle visibility on this entity, you can store visibility rules in an additional INVOICE_VR table.
It means that, when you retrieve Invoice entities, you will have to check that additional table to 'filter out' data that the user cannot see, depending on its roles. In SQL, it means e.g. using an 'exists' and a sub-select.
With NHibernate, you can declare a filter for each entity on which you want to handle (filter-def) and reference it (filter) in the <class> mapping and collection mappings of referencing entities. Then at runtime, simply enable the filter on the session, set any arguments, and data will be 'filtered' transparently. :)
Simple & beautiful thanks to NHibernate ! ;o)
Sorry but it was 11 seconds
So
Except ;-) (you asked for it)
-Then I made a single mapping file from the attributes at designtime and loaded it at runtime but still it takes 11 seconds to load and that is the fastest I could get it.
We are considering switching our tabbed MDI WinForms application (looks kinda like the Visual Studio IDE) to use NHibernate as part of a database / data layer revamp. The existing UI doesn't do a lot (mostly just data entry validation), but it does display a LOT of data on the various forms. The business logic for calculations is contained in the database triggers (and the stored procedures they call), and updating one row in a table can have ramifications for other tables through these triggers.
Several questions (I'm sure I'll have 13 million later on if we proceed):
What is the best NHibernate session-management mechanism for an MDI WinForms application?
Another session-management related question: each MDI form displays header-level information (the parent object) has multiple panels to display details (collections of child objects), and I would like to take advantage of lazy-loading for the parent's collections of child objects as each panel is displayed the first time (if / when the user clicks to view the panel). Don't I have to keep the session that loaded the parent object alive so that the child collections can be lazy-loaded? Should I use one NHibernate session per MDI form that stays alive as long as the form is open? What is the best way to do this?
Yet another session-management and lazy-loading question: when the user clicks the "Save" button on one of the MDI forms, the parent object (and all of the collections of child objects that it owns) should be flushed to the database. However, if an exception occurs during this (for example, referential integrity in the database prevents Child Object A from being deleted since it is referenced by Object X from another database table), I am supposed to consider the session as invalid (from the NHibernate docs: http://www.hibernate.org/hib_docs/nhibernate/1.2/reference/en/html_single/#manipulatingdata-exceptions). Should I create a new session? Can that new session be re-attached in some way so that lazy-loading will still work for the child object collections? Tell the user that NONE of their changes can be saved? Reload all objects from this session using a new session's Refresh() method?
[This list is getting too long for the Comments field, Part II of my questions coming soon]
[Part II of David Mc's questions]
If I save one of my business objects using NHibernate, the database triggers fire and calculations start occuring. what is the best way to handle the changes that may have occurred to other business objects (which may or may not be loaded/displayed at this time)? A simple example: a user changes a value on Object A displayed on form A, and when it is saved the database trigger may cause the object displayed by MDI form B to need to be updated because the database trigger modified something related to this object?
In addition to calculations, auditing is currently done at the database table level using triggers. Is this better done using NHibernate? Should the database trigger logic be moved into our business objects? A downside to us doing this is that we have batch import processes that would need to be modifed to use our business objects instead of calling stored procedures to insert/update data in our database.
Enforcing uniqueness is done at the database table level with constraints (for example, you can't create more than one object named X). This will often happen at the child object level, where you can't have two child objects that have the same combination of fields. What is the best way to handle this in NHibernate: do I wait until I Save() the object and catch an exception if there's a problem? do I duplicate the database constraint's logic in my NHibernate objects?
Not a question, but a technique that worked great and helped me out a lot: Damon Carr's article "NHibernate Custom Collections Supporting Lazy Loading" (http://damon.agilefactor.com/2007/07/nhibernate-custom-collections.html).
Thanks for the invitation to submit questions! I'm sure I can come up with other questions, please email me if you need clarification or want more questions!
This is a great idea. Perhaps it might be worth considering a spin off site / forum for this type of thing? Anyway here's my problem.
Consider an Order domain object. When the Order is first created and after all subsequent changes OrderHistory also needs to be persisted. The history is a mirror of the Order with some additional attributes - OriginalID, ChangedBy and ChangeTime. In the history object the ID of the original Order needs to be mapped to the OriginalID (the history object has its own identity). The same pattern needs to be applied to other (not all) objects in the domain. All domain objects inherit from a abstract base class. We are using NHibernate for persistence.
As you can see there is a lot of overlap between the current and history entities (at the domain, mapping and even database levels). In addition for each object there is (currently!) a consistent mapping between the domain and history objects.
I can make this work but I can't do this elegantly i.e. I want to write a minimal amount of code for each new object that requires history. My inclination was to use inheritance and / or generics. I have tried multiple approaches but I keep running into issues (I don't want to outline them here because this can narrow the discussion).
How would you solve this problem?
I like to see a how other people deal with Row-level security for multiple entity-types (especially when a single role can hold users and (nested) roles )
NHibernate and replicated database.
With a setup that uses a version column in the mapping files, NHibernate interprets a row count that does not equal 1 to be an optimistic concurrency control failure; with MS SQL replication triggers in play the rows affected will be greater than 1 and hence NHiberante throws a concurrency control exception.
The only workaround that's been suggested is to manually alter all the replication triggers for a database. I haven't investigated how feasible it is to automate this, but it seems a big hack. What would be an elegant way to resolve this problem?
In one of my current projects I deal with a database consisting of several hundred entities. Some of those entities and their relation among each other may change significantly during the developement process. I use NHibernate and Active Record as well as NHQG with great success. What I'm missing most is a tool which allows me for a visual representation of my (some times) complex relations between the entities. I've worked with Active Writer for a while but it's too limited for my scenarios. Also I don't like that it (re-)generates the code and is not only a tool giving me a visual representation of existing mappings (analogous to the class designer in VS). What do YOU do in such scenarions where you have to deal with several hundered different entities?
I am also dealing with the same requirements as Jason (History). Some of my entites have to be historized (who, when, what). What is the most elegant approach to this?
Comment preview