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:
DLINQ - Simple Select example
// DataContext takes a connection string
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:
DLINQ - Demonstration of a database update
// Use a standard connection string for updates
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.