Microsoft Entity Framework Core 8 samples

Karen Payne - Dec 2 '23 - - Dev Community

Microsoft release the latest version of Entity Framework Core in November 2023 which has substantially changed from Entity Framework Core 7.

To see what is new check out Microsoft documentation what's New in EF Core 8 and breaking changes in EF Core 8 documentation.

Each release of Entity Framework Core the Microsoft engineers provide code samples. Although these code samples are great, they are not structured for learning and use in a developer’s projects. Also, there are many code samples that may not be useful to the average developer. With that said I took Microsoft code samples and structured the code in a fashion suitable for the average developer to learn from.

Important

There are many project were for the most part the project name signifies the purpose of the project.

GitHub Source code

DateOnly TimeOnly

Something been long awaited is support for DateOnly and TimeOnly. Microsoft provides some samples for these yet, again not structured for a developer to learn. So for this a modified version of Microsoft NorthWind database has been provided, the Orders table originally defined dates as DateTime yet none of the dates had time values so they now are defined as dates.

When reverse engineering with EF Power Tools dates are defined as DateOnly and time as TimeOnly.

Partial Orders model show DateOnly.

public partial class Orders
{
    public int OrderID { get; set; }
    public DateOnly OrderDate { get; set; }
    public DateOnly RequiredDate { get; set; }
    public DateOnly ShippedDate { get; set; }
    public DateOnly DeliveredDate { get; set; } 
}
Enter fullscreen mode Exit fullscreen mode

TimeOnly

Check out the project TimeBetweenApp

IsBetween method for TimeOnly

Connections and logging (Console project)

To enhance learning, included is a class project for uniform connects read from appsettings.json and logging to a file.

Example implementation in a DbContext where ConnectionString() reads the main application's appsettings.json for the connection string.

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder.ConfigureWithFileLoggingOnly(ConnectionString());
Enter fullscreen mode Exit fullscreen mode

Let's look under the covers for ConfigureWithFileLoggingOnly.

  1. Configure for SQL-Server with the passed connection string
  2. Setup for logging to a new file under the debug folder where each day has a new folder and log file.
public static void ConfigureWithFileLoggingOnly(this DbContextOptionsBuilder optionsBuilder, string connectionString)
{
    optionsBuilder
        .UseSqlServer(connectionString)
        .LogTo(new DbContextToFileLogger().Log,
            new[]
            {
                DbLoggerCategory.Database.Command.Name
            },
            LogLevel.Information);
}
Enter fullscreen mode Exit fullscreen mode

Class for logging.

public class DbContextToFileLogger
{
    /// <summary>
    /// Log file name
    /// </summary>
    private readonly string _fileName = 
        Path.Combine(AppDomain.CurrentDomain.BaseDirectory, 
            "LogFiles", $"{Now.Year}-{Now.Month}-{Now.Day}", 
            "EF_Log.txt");

    /// <summary>
    /// Use to override log file name and path, file must exists
    /// </summary>
    /// <param name="fileName"></param>
    public DbContextToFileLogger(string fileName)
    {
        _fileName = fileName;
    }

    /// <summary>
    /// Setup to use default file name for logging
    /// </summary>
    public DbContextToFileLogger()
    {
        var dir = Path.GetDirectoryName(_fileName);
        if (!Directory.Exists(dir))
        {
            Directory.CreateDirectory(dir);
        }
        if (!File.Exists(_fileName))
        {
            using (StreamWriter w = File.AppendText(_fileName)) ;
        }
    }

    /// <summary>
    /// append message to the existing stream
    /// </summary>
    /// <param name="message"></param>
    [DebuggerStepThrough]
    public void Log(string message)
    {

        if (!File.Exists(_fileName))
        {
            File.CreateText(_fileName).Close();
        }

        StreamWriter streamWriter = new(_fileName, true);

        streamWriter.WriteLine(message);

        streamWriter.WriteLine(new string('-', 40));

        streamWriter.Flush();
        streamWriter.Close();
    }
}
Enter fullscreen mode Exit fullscreen mode

To ensure there is a log folder, each project project file has the following to create the log folder if not present.

<Target Name="MakeMyDir" AfterTargets="Build">
    <MakeDir Directories="$(OutDir)LogFiles" />
</Target>
Enter fullscreen mode Exit fullscreen mode

ASP.NET Core

These projects use dependency injection for connections and have no logging although they are ready to log to the console via SeriLog setup in Program.cs.

public class Program
{
    public static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);

        // Add services to the container.
        builder.Services.AddRazorPages();
        builder.Services.AddDbContextPool<ShadowContext>(options =>
            options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
        SetupLogging.Development();
Enter fullscreen mode Exit fullscreen mode

Benchmarkdotnet

Several of the projects are rigged up to perform benchmarks. Example in BookApp. Note in each method a method is commented out which has no baring on benchmarking but without benchmarking the developer can see the output of each operation performed.

[MemoryDiagnoser]
[RankColumn, 
 MinColumn, 
 MaxColumn, 
 Q1Column, 
 Q3Column, 
 AllStatisticsColumn]
[JsonExporterAttribute.Full]
[GcServer(true)]
public partial class BookOperations
{
    [Benchmark]
    public async Task GroupBy()
    {
        await using var context = new BookContext();
        var books = await context.Book.ToListAsync();

        List<IGrouping<int, Book>> results = books
            .GroupBy(book => book.Price < 10 ? 10 : (book.Price < 20 ? 20 : 30))
            .OrderBy(grouping => grouping.Key)
            .ToList();

        //GroupNoSwitchOutput(results);
    }


    [Benchmark(Baseline = true)]
    public async Task Switch()
    {
        await using var context = new BookContext();
        var books = await context.Book.ToListAsync();

        List<GroupSwitch> results = books
            .GroupBy(book => book.Price switch
            {
                <= 10 => "Cheap",
                > 10 and <= 20 => "Medium",
                _ => "Expensive"
            })
            .Select(g => new GroupSwitch(g.Key, g.ToList()))
            .OrderBy(ca => ca.Category)
            .ToList();

        //GroupWithSwitchOutput(results);
    }
}
Enter fullscreen mode Exit fullscreen mode

Source code

The following GitHub repository contains source code for this article. There will be more code samples added so do a pull request from time to time.

Current list of projects

AccessEntityFramework
BooksApp
CachingInterception
ComplexTypesSampleApp
ComputedColumnsApp
CreateAndPopulateSqlServerApp
DateOnlyTimeOnlySampleApp
DefaultConstraintSampleApp
EntityLibrary
ExecuteUpdateDeleteSampleApp
HasQueryFilterRazorApp
HierarchyIdSampleApp
ImmutableComplexTypesSampleApp
Interception
Microsoft Access
NorthWind2023App
NorthWind2023Library
ShadowProperties
SQL Server
SqlLite
Utilities
UtilityLibarary
WineConsoleApp

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .