Programming with LINQ to SQL

Introduction

The concept of object relational mapping (ORM) is not new. An ORM is a class (called an entity in this context) that has properties or fields that map to database columns. In this article, I will show you how easy it is to use LINQ to SQL to define an ORM, mapping a class to a database table, and then query that table with LINQ using a DataContext. A DataContext plays the role of the connection here.

Mapping Classes to Tables

A table mapping has two basic elements. The TableAttribute maps the class to a table in a database, and you need the ColumnAttributes to map each property to a column in the database.

To map the class to a Table, you use the TableAttribute with the Named argument to associate a class with a table. To begin, add a reference to System.Data.Linq and an imports statement for System.Data.Linq and System.Data.Linq.Mapping. For example, to map a class to an Orders table, you can write:

<Table(Name:="Orders")> _
Public Class Order
End Class

To map the columns of the Northwind.Orders table (or any table) to properties or fields in the Order class, tag each element with the ColumnAttribute. Listing 1 shows both the Orders and Order Details table of Northwind mapped to the classes Order and OrderDetail.

Listing 1: Mapping classes to tables with LINQ to SQL.

<Table(Name:="Orders")> _
Public Class Order
   <Column()> _
   Public OrderID As Integer
   <Column()> _
   Public CustomerID As String
   <Column()> _
   Public EmployeeID As Integer
   <Column()> _
   Public OrderDate As DateTime
   <Column()> _
   Public ShipCity As String
End Class


<Table(Name:="Order Details")> _
Public Class OrderDetail
   <Column()> _
   Public OrderID As Integer
   <Column()> _
   Public ProductID As Integer
   <Column()> _
   Public UnitPrice As Decimal
   <Column()> _
   Public Quantity As Int16
   <Column()> _
   Public Discount As Single
End Class

You can add named arguments to the Column attributes to specify a Name, DbType, Storage, and information such as whether the value in the database might send you a null. Name is used to indicate the column name. If yu don't use the Name argument, LINQ to SQL then assumes the property or field name maps to the Column name as defined. The Storage named argument is used to define where the value is stored in the class. For example, if you define a Property OrderID, you could have a field FOrderID and add Storage:="FOrderID" to the ColumnAttribute's list of arguments. Because you are using fields in the example, Storage is not needed. If you specify the type, you want to match the DbType to an actual database type, such as DbType:="NChar(5)", for example, for the CustomerID column. LINQ will figure it out if you leave this argument out too.

Constructing the DataContext

To get data for the ORM class, you use a DataContext. The DataContext is initialized with a connection string to the database, and you request the data from DataContext.GetTable. Because, ostensibly, you could have any variation of table, GetTable returns an instance of Table(Of T) where T is one of your entity classes.

The elided code below demonstrates how to construct the DataContext, define the generic Table(Of T) class, and request the data. (You will have to change the connection string for your copy and location of the database.)

Module Module1

   Public connectionString As String = _
      "Data Source=.\SQLEXPRESS;AttachDbFilename=" + _
      "C:\Books\Addison Wesley\LINQ\Northwind\northwnd.mdf;" + _
      "Integrated Security=True;Connect Timeout=30;
         User Instance=True"

   Sub Main()
      ' Use LINQ to SQL to get the data - context represents
      ' the database
      Dim orderContext   As DataContext =
         New DataContext(connectionString)
      Dim detailsContext As DataContext =
         New DataContext(connectionString)

' generic table does the ORM association
   Dim orders As Table(Of Order) =
      orderContext.GetTable(Of Order)()
   Dim details As Table(Of OrderDetail) =
      orderContext.GetTable(Of OrderDetail)()
...

At this point, you can treat orders and tables like any queryable sequence. The reason for this is that LINQ can query anything that implements IEnumerable and IQueryable. Table(Of T) implements both interfaces.

Tip: DataContext has a Log property. If you assign a System.IO.TextWriter to the DataContext.Log, LINQ will send information about the query transformations it is defining to convert the LINQ query to SQL. Console.Out is a TextWriter.

This LINQ to SQL mechanism works because LINQ builds expression trees and queryable providers can transmogrify expression trees to anything else, like SQL. (For an example of queryable providers, check out my book LINQ Unleashed: for C# (due in July 2008) or Bart De Smet's blog.

Programming with LINQ to SQL

Query SQL Server with LINQ to SQL

Now, all you have to do to query orders or details is to write some queries. Listing 2 is the complete example. This listing uses the ORMs defined in Listing 1, the DataContext, and a LINQ query to display all of the ProductID's from the [Order Details] table to the console.

Listing 2: The complete listing including the TableAttributes, ColumnAttributes, ORM definition, the DataContexts, and a LINQ query to select the data.

Imports System.Data.Linq
Imports System.Data.Linq.Mapping
Imports System.IO


Module Module1

   Public connectionString As String = _
      "Data Source=.\SQLEXPRESS;AttachDbFilename=" + _
      "C:\Books\Addison Wesley\LINQ\Northwind\northwnd.mdf;" + _
      "Integrated Security=True;Connect Timeout=30;
         User Instance=True"

   Sub Main()
      ' Use LINQ to SQL to get the data - context represents
      ' the database
      Dim orderContext As DataContext =
         New DataContext(connectionString)
      Dim detailsContext As DataContext =
         New DataContext(connectionString)

      ' generic table does the ORM association
      Dim orders As Table(Of Order) =
         orderContext.GetTable(Of Order)()
      Dim details As Table(Of OrderDetail) =
         orderContext.GetTable(Of OrderDetail)()

      Dim allDetails = From detail In details _
                       Select detail

      For Each d In allDetails
         Console.WriteLine(d.ProductID)
      Next

      Console.ReadLine()

   End Sub

End Module



<Table(Name:="Orders")> _
Public Class Order
   <Column()> _
   Public OrderID AsInteger
   <Column()> _
   Public CustomerID As String
   <Column()> _
   Public EmployeeID As Integer
   <Column()> _
   Public OrderDate As DateTime
   <Column()> _
   Public ShipCity As String
End Class


<Table(Name:="Order Details")> _
Public Class OrderDetail
   <Column()> _
   Public OrderID As Integer
   <Column()> _
   Public ProductID As Integer
   <Column()> _
   Public UnitPrice As Decimal
   <Column()> _
   Public Quantity As Int16
   <Column()> _
   Public Discount As Single
End Class

I hope you see that this is quite a bit easier than using connections and DataReaders to populate entity classes.

LINQ to SQL is completely compatible with ADO.NET 2.0 so you can mix in LINQ with your existing ADO.NET code and blend both styles—LINQ and plain vanilla ADO.NET. And, of course, you can still use straight ADO.NET with stored procedures if you want.

Summary

LINQ to SQL lightens the load of ADO.NET programming. All you need to do is define entity maps—which you have done before—with the TableAttribute and ColumnAttribute, and use a DataContext to get the data. LINQ writes the SQL. In Part 2 of this article, you'll use this code to define a left join on the Orders and Order Details objects.

LINQ is completely compatible with ADO.NET 2.0, so you can use these new capabilities with or to replace existing code.

About the Author

Paul Kimmel is the VB Today columnist for www.codeguru.com and has written several books on object-oriented programming and .NET. Check out his upcoming book LINQ Unleashed for C# from Sams due in July 2008. Paul Kimmel is an Application Architect for EDS. You may contact him for technology questions at pkimmel@softconcepts.com.

If you are interested in joining or sponsoring a .NET Users Group, check out www.glugnet.org. Glugnet opened a users group branch in Flint, Michigan in August 2007. If you are interested in attending, check out the www.glugnet.org web site for updates.

Copyright © 2008 by Paul T. Kimmel. All Rights Reserved.



Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Mobile is introducing sweeping changes throughout your workplace. As a senior stakeholder driving mobile initiatives in your organization, you may be lost in a sea of technologies and claims from vendors promising rapid delivery of applications to your employees, customers, and partners. To help explain some of the topics you will need to be aware of, and to separate the must-haves from the nice-to-haves, this reference guide can help you with applying a mobile strategy in the context of application …

  • As a development and deployment platform, RHEL offers an efficient, scalable, and robust operating environment with certified security and flexible deployment options in physical and virtualized environments. To assess and quantify the business benefits of RHEL, IDC recently conducted in-depth interviews with IT staff members of 21 companies using RHEL servers. The organizations represent a broad range of industries and have an average of 22,700 employees. RHEL servers accounted for 23% of the servers …

Most Popular Programming Stories

More for Developers

RSS Feeds