After using it for a couple of days, I made the following improvements:
- Added Eq() and IdIs() to many-to-one references, so now I can issue this statement:
ICollection<Order> orders = Repository<Order>.FindAll( Where.Order.Customer.IdIs(15) );It seems like we are using this quite a bit in our applications, mainly from query strings. - Added support for VB.Net
- Create a VS.Net Custom Tool, so now you wouldn't have to mess with the command line, just specify "NHibernateQueryGenerator" as the custom tool, and you are done.
- Create an MSI installer for it, so it is really close to zero friction now.
You can get it here.
Okay, it looks like I can track down fairly easily what books I read, when I read Working Effectively with Legacy Code, I wrote Rhino Mocks. Now I am reading Applying Domain-Driven Design and Patterns, and I wrote the NHibernate Query Generator.
So, what is this all about?
NHibernate has two major API for querying objects. The first one is called the query API, and uses HQL, a language similar to SQL but with OOP aspirations. The second is called the criteria API, and it is far simpler API, but it is also much easier to use.
Here is a simple example of using the criteria API using my Repository sample from yesterday:
Now, those of you that already knows me know that I have a passionate dislike to strings. I wrote a mock objects library because of this issue, so I may need physcological therapy in the future, but I hate strings.
Well, what do I do when there is something that I don't like? I fix it.
In this case, there where two main options, run time proxying and code generation.
Run time proxying is similar to what I do in Rhino Mocks, and it may look like this:
ICollection<Customer> customersInLondon = Repository<Customer>.FindAll(Expression.Eq(Expr.Of<Customer>().Name,"London"));
This has the disadvantages of being both ugly and complex, so I turned to the other route. I build a small application which takes an NHibernate mapping file (hbm.xml) file, and output a strongly typed query wrapper. The above query can now be used like this:
ICollection<Customer> customersInLondon = Repository<Customer>.FindAll(
Where.Customer.City.Eq("London") );
This is much better, and of course that you get all the other nice things about the expression API (like Between, In, etc).
You can get it here, binaries only. Source would have to wait for a while, my subversion provider has run out of room. Note that the code it generates relies on a class called NamedExpression. The class is included in the binary, so just add it to your project and you are done.
I also added support for queries, since I didn't want to leave the other side of the API high and dry, it means that you can do this:
ICollection<Customer> customersInLondon = Repository<Customer>.FindAll(
Queries.AllCustomersInCity, new Parameter("city", "London"));
Here is a little FAQ
- What does it generate:
The tool generate strongly typed wrappers for each of the mapped entities and each of their properties. At the moment, is supports querying against properties, many-to-one, and ids. - How to make it work for all the files in a directory?
Simply use the batch capabilities of the shell, like this:
for %f in (*.hbm.xml) DO "NHibernate.Query.Generator.exe" %f D:\QueriesThis will run the tool over all the files in the directory, and output them all to the Queries dir. - How to make it work with ActiveRecord?
You need to use the isDebug="true" flag in the configuration, and then point the tool to the resulting mapping files. - Can I get is as a custom tool for Visual Studio?
It shouldn't be hard to do, but I don't have the time at the moment. Feel free to send me a patch for it when I release the code.
I'm pretty ashamed to admit it, but I managed to foul up IEnumerator<T> for EntityDictionary<T,K>. I was very surprised to realize this, I assure you, I thought that at this point I could be trusted to handle a simple collection delegation, but apperantely this is not the case.
There is a new version which fix this issue, add some better error messages, etc. As usual, you can get it here.
This release fixes a very annoying bug with NHibernate Generics and lazy load exception when the entity ref is set. A huge thanks for Daniel for giving me a much better solution than the workaround that I found.
You can find source and binaries here
Looks like I made an error in EntityList, and ended up calling the wrong delegate on RemoteAt().
This is fixed now, and you can get the updated binary and sources here.
Thanks for Matej for finding out the bug and directing me to the exact line in the code where it happened.
Don't ask about the version number, it just is. I haven't done an all-nighter in quite a while, and while it was fun to do, I'm not thinking very clearly right now. Here is the details about this release:
I finally got around to take a good hard look into NHibernate Query Analyzer (AKA: NQA). I haven't update it in a while, and that is a shame. There was an issue with query translations when using parameters that kept me from going forward for a long time. Today I bit the bullet and trace it through all the twists and turns along the way and found the problem.
Along the way I also solved the Nullables issue that I've not been able to fix since forever. I run into some very strange differences between 1.1 and 2.0 along the way. Up until now, I mostly developed NQA on 1.1, but now I did all the work in 2.0 and the issues highly annoying (see randon GC failure).
The new stuff is:
- Working against NHibernate 1.0.2 (the latest and greatest).
- Supports 2.0 and 1.1
- Supports Nullables
The fun part is that I did all of that without opening the UI once. I'm pretty confident in my tests to be able to do this without stuff breaking. I'm too tired now to think, but in the background there is a gong that warns against temping Merphie, but what the hell.
You can get it here
I'm trying to load this type using Type.GetType(string):
The Nullables.NHiberante is loaded in the assembly, but I keep getting Strong Name Is Required exceptions.
After spending way too much time on this, I figured out that I had added a reference to the NHibernate project, and that the Nullables.NHibernate library was looking for the NHibernate library (this is important, since the NHibernate project is not signed and the library is). I solve this, and I get a strange error about a Nullables.NullableInt32 could not be assigned to Nullables.NullableInt32. It this mismatched version thingie again.
I solve that one, and everything works. It's like magic. I just tested it on a second computer in both .net 2.0 and .net 1.1, it appears to work there as well, so there is something horribly wrong in the horizon and it is closing fast. I've better release something before it arrive.
I just run into this issue where both TestDriven.Net and ReSharper Test Runner doesn't handle this issue. I would get a nasty error message from Windows telling me that this is a rouge application and none of my tests will continue to run.
The issue was that several of the tests were creating chain of objects, and the lower most object was a Component, which mean that it is disposable and finalizable. In the dispose method it called a dipose method on a mock object, but because the mock object was no longer in a valid state when the finalizer was called, an exception was thrown.
The fun part is that when I try to fix this, I find out that any exception from the test will cause the mock object to be invalid state and then the finalizer will kick in and dispose the object, which will call the mock, which will throw...
The really nice part is that running the test one by one will not expose this bug. It seems like the finalizer is not getting a chance to run in this case. But the moment you got several tests, it will kick in and kill the tests. It's just two tests, and to fix this I would need to open up a chain of seams in the objects, so for now I'm just calling GC.WaitForPendingFinalizers(). This at least will crash the tests predictably :-)