DbContext definition
A DbContext instance represents a combination of the Unit Of Work and Repository patterns such that it can be used to query from a database and group together changes that will then be written back to the store as a unit.
- Has code to configure each DbSet where TEntity is a model e.g. Blog, Post
DbSet definition
A non-generic version of DbSet<TEntity> which can be used when the type of entity is not known at build time.
Let's look at these, a DbContext contains one or more DbSet e.g. a Blog as a master table and Post as a child table to Blog table.
See the following code for a DbContext with two DbSet, blog and posts.
Remove Range, the first one simple removes a range of TEnity in a generic way while the second is not generic as it's targeting a specific model.
Usually models (a class representing a table in a database which may have relations with other tables) are setup as shown below.
Now going back to a DbContext, in the image below each DbSet are setup under the Configurations folder called by, in this case NorthwindContext.cs
In closing, the best way to understand Entity Framework Core is to setup a database then reverse engineer the database using EF Power Tools. Sticking with Blogs/Posts, play around with various task such as shown below. And take to time read the docs on EF Core.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Saving.Data;
using Saving.Models;
namespace Saving
{
public class Program
{
public static async Task Main()
{
await Sample.DeleteAndModifyRecordIndividualContexts();
}
}
public class Sample
{
/// <summary>
/// Important to keep the DbContext scoped properly for each operation
/// and to understand how caching and change tracking works.
///
/// - Create three blogs with post
/// - Delete one blog
/// - Edit one blog
/// - inspect
/// - force reload
/// </summary>
public static async Task DeleteAndModifyRecordIndividualContexts()
{
WriteHeader($"Running {nameof(DeleteAndModifyRecordIndividualContexts)}");
/*
* Iterate all blogs/post
*/
static void ShowBlogs(BloggingContext context)
{
foreach (var blog in context.Blogs)
{
Console.WriteLine($"{blog.BlogId, -3}{blog.Description,-30}{blog.Url}");
// never assume there are children
if (blog.Posts is not null)
{
foreach (var post in blog.Posts)
{
Console.WriteLine($"\t{post.PostId,-3}{post.Title}");
Console.WriteLine($"\t\t{post.Content}");
}
}
else
{
Console.WriteLine("No post");
}
Console.WriteLine();
}
}
/*
* Update a single blog url which the caller will
* not know about see in this case the caller reloads
* said blog entry.
*/
static void UpdateBlogTitle(int blogIdentifier)
{
using var context = new BloggingContext();
var blog = context.Blogs.FirstOrDefault(b => b.BlogId == blogIdentifier);
blog.Url = "https://csharpforums.net/";
context.SaveChanges();
}
// start fresh
await using (var context = new BloggingContext())
{
await context.Database.EnsureDeletedAsync();
await context.Database.EnsureCreatedAsync();
}
// create blogs and post
await using (var context = new BloggingContext())
{
var blog1 = new Blog
{
Url = "http://blogs.msdn.com/dotnet/csharp",
Description = "Developer blog",
Posts = new List<Post>
{
new() { Title = "Intro to C#", Content = "Basic C#"},
new() { Title = "Working with classes", Content = "Understanding classes"}
}
};
var blog2 = new Blog
{
Url = "http://blogs.msdn.com/dotnet/vbnet",
Description = "Developer blog",
Posts = new List<Post> { new() { Title = "Intro to VB", Content = "Basic VB.NET"} }
};
var blog3 = new Blog
{
Url = "http://blogs.msdn.com/dotnet/fsharp",
Description = "Developer blog",
Posts = new List<Post> { new() { Title = "Intro to F#", Content = "Learn F#"} }
};
context.AddRange(blog1, blog2, blog3);
await context.SaveChangesAsync();
ShowBlogs(context);
Console.WriteLine($"Blog count after add {context.Blogs.Count()}");
context.Blogs.Remove(blog2);
await context.SaveChangesAsync();
Console.WriteLine($"Blog count after remove one blog {context.Blogs.Count()}");
int blogIdentifier = 1;
// this update will not be seen for the current DbContext being tracked
UpdateBlogTitle(blogIdentifier);
var changedBlog = context.Blogs.FirstOrDefault(b => b.BlogId == blogIdentifier);
Console.WriteLine($"After change: '{changedBlog.Url}'");
// to see the change, reload
await context.Entry(changedBlog).ReloadAsync();
Console.WriteLine($"After change reloaded: '{changedBlog.Url}' State: {context.Entry(changedBlog).State}");
/*
* this is debatable dependent on business logic
*/
context.Entry(changedBlog).State = EntityState.Modified;
Console.WriteLine($"After change reloaded: '{changedBlog.Url}' State: {context.Entry(changedBlog).State}");
}
// simple inspection
await using (var context = new BloggingContext())
{
Console.WriteLine($"Blog identifiers {string.Join(",", context.Blogs.Select(blog => blog.BlogId))}");
}
}
/// <summary>
/// From Microsoft
/// </summary>
public static void Run()
{
using (var context = new BloggingContext())
{
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
}
#region AddingGraphOfEntities
using (var context = new BloggingContext())
{
var blog = new Blog
{
Url = "http://blogs.msdn.com/dotnet",
Posts = new List<Post>
{
new() { Title = "Intro to C#" },
new() { Title = "Intro to VB.NET" },
new() { Title = "Intro to F#" }
}
};
context.Blogs.Add(blog);
Console.WriteLine($"{context.SaveChanges()}");
}
#endregion
#region AddingRelatedEntity
using (var context = new BloggingContext())
{
var blog = context.Blogs.Include(b => b.Posts).First();
var post = new Post { Title = "Intro to EF Core" };
blog.Posts.Add(post);
Console.WriteLine($"{context.SaveChanges()}");
}
#endregion
#region ChangingRelationships
using (var context = new BloggingContext())
{
var blog = new Blog { Url = "http://blogs.msdn.com/visualstudio" };
var post = context.Posts.First();
post.Blog = blog;
Console.WriteLine($"{context.SaveChanges()}");
}
#endregion
#region RemovingRelationships
using (var context = new BloggingContext())
{
var blog = context.Blogs.Include(b => b.Posts).First();
var post = blog.Posts.First();
blog.Posts.Remove(post);
Console.WriteLine($"{context.SaveChanges()}");
}
#endregion
}
}
}