Mastering Data Interaction with .NET
Entity Framework (EF) is a popular object-relational mapper (ORM) for .NET that enables developers to work with relational data using domain-specific objects. This tutorial will guide you through the fundamental concepts and practical applications of EF for data access in your .NET applications.
EF simplifies data access by abstracting away much of the boilerplate code typically required for database interactions. It allows you to query and manipulate data using C# or VB.NET code, treating your database tables as collections of objects.
Before we begin, ensure you have the following prerequisites:
Start by creating a new .NET Core Console Application in Visual Studio. Name your project something descriptive, like EFCoreDataAccessDemo.
You can install the necessary Entity Framework Core NuGet packages using the Package Manager Console or the NuGet Package Manager UI. We'll primarily use the following packages:
Microsoft.EntityFrameworkCore.SqlServer: For SQL Server provider.Microsoft.EntityFrameworkCore.Tools: For scaffolding and migrations.In the Package Manager Console, run:
Install-Package Microsoft.EntityFrameworkCore.SqlServer
Install-Package Microsoft.EntityFrameworkCore.Tools
Understanding these core concepts is crucial for effective use of Entity Framework:
The DbContext class is the primary gateway to your database. It represents a session with the database and allows you to query and save data. You'll typically create a class that inherits from DbContext and represents your database context.
Example DbContext:
using Microsoft.EntityFrameworkCore;
public class ApplicationDbContext : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<Category> Categories { get; set; }
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Seed initial data if needed
modelBuilder.Entity<Category>().HasData(
new Category { Id = 1, Name = "Electronics" },
new Category { Id = 2, Name = "Books" }
);
}
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public int CategoryId { get; set; }
public Category Category { get; set; }
}
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Product> Products { get; set; }
}
A DbSet<TEntity> represents a collection of all entities in the store (e.g., all instances of a particular entity type in the database) and can be used to query and save data for that entity type.
EF Migrations is a powerful feature that allows you to evolve your database schema over time as your application models change. You can use it to create, update, and manage your database schema.
To enable migrations in your project, open the Package Manager Console and run:
Enable-Migrations
Then, create your first migration:
Add-Migration InitialCreate
And apply it to your database:
Update-Database
You can configure your entity models using data annotations or the Fluent API within the OnModelCreating method of your DbContext. The Fluent API offers more control over complex mappings.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>()
.Property(p => p.Price)
.HasColumnType("decimal(18, 2)");
modelBuilder.Entity<Product>()
.HasOne(p => p.Category)
.WithMany(c => c.Products)
.HasForeignKey(p => p.CategoryId);
}
Entity Framework provides intuitive ways to perform standard Create, Read, Update, and Delete operations.
To add a new entity to the database, create an instance of your entity, attach it to the DbContext, and then call SaveChanges.
using var context = new ApplicationDbContext(...); // Assume context is configured
var newProduct = new Product
{
Name = "Laptop",
Price = 1200.50m,
CategoryId = 1 // Assuming CategoryId for Electronics
};
context.Products.Add(newProduct);
await context.SaveChangesAsync();
You can query data using LINQ (Language Integrated Query). EF translates your LINQ queries into SQL.
using var context = new ApplicationDbContext(...);
// Get all products
var allProducts = await context.Products.ToListAsync();
// Get a specific product by ID
var product = await context.Products.FindAsync(1);
// Query with filtering and ordering
var expensiveElectronics = await context.Products
.Where(p => p.Category.Name == "Electronics" && p.Price > 1000)
.OrderBy(p => p.Name)
.ToListAsync();
// Using Include to load related data
var productsWithCategories = await context.Products
.Include(p => p.Category)
.ToListAsync();
To update an existing entity, retrieve it from the context, modify its properties, and then call SaveChanges.
using var context = new ApplicationDbContext(...);
var productToUpdate = await context.Products.FindAsync(1);
if (productToUpdate != null)
{
productToUpdate.Price = 1250.00m;
await context.SaveChangesAsync();
}
To delete an entity, retrieve it, mark it for deletion using Remove, and then call SaveChanges.
using var context = new ApplicationDbContext(...);
var productToDelete = await context.Products.FindAsync(2);
if (productToDelete != null)
{
context.Products.Remove(productToDelete);
await context.SaveChangesAsync();
}
Once you're comfortable with the basics, explore these advanced features:
EF automatically tracks changes to entities. You can inspect the state of entities and understand how changes are managed.
Understand the differences between lazy loading (loading related data on demand) and eager loading (loading related data immediately using Include) and when to use each.
While EF promotes LINQ-based queries, you can also execute stored procedures or raw SQL queries when necessary.
var productsFromSql = await context.Products
.FromSqlRaw("SELECT * FROM Products WHERE Price > {0}", 500)
.ToListAsync();
Always use asynchronous methods like ToListAsync(), FindAsync(), and SaveChangesAsync() in asynchronous code to prevent blocking threads.
Learn techniques for optimizing EF performance, such as:
AsNoTracking() for read-only queries.SaveChanges call.Entity Framework provides a robust and flexible way to interact with your database in .NET applications. By understanding its core concepts and features, you can significantly improve your productivity and the maintainability of your data access code.
This tutorial covered the essentials, from setting up your environment and understanding the DbContext to performing basic CRUD operations and touching upon advanced topics. Continue exploring the official Microsoft documentation for more in-depth information and advanced patterns.