views:

995

answers:

8

This is a simplification of the issue (there are lots of ways of doing things), but among applications that need to talk to a database I have usually seen one of two patterns:

  1. Object-Relational Mapping (ORM), where (usually) each table in the database has a corresponding "row wrapper" class with public properties that match the columns in the table. Sometimes these classes also automagically retrieve related information, so that foreign key columns can instead be exposed and displayed as the related data (rather than just the PK values).
  2. DataTables (and/or DataSets), where data is retrieved from the server as a DataTable and worked with in that form (even in the UI).

One of the major differences between the two approaches is that ORM allows you to reference strongly-typed fields in your code like so:

Person bob = new Person();
bob.FirstName = "Bob";
collectionPeople.Add(bob);

whereas with the DataTable approach your code would be something like:

DataRow newrow = datatablePeople.NewRow();
newrow["FirstName"] = "Bob";
datatablePeople.Rows.Add(newrow);

In this case, the ORM approach benefits from compile-time checking while the DataTable approach does not. On the other hand, the DataTable (and the DataSet) are already-written data structures that do an excellent job of representing relational data directly, so code that uses them can usually be implemented more quickly. In addition, code that uses DataTables can be easily understood and modified by others; home-grown (and often COTS) ORM systems often do extra database access "under the hood" to populate foreign keys and so forth, which can create problems for the unaware.

So which approach do you generally favor and why?

A: 

I used to just use a datareader to read fields onto my object using GetString, GetInt etc. but i've moved on now to a much more OO and testable approach using a Gateway to return a datatable from a query, this is then passed into a Service class which parses the table onto an object.

I never really liked ORM tools, they were always cumbersome and difficult to maintain, but i havent had a chance to play with LINQ yet so my opinion may change.

Andrew Bullock
This may be semantic, but I think if you're parsing info from a datatable onto an object, you're doing ORM.
MusiGenesis
a custom ORM, which could be an argument against a one-size-fits-all orm like LINQ.
alchemical
+7  A: 

I favor the DataTables way, because I'm old, tired, and skeptical of fashions like Subsonic and Linq.

Beyond that, when you are working with an ORM, you are generally minimizing what you do in SQL. You don't put a lot of logic in the SQL and you don't batch up several SQL statements so as to do multiple things in one trip to the database. Therefore, you tend to go to the database more often, and that's a big performance hit.

Using Datasets, I can do something like:

select col1, col2... from table1
select col1, col2... from table2
select col1, col2... from table3

and then make ONE trip to the database to get all three DataSets, using Tables[0], Tables[1], Tables[2] to reference them. It makes a very big difference in performance.

Someday maybe the interaction with the database will be so fast that there would be no point in batching up the SQL, but that day isn't here yet. When it comes, I'll switch to ORM, but until then, I'm willing to have my code be a little uglier in exchange for performance. It's no fun for users to use a sluggish app.

Finally, I like SQL. I'm good at SQL. Real good. I don't want to spend my time figuring out how to co-erce Linq to emit the SQL that I want. It would slow down MY performance.

Corey Trager
You can do one roundtrip with mature ORMs as well like NHibernate.
mmiika
You could take a mixed approach, and use ORM when you are doing simple operations like single row create,read,update,deletes (CRUD), but go to straight SQL when you need to do more complex operations.
Kibbee
The question wasn't really meant to be about using straight SQL to manipulate the DB, but rather how best to work with in-memory representations of the DB data.
MusiGenesis
Amen Corey, Amen.
Kevin
@MusiGenesis - Oh, if THAT's all you were asking, then sure, hands down, it's nicer to worked with the stronger types that the compiler can check and that Intellisense can autocomplete.
Corey Trager
Corey, you almost got the check until that last comment. :)
MusiGenesis
+4  A: 

You could combine both approaches mentioned using Strongly Typed DataSets.

It's possible to add them to a Visual Studio project via "Add New Item" dialog "DataSet template" and then use visual Dataset Designer (it edits XSD file behind the scenes).

There is another article on subject.

Alexander Prokofyev
Have you used these? Do you know of a way to generate the required XSD from the SQL Server database automatically?
MusiGenesis
Investigate DataAdapter.FillSchema(), DataSet.WriteXmlSchema() and DataReader.GetSchemaTable() methods.
Alexander Prokofyev
Just how many tables are in your database? It's easy enough to select all the tables in Server Explorer, then drag them onto your DataSet designer surface. How many tables would you have to have for that not to work?
Kyralessa
But be aware of the Typed DataSets idiossincrasies... Specially concerning DBNull and nullable fields.
Seiti
A: 

I find that I'm able to develop less code and test my code better using an ORM than DataSets/DataTables. I'm currently using LINQ-to-SQL and wrapping a thin layer around the designer generated code via partial classes as my ORM. The thin layer basically allows me to add some role-based permissions and increases the testability of the code by refactoring to interfaces and using dependency injection to specify the data context (that way I can mock it up for other tests).

I've done both -- write my own ORM around DS/DT, strongly-typed DS/DT, etc. and have switched to LINQ and am not going back. I may eventually move to something like NHibernate, Entity Framework, or something else, but for now my LINQ solution offers pretty much all I need and is much simpler than my old solutions.

tvanfosson
How do you deal with the problem of hand-editing generated code? I've worked on projects with code generation, and a persistent issue was developers having manual modifications wiped out whenever a class was re-generated. It could be re-added from source control, but still a pain.
MusiGenesis
+6  A: 

Datatable will certainly be conceptually more straight forward in working with data. And its devoid of sometimes unnatural idioms that you find in ORM. (querying a record into local memory, before updating it; joins are pointers; the key value itself is a pointer, hence, adding a record requires loading the parent record)

The big advantages for ORM are...

1) it writes the sql for you, so you dont really have to write any sql to do basic crud. Of course writing more complex statements has to be done in a less powerful sublanguage (i.e. hql)

2) The other big advantage of ORM is when you get results back, it maps it into value objects, without writing a bunch of code to map the values and handle type conversion.

If you have strong sql skills but want advantage 2 covered, i would go with ibatis

#1 "basic crud" means extremely basic, we're talking data from a single table--that's extremely limiting imho. #2 Very often it doesn't map in a useful way. var types in c# can't be moved outside the function.
alchemical
+2  A: 

I would look at the advantages of data objects versus DataTables (a fancy ORM library isn't really necessary though they can be nice):

  • Conceptually clean. You're forced to apply OO concepts to your data. It's easier to understand "this is a Person" versus "the Person I want is somewhere in this table".
  • Enforces separation of concerns. It's tempting to tack UI context data to a DataTable - for one UI I get a Person with a primary address in the same record, for another I get a Person with credit information in the same record. When I'm working with a model, I want that model to be consistent wherever I consume it.
  • Transform the data only once. In the DataTable-crunching apps I've seen, there's a lot of this scattered all over:

    if(row["col"] == DBNull.Value || string.IsNullOrEmpty(row["col"] as string)) ...

    I'd rather check that condition once when I populate the data object versus checking it everywhere the DataTable is used.

  • Easier to unit test. If you reference a field in a data object that doesn't exist, you get a compile-time error. If you reference a field in a DataTable that doesn't exist, you get a run-time error.

I do believe ORM can make you lazy. For instance, there's absolutely no reason to populate a set of related data objects from individual queries in the objects if those objects are always used together. Instead, write a big and efficient query that grabs all the necessary data and then builds the data object graph. Still, if you keep its limitations in mind, it does save a lot of work.

Related: http://stackoverflow.com/questions/37378/how-to-convince-my-co-workers-not-to-use-datasets-for-enterprise-development-ne.

Corbin March
+2  A: 

In .Net strongly typed datasets have the benefits you attribute to ORM -- the value of strong typing in the IDE and Intellisense,

The TableAdapters created in Visual Studio will write all of your CRUD SQL for you. You put your business logic in the C# (or other lang.) and not in the SQL.

I think the disconnected data model offered by datasets proves to be more efficient in practice than typical hand-coded SQL. It leads to non-chatty DB interactions where you get all of your related table data in a single query. And it has very good support for optimistic locking (providing DBConcurrency exceptions). The dataset also tracks insertions, modifications and deletions to your data, so you don't have to.

Strongly typed datasets also offer straight-forward navigation to related table data.

A good reference on DataSets is

Programming Microsoft® ADO.NET 2.0 Core Reference by David Sceppa

Publisher: Microsoft Press Pub Date: May 17, 2006 Print ISBN-10: 0-7356-2206-X Print ISBN-13: 978-0-7356-2206-7 Pages: 800

+tom

Tom A
+1  A: 

I used to use DataSets in early .NET days, then started using typed DataSets, over time found that the memory footprint left by DataSets are very high and it did not make sense to use unmanaged objects for every simple tasks.
Hence concluded that DTOs/POCOs are a better choice and started using NHibernate and iBatis.
My average(or below average) developers found them complex and hard to use(no pun intended).
So I made a home grown ORM XmlDataMapper that was much easier to use that the complex ORMs out there.

To integrate XmlDataMapper all you need to do is 4 little steps

  1. Create a Business Entity / DTO for the tables
  2. Create an XML File with the mapping information between the table and the DTO.
  3. Specify the DTO and xml file in the configuration.
  4. Just call the DTOConverter.Convert(dataReader) and other such methods to convert your database record to DTO / Business Entity
Binoj Antony