Querying is a business concern
A lot of people moving from hand-built / code-gened DAL to NHibernate seem to encounter the issue of anemic data layer. NHibernate subsume all the responsabilities, leaving the developer with just managing the session and maybe adding a couple of Get/Save methods. It tends to bother some people, who are used to data layers that does quite a bit more.
A more important concern is that you end up putting query logic inside your business objects, and that breaks the way things should happen according to the Ancient Wisdom. As far as I can see it, NHibernate is dealing with the mechanics of querying the database, leaving you to work with the model. When I put it this way, does it make more sense?
If I want to display to the user its next actions, I need to ask: "give me all the users's actions whose state is not completed, ordered by date and then importance". This is a business decision, in my book, and expressed in code, it would turned out like this:
Where.Action.User == CurrentUser &&
Where.Action.Status != ActionStatus.Complete,
OrderBy.Action.Date, OrderBy.Action.Importance
);
You could put this in a method called DataLayer.GetUserNextActions(User user), but I think that when you get to this point, it is getting to be pointless.
There are two cases that I would like to point out as exceptions:
- Very complex queries should be put in a seperate class, I like to use [Entity][Scenario]Finder for those classes, but I don't really think about those as data layer classes.
- I prefer to use named queries over inline HQL. It is mostly left over from the "Don't Put SQL In Your Code", I admit, but I still prefer this.
Comments
I couldn't agree more. How do you blog so much? I can hardly keep up doing my job and reading your blog throughout the day! It doesn't help that you have some other great minds reading and commenting on your posts. =)
I agree with you but when i have set of criteras for search
for example some like this (i use public props in this code for exampe purposes only in read apps i use only access through properties )
class Crit
{
public int? _AccountId;
public int? _CurrencyId;
public string _CompanyName;
public string _TransferPurpose;
public string _CustomerName;
...
}
and i need dynamically generate HQL query depended on fields witch valuse !=null/
Other way is used HQL query for all combinations of fields.
May be exist any other ways but i don't know their. :-(
Why not use the criteria API for this?
This is what it was meant for.
With criteria API as I know I can't create query like this HQL
"select count(e.)
from MyBusinessEntity as e
where [ different search criterias]"
and this query must evaluated on the DBMS server not on
Application Server, using method IQuery.UniqueResult().
With Criteria API I can implement this query like
"CreateCriteria(typeof(MyBusinessEntity)).LIst().Count";
but evaluating this criteria query on my APP server is unacceptable for me bacause I need fetch many instances of class on app server and only after that count all of them;
If you know other way, without fetching instances of classes on APP server, I will be very appreciative you, if you should tell me about this way. :-)
Yes, you can:
CreateCriteria(typeof(MyBusinessEntity))
.SetProjection(Projections.RowCount())
.UniqueResult();
NH 1.2 only, though
Maybe I'm having some difficulty understanding you here, Oren, but I'd disagree with how you've "expressed your business logic in code".
You're 100% right that nHibernate lets you think in terms of the domain, rather than in queries, but the example you've provided is the exact same thing, except you're querying a repository object instead of a database. I'd write it differently myself (the code would look as close to how the user would think about it, so without knowing the exact requirements this won't be perfect):
public class User{
public List<Action> GetIncompleteActions( IComparable c ){
}
public List<Action> Actions{
}
}
Oh! Thanks!!!!!! I will investigate this way! In this time I using NH 1.0.2,
and I can' t use version 1.2 in my application because it's beta version :-( and I and my customer can not use beta's of any frameworks in my applications. I'm very attentively watching for any additionals features in NH 1.2. It's have got many interesting and very useul features for me !
But by the way thanks!! I'm very appreciative to you! :-)
The Repository object is the database, as far as I am concerned.
What you saw eventually transalate to:
select action.Id, action.Name, ... from Actions action
where action.Status != 5243
order by action.Date, action.Importance
</Sigh>
This is political football between the relational and the object oriented crowds. There are times for both, depending on business requirements and supporting technologies used or available. The difference of the paradigms are similar to the difference between expressing something in base 2 and in plain old ascii. Whereas my email may be transmitted in base 2, I would want to write my friend a string of bits. So, relational access is like base 2. It can represent anything, but may take a whole lot of bits (or in relational terms, joins) to encode complex ideas.
Some truisms:
Relational access = just data, no behavior = no intent in stored data = relationships can be made adhoc = more generalized = data can outlive an application or be used in ways unintended
Object access = data + behavior = intent for data = context specific = relationships are domain driven = data can be navigated naturally
The relational folks tend to think that the universe should fit nicely into a RDB schema, and all things should be viewed through the a SQL lens. However, reality is that data is stored in a multitude of organic ways and typically accessed for specific contexts.
That's why, When possible, I prefer coding to the repository philosophy. It's the best of both worlds because it is an abstraction of a datastore that has organic accessibility. It can be retargeted to match whatever the actual storage technology the business chooses, be it relational or otherwise. This is a Good Thing. Take a look at MS's LINQ efforts. LINQ formalizes a generalization/abstraction of querying semantics that gets retargeted to a concrete query expressions for the various storage APIs. BAM! You got peanutbutter in my chocolate! :-)
@Jake,
Why the sigh?
And I don't think that I am even touching about the subjects that you are talking about.
@ Ayende
Wow! I did throw a major wobbly there. I misread an earlier comment and overreacted! Never mind... and thanks for deflating my rant! ;-)
Just to get this out of the way, here is my recap from the MVP summit. You will find something to argue
I am swayed by your point and can see how querying could fit naturally in the domain.However, I do have questions regarding how it can be done if using TDD (or anything similar).
How would you unit test/develop an individual domain class if you are using the query criteria inside of it ? If we were using your NHQG would we mock the query objects themselves or leave their concrete dependencies in them?
Also, how do you validate the data results itself from the database request if the criteria logic is outside of the data access layer (Repository)? Would you even need to? Would we use integration tests later instead?
@Vega,
Testing that the criteria is valid / data is valid - usually tested either via in memory database or using integration tests. I like using in memory db for this, or maybe a prepared set of data.
I don't feel that mocking gives me much here, because I want to test that the final query is correct, no just its construction.
Comment preview