views:

192

answers:

1

I have a legacy database with a pretty evil design that I need to write some applications for. I am not allowed to touch the database design at all, seeing how this is a fragile old system held together by spit and prayers. I am of course very aware that this is not how the database should have been designed in the first place, but real life some times gets in the way..

For my new application I am using NHibernate (with Fluent for mappings and NHibernate LINQ for querying) and trying to Do Things Right. So there is IoC and repositories and more interfaces than I can count. However, the DB structure is giving me some headaches.

The system is very much focused around the concept of customers, and each customer lives in a campaign. These campaigns are created by one of the old applications. Each campaign in the system is defined in a table called CampaignSettings. One of the columns of this table is simply a text column called "Table", which refers to a database table that is created at the same time as the campaign entry in CampaignSettings. The name of this table is related to the name of the campaign, which can pretty much be anything the customer wants (within the constraints given by SQL Server (2000 or 2005)). In these tables the customers live.

So that is challenge #1 - I won't know the table names until runtime. And it will change from site to site - no static mapping I guess.

To make it even worse, we have challenge #2 - this campaign table is also dynamic in structure, meaning it has a certain number of columns that are always there (customer id, name, phone number, email address and other housekeeping stuff), and then there are two other sets of columns, added depending on the requirements of the customer on a case-by-case basis.

The old applications use SQL to get the column names present in the table, then add the ones it doesn't know about as "custom fields" in the application. I need to handle this.

I know I probably can't handle these challenges simply by using mapping magic, and I am prepared to do some ugly SQL in addition to the ORM goodness that I get from NHibernate (there are 20-some "static" tables in here as well which NHibernate handles beautifully) - but how?

I will create a Customer entity that I guess I can populate manually by doing direct SQL like

SELECT * FROM SomeCampaignTable WHERE id=<?>

and then going through the columns one by one and putting stuff where it belongs. Not fun, but necessary.

And then I guess to discover the structure of the table in the first place, I could run SQL like this:

SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'SomeCampaignTable'
ORDER BY ORDINAL_POSITION

And again do some manual work to configure my object to handle the custom fields.

My question is simply - how can I do this in NHibernate? Is it a simple matter of finding a way to run my own SQL, then looping through the results, or is there a more elegant way to take the pain out of it?

While I appreciate that this database design belongs in some kind of Museum of Torture somewhere, answers like "Add some views" or "Change the DB" won't help me - I will be shot if I suggest something like that.

Thanks for anything that could help save my sanity here!

+1  A: 

You might be able to use NHibernate using Native SQL Entity Queries. Forget Linq2NH - not that I would recommend Linq2NH for any serious application.

Check this page.
13.1.2. Entity queries https://www.hibernate.org/hib_docs/nhibernate/1.2/reference/en/html/querysql.html

You could maybe do something like this: Map your entities based on a 'fake' table to keep NHibernate happy when it compiles the mapping documents (I know you said you can't change the DB, but hopefully ok to make an empty table to keep NH happy).

Then run a query like this, as per 13.1.2 above:

sess.CreateSQLQuery("SELECT tempColumn1 as mappingFileColumn1, tempColumn2 as mappingFileColumn2, tempColumn3 as mappingFileColumn3 FROM tempTableName").AddEntity(typeof(Cat));

NHibernate should stitch together the columns you've returned with the mapped entity and give you the entity of type 'Cat' with all the properties populated. I am speculating here though, I do not know for sure if this will work, its the only way I can think of to use NHibernate for this given you don't know the tables/columns at compile time. You definitely cannot use HQL, Criteria, Linq2NH since you don't know the tables and columns at compile time, and HQL et al all convert your mappings to the mapped column names to produce the underlying SQL. Native SQL Queries are the only way I think.

reach4thelasers
Thanks - that got me on the right track! Using CreateSQLQuery and .SetResultTransformer(Transformers.AliasToEntityMap) made it possible to get all the information needed from the tables. It's dirty, but it works! Thanks!Just out of curiosity - any particular reason why Linq2NH is bad (not for this particular scenario, just in general)?
Rune Jacobsen
Linq2NH is still very immature. It only supports basic queries, doesn't support Caching, eager loading... I used it in my current app for 6 months before I abandoned it because I was too lazy to learn HQL/Criteria, then spent a week ripping it out. Linq2NH is easy to learn, easy to operate, but if you spend a bit of time learning HQL/Criteria you will have a lot more power and control at your fingertips, and your app will be a lot faster. Good luck! sounds like you've got a hell of a project on your hands!
reach4thelasers
Thanks! I have also used Linq2NH so far, but I guess I can live without that week (or whatever it will take) getting rid of it later.. Thanks for the help, advice and wishes, I will be lucky to get out of this project with any hair left on my head. :)
Rune Jacobsen