LINQ looks good, but DLINQ scares me
One of Microsoft's announcements at the PDC this week has been LINQ (Language INtegrated Query). Here's the elevator speech version: "LINQ enables developers to query objects, databases and XML using a unified programming model because LINQ makes data transforms and queries first class NET citizens."
There's a lot more about it on the MSDN LINQ page, including 101 LINQ code samples. Paul Vick has a pretty good introductory article on LINQ, as well. Barry Gervin has come up with a nice summary of how the devlopment community is reacting to LINQ .
On the whole, I think it's a nice advance. While it's nice to simplify things a bit mentally by separating procedural logic into applications and declarative, set based logic into the database, that line has been bluring lately with the introduction of the .NET CLR to SQL Server 2005 (and of course, JServer back with Oracle 8i). I'll probably cling to this distinction where performance and / or maintainability are driving factors, but I definitely see how SQL syntax queries against collections can boost productivity.
XLINQ (extensions to allow LINQ to operate on XML data) looks good, too. It's still way too hard to query XML, and XLINQ looks like it would solve that.
DLINQ scares me, though. Not for the reasons Frans Bouma and Paul Wilson are talking about, although I do respect their OR/M expertise. Paul says:What's wrong with DLinq? Here's the list I have so far: attribute-based, MS Sql Server only, overly complicated, poor stored proc support, no server-side paging, very limited functionality, and no WinFS/OPath integration."
I'm just scared about code maintainability.
I actually have some relevant experience here - several years ago I was tasked with migrating a legacy PowerBuilder application to VB COM and T-SQL. PowerBuilder allows you to intermingle data access SQL with your procedural logic, and the application developers had made full use of this capability. Over the years, this application had grown to the point that it was mission critical, but very difficult to maintain. Additionally, there was a need to migrate from inline SQL to stored procedures for performance and data management reasons.
The project to upgrade to VB COM and T-SQL failed. The program flow was nearly impossible to follow, and the set-based and procedural logic were intermingled in such a way that separating them was just too expensive.
This is being pushed as a productivity enhancement for developers, and I totally agree with that. DLINQ makes it very easy to write database oriented applications very quickly. Properly architected applications could, of course, isolate the data access to layers or components to future-proof. DLINQ has support for custom UpdateMethods which could call stored procedures, for example. Developers and projects focused on productivity probably won't bother with any of that, in my experience. They'll code their SQL inline and move on to the next project.
I've had a similar concern with the SQL-CLR integration, but I think DLINQ is much more subject to abuse. If the SQL-CLR makes it easy to shoot yourself in the foot, DLINQ is a Junior H-Bomb Activity Kit. Take a look at the following code samples (from the LINQ Code Samples page and the DLINQ Hands On Lab), and imagine a three year old application with thousands of lines of code which use DLINQ for data access:
DataContext db = new DataContext("c:\\northwind\\northwnd.mdf");
// Get a typed table to run queries
Table<Customer> Customers = db.GetTable<Customer>();
// Query for customers from London
var q =
from c in Customers
where c.City == "London"
select c;
foreach (var cust in q)
Console.WriteLine("id = {0}, City = {1}", cust.CustomerID, cust.City);
That's not too bad, but it wouldn't be very easy to migrate a complex query to a stored procedure or move the Cities into a link table. Here's a database update:
Northwind db = new Northwind(@"C:\Temp\northwnd.mdf");
using(TransactionScope ts = new TransactionScope())
{
var q =
from p in db.Products
where p.ProductID == 15
select p;
Product prod = q.First();
// Show UnitsInStock before update
Console.WriteLine("In stock before update: {0}", prod.UnitsInStock);
if (prod.UnitsInStock > 0) prod.UnitsInStock--;
db.SubmitChanges();
ts.Complete();
Console.WriteLine("Transaction successful");
}
Console.ReadLine();
Now imagine we've got multiple nested selects, inserts, and updates. Complicated, right?
Hey, PM,I can't figure out what's causing this weird bug and QA doesn't want to retest the binary, but I can fix it with an INSERT TRIGGER on the database...Pretty soon we've got an unmaintainable application on our hands.