6

I will not go into the details why I am exploring the use of Micro ORMs at this stage - except to say that I feel powerless when I use a full blown ORM. There are too many things going on in the background that happens automatically, and not all of them are the best possible choices. I was quite ready to go back to raw database access, but I found out about the three new guys on the block: Dapper, PetaPoco and Massive. So I decided to give the low-level approach a go with a pet project. It is not relevant, but so far, I am using PetaPoco.

In any case, I am having trouble deciding how to go about maintaining the SQL strings that I will use from the higher levels. There are three main solutions that I can think of:

  1. Sprinkle the SQL queries wherever I need them. This is the least infrastructure heavy method. However, it suffers in both maintainability and testability areas.

  2. Limit the query usage to some service classes. This helps maintainability, is still low on infrastructure I need to implement. It may also be possible to build these service classes such that it would be easy to mock for testing purposes.

  3. Prepare some classes to make the system somewhat flexible. I have started on this path. I implemented a Repository interface, and a database dependent Repository class. I have also build some tiny interfaces to capture SQL queries that can be passed to my Repository's GetMany() method. All the queries are implemented as individual classes right now, and I will probably need a little more interface around this to add some level of database independence - and maybe for some flexibility in decorating queries into paged and sorted queries (again, this would also make them a little bit more flexible in handling different databases).

What I am mainly worried about right now is that I have entered the slippery slope of writing all the functions needed for a full blown ORM, but badly. For example, it feels sensible right now that I write or find a library to convert linq calls into SQL statements so that I can massage my queries easily or write extenders that can decorate any query I pass to it, etc. But that is a large task, and is already done by the big guys, so I am resisting the urge to go there. I also want to retain control over what queries I send to the database - by explicitly writing them.

So what is the suggestion? Should I go #2 option, or try to stumble along on option #3? I am certain I cannot show any code written in the first option to anyone without blushing. Is there any other approach you can recommend?


EDIT: After I've asked the question, I realized there is another option, somewhat orthogonal to these three options: stored procedures. There seems to be a few advantages to putting all your queries inside the database as stored procedures. They are kept in a central location, and not spread through the code (though maintenance is an issue - the parameters may get out of sync). The reliance on database dialect is solved automatically: if you move databases, you port all your stored procedures, and you are done. And there is also the security benefits.

With the stored procedure option, the alternatives 1 and 2 seem a little bit more suitable. There seems to be not enough entities to warrant option 3 - but it is still possible to separate the procedure call commands from database accessing code.

I've implemented option 3 without stored procedures, and option 2 with stored procedures, and it seems like the latter is more suitable for me (in case anyone is interested with the outcome of the question).

vhallac
  • 13,301
  • 3
  • 25
  • 36
  • Before delving into details... Did you check ibatis.net? – p.marino May 14 '11 at 11:57
  • There is no 1 size fits all answer here, it really depends on the type of project you have .... also you have the option to mix and match – Sam Saffron May 14 '11 at 11:57
  • @p.marino I did, and I was also tempted by it. I liked the interfaces of the three I mentioned a little bit more. I may end up porting my pet project to that to see how it feels, though. – vhallac May 14 '11 at 12:02
  • @Sam I agree with you. Consider this as a fishing expedition, if you will. I am somewhat new with the DB applications domain, and if there are options I haven't seen yet, I would appreciate the chance to examine them. – vhallac May 14 '11 at 12:06
  • Not sure if this'll help, but we manage our sql connections using the abstract factory pattern (really flexible for our needs, hopefully it'll benefit someone else too?): http://stackoverflow.com/questions/2969841/generic-multi-layer-data-access-pattern/2972736#2972736 – Mr Shoubs May 14 '11 at 12:16

2 Answers2

6

I would say put the sql where you would have put the equivalent LINQ query, or the sql for DataContext.ExecuteQuery. As for where that is... well, that is up to you and depends on how much separation you want. - Marc Gravell, creator on Dapper

See Marc's opinion on the matter

I think the key point is, you shouldn't really be re-using the SQL. If your logic is re-used then it should be wrapped in a method called that can then be called from multiple places.

Community
  • 1
  • 1
David Glenn
  • 24,412
  • 19
  • 74
  • 94
  • Thanks, I didn't see that question - probably was made after I read through the questions that had the word 'dapper' in it. – vhallac May 14 '11 at 12:09
4

I know you've accepted your answer already but I still wanted to show you a nice alternative that may be helpful in your case as well. Now or in the future.

When using stored procedures it's wise to use T4

I tend to use stored procedures on my project even though it's not using PetaPoco, Dapper or Massive (project started before these were here). It uses BLToolkit instead. Anyway. Instead of writing my methods to run stored procedures and write code to provide stored procedure parameters, I've written a T4 template that generates the code for me.

Whenever stored procedures change (some may be added/removed, parameters added/removed/renamed/retyped), my code will break on compilation because method calls will not match their signature any more.

I keep my stored procedures in a file (so they get version controlled). If you work in a multi-developer team it may be sensible to have stored procedures each in its own file. It makes updates much less painful. I've experienced that on some project and it worked ok as long as number of SPs is not huge. You can restructure them into folders based on the entity they're related to.

Anyway. Maintenance is related to stored procedures, code change is just a simple click of a button in Visual Studio that converts all T4s at once. You don't have to search your methods that use those procedures. You'll be reported errors while compiling. One thing less to worry about.

So instead of writing

using (var db = new DbManager())
{
    return db
        .SetSpCommand(
            "Person_SaveWithRelations",
            db.Parameter("@Name", name),
            db.Parameter("@Email", email),
            db.Parameter("@Birth", birth),
            db.Parameter("@ExternalID", exId))
        .ExecuteObject<Person>();
}

and having a bunch of magic strings I can just simply write:

using (var db = new DataManager())
{
    return db
        .Person
        .SaveWithRelations(name, email, birth, exId)
        .ExecuteObject<Person>();
}

This is nicer, cleaner breaks on compile and provides intellisense so it's also faster to while developing.

The good thing is that stored procedures may become very complex and may do many things. In my upper example I check some data, insert person record and some related one as well and in the end return the newly inserted Person record. Inserts and updated should usually return data that was added/changed to reflect actual state.

Robert Koritnik
  • 103,639
  • 52
  • 277
  • 404
  • Very nice idea. I've only seen T4 once, I have dismissed it as something I wouldn't use. This answer tells me that I shouldn't be too quick to dismiss these things. I will certainly try it out on the toy project to get a feel for it. Right now, I am going back and forth between stored procedures and keeping queries in code. This method may give stored procedures a +1. :) – vhallac May 20 '11 at 20:03
  • @Dysaster: I'm not a fan of French salad dressing... if you know what I mean... ;) Fact is I don't like mixing TSQL with C# code just as much as I hate mixing Javascript/HTML with C# code. Stored procedures should be part of their own space and shouldn't mix with C# either. T4 makes it possible to transparently convert them into usable form that eliminates the human error factor of forgetting mistyping etc (provided your T4 doesn't have bugs). – Robert Koritnik May 20 '11 at 21:10