It looks like you've got a number of issues to tackle here! I'm probably not the most experienced person to ask but might be able to point you in the right direction...
Problem 1) Selecting a layering strategy for business objects
eavonius wrote:
I'm working on creating an object-oriented class library that will be used as a public API for our customers into a portal product. I have used other ORM tools before to try to do this, and always end up having two classes for each "entity" in the system - one that is the one the ORM tool creates, and one that wraps this with a public interface to hide the ORM details. I would prefer that our customers not have to make direct ORM calls to save/update etc. if possible.
Having tried both, I prefer to have one class per entity. Generating base classes + wrappers/extenders didn't deliver me much value when I tried it in a couple of projects. However, I'm guessing this is different for your case? What about having separate "Repository" or "Manager" objects for handling saving/loading, so that the business objects themselves aren't burdened with this responsibility? NHibernate is very good this kind of thing I think. For our projects I tend not to mind exposing *some* ORM concepts in the business layer where it makes life easy (usually that means just exposing a database identifier).
Problem 2) Where to use the 2nd Level Cache
eavonius wrote:
I could create an object, say "BicycleManager" that I could ask for the "Bicycles" property of and get a collection of Bicycles back. I know I can use a query to get these objects and then return them. If the same session (ASP.NET request or application thread) calls this Bicycles property twice, should I implement my own cacheing of the results of this call, or should I use the second-level cache feature and always make this query regardless of whether it has been called once or not?
From what I recall, the "Hibernate in Action" book suggests using the 2nd level cache very carefully, and gives an overview of where it's good to use it and where it isn't. My foggy memory noted that "I'm only gonna use this if I really feel some performance pain!".
Problem 3. Handling Concurrency
eavonius wrote:
Now lets say one user lists the bicycles and gets 4 back in a list in their UI. Then another user deletes one of these bicycles. Then the first user selects the bicycle that was deleted and goes to edit it. Is he going to not have any concurrency exceptions thrown until he commits changes, because his session has a separate "copy" of the collection of bicycles? This seems problematic because now a user wont know something is wrong until he goes to save, instead of just telling him "the object you selected no longer exists" by trying to find the bicycle that is now gone in the collection when the user goes to edit it after the first user has deleted it.
This is tricky. Have you looked at many books that discuss issues in concurrency & locking? I've been lucky/unlucky enough to work on smaller systems that don't have such high concurrency, but I know it can be a real ball ache. I'd research your various optimistic/pessimistic locking approaches. Just as one example, the "Domain Driven Design" book has a section on this with regard to identifying what constitutes a "whole" entity that must be locked at once.
eavonius wrote:
Basically I want to come up with a consistent strategy for handling concurrency issues without just ending every user process with an exception handler that has to recover from a ton of concurrency issues.
Again, the "Domain Driven Design" has some pointers on this. Even so, I think you still have to do lots of hard work to understand where contention points are in your system. From a performance perspective you want to be locking as late and little as possible, so a single, defensive approach might not be so helpful.
eavonius wrote:
The documentation explains the methods available, but I am afraid it is hard to tell which of these to use. I really need to find a way to also hide this from my API users and thrown my own simpler exceptions somehow so they dont have to be burdened with so many NHibernate details.
You could have a read of the Java Hibernate manual, this is quite verbose with lots of examples. See
www.hibernate.org. Not all applies to NHibernate, but I found it very useful for getting a feel for how the API should be used. I haven't checked the docs here recently, but I'm sure they've been growing!
I think you're right to want to protect users from NHibernate, they shouldn't have to be aware of it at all really. I guess you know what your customers need, but I'd consider something like:
public class DataAccessException : Exception{ ... } //load/save errors
public class BusinessLogicException : Exception{ ... } //business rule failure
You could also use a logging framework to allow access to the real meat of the problems that you don't expose to API users.
Just my 2 pence worth, hope some of it's helpful!
Tobin