DLinq provides a run-time framework for managing relational
data as objects. It does this by translating LINQ queries into SQL for
execution by the database and then translates the tabular results back into
objects you define. Your application can then manipulate these objects however
you want to. While you do this DLinq will track any changes you make to them,
so that you can optionally submit changes back to the database (for update,
insert, and delete operations).
DLINQ allows you to define the object model and class
structure that best represents your application data. For example, if you
have a Customers database table with columns “CustomerId”, “City”, and
“CustomerName”, you could optionally create a “Customer” class that represented
it like so:
Listing 1
public class Customer
{
public string CustomerId;
public string City;
public string CustomerName;
}
Note that you can use whatever naming pattern you want in
your classes -- you aren't required to have the same names as in the
database.
DLINQ allows you to map classes to a database schema in two
ways. The first option is by adding attributes to the classes that indicate
where and how they should be stored in the database (this is called “Attribute
Based Mapping”). Alternatively you can specify the mapping using an external
XML file. The external XML file enables the underlying database schema to be
kept separate from the code, and even allows runtime schema mapping changes on
the fly (meaning you do not need to recompile an assembly using DLINQ to modify
the database storage schema being used). Developers can choose whichever
mapping approach works best for them and their projects.
In addition to supporting single table mappings, it is also
possible to easily define relationships between tables using DLINQ. In a
relational database this is typically modeled using foreign-keys referring to
primary keys in other tables. DLINQ allows developers to define “Association
Relationships” between classes to express relationships. This allows a
developer, for example, to write “Customer.Orders” to reference the collection
of orders for the customer instance.
Important: It is not necessary to manually define your table
mappings or relationships. The May CTP drop of DLINQ ships with both a
command-line utility and a Visual Studio data designer to make defining these
relationships and mappings simple (it will take us less than 30 seconds to
define a complete DLINQ mapping for the Northwind database in our sample
below).
Once data mappings and relationships are defined, developers
can then easily write LINQ code to perform queries and updates against a
database. For example, the below code uses the SQL Northwind database to
retrieve all customers from London, and then prints out the Customer Name, as
well as a hierarchical sub-listing of each of the customer’s orders to a page:
Listing 2
Northwind db = new Northwind(connectionString);
// Query for all customers in london
IEnumerable<Customer> customers = from cust in db.Customers
where cust.City == "London"
select cust;
// Fetch each customer and output its name and order history
foreach (Customer cust in customers) {
Response.Write ("Customer = " + cust.CustomerName);
foreach (Order order in cust.Orders) {
Response.Write("---- OrderID: " + order.OrderId);
}
}
Note how the Customer and Order classes are strongly-typed,
and how the Customer class has an “Orders” association relationship that uses
the ForeignKey/PrimaryKey relationship between the Customers and Orders tables
in the database.
We can also then easily write code to retrieve an individual
Customer and simultaneously update both its ContactName and add a new Order for
it in the system like so:
Listing 3
Northwind db = new Northwind(connectionString);
// Fetch a specific customer
Customer cust = db.Customers.Single(c => c.CustomerID == "ALFKI");
// Change the name of the contact
cust.ContactName = "ScottGu";
// Create and add a new Order to the customer's orders collection
Order order = new Order();
order.OrderDate = DateTime.Now;
order.ShipCity = "London";
cust.Orders.Add(order);
// Save all the changes to database
db.SubmitChanges();
We do not need to write any additional data access code or
define any SQL statements for the above two samples to work. Instead, I can
program and work with these objects for my data access, and have DLINQ perform
the underlying data statements for me. The compiler will automatically perform
syntax checking against my queries/code and will give me warnings/errors if I
do things wrong (for example: mistype a column name, misuse a double as an
integer, try to convert an inappropriate string to a date, etc). I can use the
debugger to inspect any value. And with the next release of VS I will get full
intellisense completion when writing these queries and operations.
DLINQ supports Stored Procedures, Views, and User-Defined
Functions, and allows developers to drop-down and add custom SQL where
necessary. Developers can add additional, non-database based, properties and
methods to data classes to extend their semantics, and can also add both
property and entity validation/business rules to them as well (the partial
class feature in VB and C# makes doing this clean and easy). Transactions are
also fully supported, including two-phase commit transactions where the DLINQ
operations can be enlisted in a broader transaction with multiple databases or
storage providers.
In short – there is a lot of cool stuff there. There is a
lot of documentation that comes with the May LINQ CTP that you can
read to learn more.
What I’m going to do below for the rest of this post is walk
through a few step-by-step instructions on how to start using the May LINQ CTP
with DLINQ in an ASP.NET app.