Introduction to LINQ to SQL

Basic Introduction to Mapping Entities using LINQ to SQL

Updated: 03 September 2023

Note that you can follow this entire process or skip to the Shortcut section at the end

Using the Microsoft docs on LINQ to SQL and the sample database setup script

You will also need Visual Studio installed

Create the Database

To get started first create the database based on the Script above by applying it from SQL Server Management Server or another database management tool

Typical Process

The typical process for a LINQ to SQL application development is as follows:

  1. Create a model class and execute simple queries
  2. Add additional classes and execute more complex queries
  3. Add, change, and delete database items
  4. Use Stored Procedures

1. Model Setup and Single Entity Query

Documentation here

Configure the Project

  1. In Visual Studio create a new .NET Framework Console App called LinqToSql
  2. From the Solution Explorer right click on the Project and select References > Add Reference and add a reference to System.Data.Linq from the Framework section
  3. Add the following usings to the Program.cs file:
1
using System.Data.Linq;

Mapping a Class to Database Table

Now we can create a new class and map it to our database table. Create a file in the Project called Models/Customer.cs with the following class definition. This states that the Table Name for our Customer collection is Customer

1
[Table(Name = "Customers")]
2
public class Customer { }

In the class body we can reference the different columns as well as the private internal variable that will hold the value, this is referenced by the Column Annotation, don’t collapse it to a single property (even if Visual Studio tells you to). We can see the full Customer.cs file below:

1
using System.Data.Linq.Mapping;
2
3
namespace LinqToSql.Models
4
{
5
[Table(Name = "Customers")]
6
public class Customer
7
{
8
private string _CustomerID;
9
[Column(IsPrimaryKey = true, Storage = "_CustomerID")]
10
public string CustomerID
11
{
12
get
13
{
14
return this._CustomerID;
15
}
16
set
17
{
18
this._CustomerID = value;
19
}
20
21
}
22
23
private string _City;
24
[Column(Storage = "_City")]
25
public string City
26
{
27
get
28
{
29
return this._City;
30
}
31
set
32
{
33
this._City = value;
34
}
35
}
36
}
37
}

Query Database

We need to create a link to our database using a DataContext object. The documentation makes use of the connection to the mdf file, however we’ll use a ConnectionString instead as this makes more sense in practice

We can create a new DataContext as follows

1
DataContext db = new DataContext("<ConnectionString>")

Thereafter we can query a table in our db object by using the db.GetTable function

1
Table<Customer> customers = db.GetTable<Customer>();

Using our customers object we can make queries against the table with:

1
IQueryable<Customer> custQuery = customers.Where(c => c.City == "London");

And print out the reqults of the query with:

1
foreach (Customer c in custQuery)
2
{
3
Console.WriteLine("ID={0}, City={1}", c.CustomerID, c.City);
4
}

Putting this all together, our Program.cs file should now contain the following:

1
using LinqToSql.Models;
2
using System;
3
using System.Data.Linq;
4
using System.Linq;
5
6
namespace LinqToSql
7
{
8
class Program
9
{
10
static void Main(string[] args)
11
{
12
DataContext db = new DataContext(@"Data Source=localhost\SQLEXPRESS;Initial Catalog=Northwind;Integrated Security=True");
13
14
db.Log = Console.Out;
15
16
Table<Customer> customers = db.GetTable<Customer>();
17
18
IQueryable<Customer> custQuery = customers.Where(c => c.City == "London");
19
20
foreach (Customer c in custQuery)
21
{
22
Console.WriteLine("ID={0}, City={1}", c.CustomerID, c.City);
23
}
24
25
Console.ReadLine();
26
}
27
}
28
}

2. Query Across Relationships

Documentation here

Update the Data Model

First, create the Models/Order.cs file with the following content:

1
using System.Data.Linq;
2
using System.Data.Linq.Mapping;
3
4
namespace LinqToSql.Models
5
{
6
[Table(Name = "Orders")]
7
public class Order
8
{
9
private int _OrderID = 0;
10
private string _CustomerID;
11
private EntityRef<Customer> _Customer;
12
public Order() { this._Customer = new EntityRef<Customer>(); }
13
14
[Column(Storage = "_OrderID", DbType = "Int NOT NULL IDENTITY",
15
IsPrimaryKey = true, IsDbGenerated = true)]
16
public int OrderID
17
{
18
get { return this._OrderID; }
19
}
20
21
[Column(Storage = "_CustomerID", DbType = "NChar(5)")]
22
public string CustomerID
23
{
24
get { return this._CustomerID; }
25
set { this._CustomerID = value; }
26
}
27
28
[Association(Storage = "_Customer", ThisKey = "CustomerID")]
29
public Customer Customer
30
{
31
get { return this._Customer.Entity; }
32
set { this._Customer.Entity = value; }
33
}
34
}
35
}

Note that in the Order model we do not have a getter for the OrderId as this is generated by the database and cannot be set

Next we should add a reference to the Order model from the Customer model so as to make it easier to navigate the relationship. We can do this by dding a reference to the Order entity in our constructor:

1
public Customer()
2
{
3
this._Orders = new EntitySet<Order>();
4
}

And then adding the Orders property on the field:

1
private EntitySet<Order> _Orders;
2
[Association(Storage = "_Orders", OtherKey = "CustomerID")]
3
public EntitySet<Order> Orders
4
{
5
get
6
{
7
return this._Orders;
8
}
9
set
10
{
11
this._Orders.Assign(value);
12
}
13
}

Finally the Customer.cs file should be as follows:

1
using System.Data.Linq;
2
using System.Data.Linq.Mapping;
3
4
namespace LinqToSql.Models
5
{
6
[Table(Name = "Customers")]
7
public class Customer
8
{
9
public Customer()
10
{
11
this._Orders = new EntitySet<Order>();
12
}
13
14
private string _CustomerID;
15
[Column(IsPrimaryKey = true, Storage = "_CustomerID")]
16
public string CustomerID
17
{
18
get
19
{
20
return this._CustomerID;
21
}
22
set
23
{
24
this._CustomerID = value;
25
}
26
27
}
28
29
private string _City;
30
[Column(Storage = "_City")]
31
public string City
32
{
33
get
34
{
35
return this._City;
36
}
37
set
38
{
39
this._City = value;
40
}
41
}
42
43
private EntitySet<Order> _Orders;
44
[Association(Storage = "_Orders", OtherKey = "CustomerID")]
45
public EntitySet<Order> Orders
46
{
47
get
48
{
49
return this._Orders;
50
}
51
set
52
{
53
this._Orders.Assign(value);
54
}
55
}
56
}
57
}

Query Across Multiple Entities

Now we can query this data from the Program.cs file with a query like the following:

1
IQueryable<Customer> custOrderQuery = db.Customers.Where(c => c.Orders.Any());
2
3
foreach (Customer c in custOrderQuery)
4
{
5
Console.WriteLine("ID={0}, City={1}, Orders={2}", c.CustomerID, c.City, c.Orders.Count);
6
}

Create a Strongly Typed Database View

It can be easier to create a strongly typed view of the database by creating a DataContext object that we define instead of using the GetTable function to retrieve a specific table

Create a class called NorthwindContext with the following code containing a definition for Customers and Orders:

1
using System.Data.Linq;
2
3
namespace LinqToSql.Models
4
{
5
public class NorthwindContext : DataContext
6
{
7
// Table<T> abstracts database details per table/data type.
8
public Table<Customer> Customers;
9
public Table<Order> Orders;
10
11
public Northwind(string connection) : base(connection) { }
12
}
13
}

We can now replace DbContext definition with NorthwindContext and the call to GetTable to just use the Customers property in the NorthwindContext class like so:

1
NorthwindContext db = new NorthwindContext(@"Data Source=localhost\SQLEXPRESS;Initial Catalog=Northwind;Integrated Security=True");
2
3
IQueryable<Customer> custQuery = db.Customers.Where(c => c.City == "London");

As well as in our second query:

1
IQueryable<Customer> custOrderQuery = db.Customers.Where(c => c.Orders.Any());

3. Manipulating Data

To manipulate data we can use some of the functions provided to us by LINQ to SQL

We can update data in our database by followind a few basic steps

  1. Retreive the entity we want to modify
  2. Create, Update, or Delete the entity
  3. Submit changes to the datbase

Retrieve an Entity

We can retrieve an entity by wuerying the database for it

1
Customer custToUpdate = db.Customers.Where(c => c.Orders.Any()).First();

Modify Entity

Next you we can modify the City and delete an Order from the customer

1
custToUpdate.City = "NEW CITY";
2
3
db.Orders.DeleteOnSubmit(custToUpdate.Orders[0]);

Submit Changes

Lastly we can submit the changes to the database

1
db.SubmitChanges();

4. Using Stored Procedures

In order to use existing Stored Procedures you can make use of the SQLMetal tool or using the Object Relational Designer in Visual Studio

Getting Started with the O/R Designer

Documentation

First, create a new Linq to SQL Class file from the Right Click Project > Add New Item dialog, you can call the file DataClasses.dbml

Next add a link to the Database you are working with from the Server Explorer in Visual Studio and you can drag in the Customers and Orders table and the relationship should be visible and this should update the generated DataContext classes with the information you update in the designer. This will not update the database

The O/R Designer only supports 1:1 mappings

You can modify and update information in this view although I would suggest doing it from the

Creating Methods from Stored Procedures can be done by pulling them in from the Stored Procedures folder on the DB to the methods section on the designer view

Shortcut

You don’t need to do all the above manually. From Visual Studio do the following:

  1. Create the project
  2. Open the database in the Server Explorer
  3. Add a LinqToSQL Class file (.dbml)
  4. Drag in the tables you would like to work and it will generate the