LINQ to SQLite Tutorial

LinqConnect (formerly known as LINQ to SQLite) is a fast and lightweight ORM solution compatible with Microsoft LINQ to SQL. It contains advanced features, such as complex type support, advanced data fetching options, configurable compiled query caching, and others.

LinqConnect is available as a separate product or as a part of Devart dotConnect providers for the corresponding databases. dotConnect for SQLite is one of such providers, and its Professional edition includes LinqConnect support for SQLite.

This tutorial guides you through the process of creating a simple application powered by LinqConnect.

Download Now dotConnect for SQLite

What is LINQ?

LINQ stands for Language-Integrated Query, which means that data retrieval is no longer a separate language. The LINQ engine allows .NET applications to connect to databases without worries about columns and rows. Your business logic automatically forms the data you receive as objects ready to use.

LINQ to Relational Data is an object-relational mapping (ORM) tool. The type-safe LINQ queries get compiled into MSIL on the fly, and the query clauses are translated into SQL and sent to the SQLite database for execution. It makes your data access layer safer, faster, and more convenient to design.

Requirements

What you need for this tutorial:

  • Visual Studio 2022: The IDE we use to illustrate our tutorial. If you do not have this IDE on your machine, download it from the official website and install it. We will use the free Community Edition.
  • dotConnect for SQLite: A feature-rich ADO.NET provider with Entity Framework, NHibernate, and LinqConnect support.
  • LinqConnect: A fast and lightweight LINQ to SQL ORM designer.
  • Sakila database: A popular sample database for learning and testing.

Prepare the project

Create a new console application in Visual Studio. It could be any project type, but we'll use the console project throughout the tutorial for simplicity.

We have created the test project named LinqSqlite. If your project has a different name, substitute LinqSqlite with the actual name in Solution Explorer when you follow this tutorial.

Build a Database First LINQ model

The Professional edition of dotConnect for SQLite includes Entity Developer - an ORM model designer and code generator supporting Entity Framework and LinqConnect ORM. It allows you to generate a model from a database (or a database from a model).

Add Devart LinqConnect Model to the project. To do this, right-click the project in Solution Explorer, point to Add > New Item.

Add Devart LinqConnect Model to the project

In the Add New Item dialog, select Data, then Devart LinqConnect Model, and click Add.

Create or generate a new model

The Entity Developer's Create Model Wizard opens automatically. It creates a new empty model or generates it from the database. Choose the Database First approach and click Next on the welcome screen.

Select Database First approach

Fill in the details of your database connection and click Next.

Provide correct database connection details

Select database objects to use in the model. These are all objects from the Sakila database, including auxiliary tables. Click Next.

Select database objects to use in the model

Define the naming convention for the property names in the database object and click Next. We keep the default settings this time.

Define the naming conventions

Input SakilaContext as the namespace and SakilaDataContext as the name of the DataContext descendant. It will be the name of the main data access class. Click Next.

Provide namespace and DataContext

Choose the code generation templates for your objects. You can define different parameters you want the object to follow. Let's use the default settings for this tutorial. Click Next.

Choose code generation templates for objects

Click Finish. The model will be generated and opened in Entity Developer.

View the generated model in Entity Developer

Entity Developer creates classes for all selected tables that represent entities. It also creates a descendant of the Devart.Data.Linq.DataContext class that controls the connection to the database and the whole data flow. This class includes properties and methods named after your database objects. You will use these members to retrieve and modify data in the context.

The generated code is in the file DataContext1.Designer.cs (DataContext1.Designer.vb). You may write your partial classes and methods in the file DataContext1.cs (DataContext1.vb).

Query data

All LINQ to SQLite operations are executed through the DataContext descendant named CrmDemoDataContext in this tutorial.

To retrieve data, you must first create an instance of the context, then prepare a query with LinqConnect, and then access the object returned by the query, which may be a collection of objects or a single object.

We need to read all data from the Company table, sort it by CompanyID, and output several columns.

Add the following code block to the main method:

using (var context = new SakilaDataContext())
{
// Check if the connection can be opened
context.Connection.Open();
Console.WriteLine("Connection to the database established successfully.");

// Query the first 10 records from the Actors table
var actors = context.Actors
     .Take(10)
     .ToList();

// Display the records
if (actors.Any())
{
foreach (var actor in actors)
{
Console.WriteLine($"ActorID: {actor.ActorId}, FirstName: {actor.FirstName}, LastName: {actor.LastName}");
}
}
else
{
Console.WriteLine("No records found in the Actors table.");
}
}

We prepare a query and then iterate through it as with a usual object collection. LinqConnect performs the database interaction in the background. Now, let's examine this code sample.

SakilaDataContext is the name of the class that knows all about the model and does everything to retrieve and modify related data in the database.

VIew the SakilaDataContext class

All LinqConnect operations are performed within this class's properties and methods which are designed to be lightweight and not expensive to create. We recommend creating a new DataContext instance for any 'unit of work'. After completing this unit, we can dispose it.

  • Query are arbitrary variable names in the LINQ to SQL statement. The former collects data objects, and the latter references single entities in a collection that exist inside the statement only.
  • context.Actor refers to a public property of the SakilaDataContext class. It represents the collection of all companies in the context.
  • Actor (in the foreach statement) is the name of an autogenerated class that maps to the Actor table and is named after that table.

Here is the project's output in the console:

View the retrieved data in a console

Note
The LINQ query code just describes the query. It does not execute it. This approach is known as deferred execution.

Let's query data from two tables united by a foreign key. Let's assume you have two tables: Actors and FilmActor, where FilmActor has a foreign key referencing Actors.

  • Actors: Contains actor details. Columns: ActorId, FirstName, LastName.
  • FilmActor: Contains the relationship between actors and films. Columns: ActorId (foreign key), FilmId.

Replace the old code with the following block:

using (var context = new SakilaDataContext())
{
	// Check if the connection can be opened
	context.Connection.Open();
	Console.WriteLine("Connection to the database established successfully.");

	// Query actors and their associated films
	var actorFilms = from actor in context.Actors
         join filmActor in context.FilmActors on actor.ActorId equals filmActor.ActorId
         select new
            {
            ActorId = actor.ActorId,
            FirstName = actor.FirstName,
            LastName = actor.LastName,
            FilmId = filmActor.FilmId
            };

	// Display the records
	foreach (var actorFilm in actorFilms)
	{
    	Console.WriteLine($"ActorID: {actorFilm.ActorId}, Name: {actorFilm.FirstName} {actorFilm.LastName}, FilmID: {actorFilm.FilmId}");
	}
}

The output is:

View full retrieved data in a console

Insert new data

Adding rows to tables is just adding new objects to context collections. When you are ready to send changes to the database, call the SubmitChanges() method of the context.

Before doing this, you must set all properties that do not support null (Nothing) values. The SubmitChanges() method generates and executes commands that perform the equivalent INSERT, UPDATE, or DELETE statements against the data source.

To insert a new record into the Actors table and display its details, replace the old code with the following block:

using (var context = new SakilaDataContext())
{
	// Check if the connection can be opened
	context.Connection.Open();
	Console.WriteLine("Connection to the database established successfully.");

	// Create a new Actor object
	var newActor = new Actor
	{
    	FirstName = "John",
    	LastName = "Doe"
	};

	// Insert the new actor into the Actors table
	context.Actors.InsertOnSubmit(newActor);

	// Submit the changes to the database
	context.SubmitChanges();

	Console.WriteLine("New actor inserted successfully.");

	// Query the newly inserted actor's details
	var insertedActor = context.Actors
             .Where(a => a.ActorId == newActor.ActorId)
             .Select(a => new
             	{
             	a.ActorId,
             	a.FirstName,
             	a.LastName
             	})
             .FirstOrDefault();

	// Display the details of the newly inserted actor
	if (insertedActor != null)
	{
    	Console.WriteLine($"Inserted Actor Details - ActorID: {insertedActor.ActorId}, Name: {insertedActor.FirstName} {insertedActor.LastName}");
	}
	else
	{
    	Console.WriteLine("Failed to retrieve the newly inserted actor's details.");
	}
}

The InsertOnSubmit() method is created for every collection in the context. It stores information about all linked objects in the database. As shown in the example, you only need to call InsertOnSubmit() once to submit both product and category objects.

Insert a new record into the table

Note
After you have added the new product and category by submitting the changes, you cannot execute this solution again as is. To execute the solution again, change the IDs of the objects to be added.

Update data

Entity instances are modified as usual. The only thing to remember is that you have to invoke the SubmitChanges() method to send the data to the database.

Append the following block to the existing code and launch the project:

using (var context = new SakilaDataContext())
{
// Check if the connection can be opened
context.Connection.Open();
Console.WriteLine("Connection to the database established successfully.");

// Create a new Actor object
var newActor = new Actor
{
   	FirstName = "John",
   	LastName = "Doe"
};

// Insert the new actor into the Actors table
context.Actors.InsertOnSubmit(newActor);
context.SubmitChanges();

Console.WriteLine("New actor inserted successfully.");

// Update the actor's details
newActor.FirstName = "Jane";
newActor.LastName = "Smith";

// Submit the changes to update the actor's details in the database
context.SubmitChanges();

Console.WriteLine("Actor's details updated successfully.");

// Query the updated actor's details
var updatedActor = context.Actors
     .Where(a => a.ActorId == newActor.ActorId)
     .Select(a => new
     {
     a.ActorId,
     a.FirstName,
     a.LastName
     })
     .FirstOrDefault();

// Display the updated details of the actor
if (updatedActor != null)
{
    Console.WriteLine($"Updated Actor Details - ActorID: {updatedActor.ActorId}, Name: {updatedActor.FirstName} {updatedActor.LastName}");
}
else
{
    Console.WriteLine("Failed to retrieve the updated actor's details.");
}
}

You can see that the data is successfully updated.

Data is successfully updated

Delete data

To extract an instance from a context, use the DeleteOnSubmit method of the corresponding collection. The object is removed from the collection of its type but not destroyed.

To delete the object's data from the database, invoke the SubmitChanges() method. You can do this with the below block of code:

using (var context = new SakilaDataContext())
{
	// Check if the connection can be opened
	context.Connection.Open();
	Console.WriteLine("Connection to the database established successfully.");

	// Define the ActorID of the record to be deleted
	int actorIdToDelete = 201;

	// Retrieve the actor with the specified ActorID
	var actorToDelete = context.Actors
                          	.FirstOrDefault(a => a.ActorId == actorIdToDelete);

	if (actorToDelete != null)
	{
    	// Delete the actor's record
    	context.Actors.DeleteOnSubmit(actorToDelete);
    	context.SubmitChanges();

    	Console.WriteLine($"Actor with ActorID {actorIdToDelete} has been successfully deleted.");
	}
	else
	{
    	Console.WriteLine($"No actor found with ActorID {actorIdToDelete}.");
	}

	// Verify the deletion by attempting to retrieve the actor's details
	var deletedActor = context.Actors
                          	.FirstOrDefault(a => a.ActorId == actorIdToDelete);

	// Display the result of the deletion
	if (deletedActor == null)
	{
    	Console.WriteLine("Actor's record has been successfully deleted.");
	}
	else
	{
    	Console.WriteLine("Failed to delete the actor's record.");
	}
}

You can see that the record has been deleted:

The record is successfullly deleted

Deletion of objects is affected by attributes in the model. When the DeleteRule parameter is Cascade, dependent objects are deleted automatically. When this parameter is SetNull, dependent objects are not deleted, but the relation is nullified. When no rule is specified, the order of deletion sequence is essential.

Additional information

Now that you can perform the basic data manipulation with LinqConnect, you can move on to more advanced topics. dotConnect for SQLite includes a help section dedicated to the LinqConnect technology. You can access it online at LinqConnect Documentation.

To understand the works of the LinqConnect engine, you can watch the generated SQL statements in dbMonitor or use the DataContext.Log property.

Conclusion

This tutorial shows how to easily create a data access layer for an SQLite database and work with its data via LINQ, using either dotConnect for SQLite or LinqConnect products. LinqConnect is a lightweight, LINQ to SQL-compatible ORM solution with support for SQLite, Oracle, PostgreSQL, and SQLite. dotConnect for SQLite is an ADO.NET provider from Devart with support for such ORM solutions as Entity Framework v1-v6, Entity Framework Core, NHibernate, and LinqConnect.


Back to the list

dotConnect for SQLite

Get enhanced ORM-enabled data provider for SQLite and develop .NET applications working with SQLite data quickly and easily!