Phoesion.EFCore.PrecomputedViews 0.0.1

Prefix Reserved
dotnet add package Phoesion.EFCore.PrecomputedViews --version 0.0.1                
NuGet\Install-Package Phoesion.EFCore.PrecomputedViews -Version 0.0.1                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Phoesion.EFCore.PrecomputedViews" Version="0.0.1" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Phoesion.EFCore.PrecomputedViews --version 0.0.1                
#r "nuget: Phoesion.EFCore.PrecomputedViews, 0.0.1"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Phoesion.EFCore.PrecomputedViews as a Cake Addin
#addin nuget:?package=Phoesion.EFCore.PrecomputedViews&version=0.0.1

// Install Phoesion.EFCore.PrecomputedViews as a Cake Tool
#tool nuget:?package=Phoesion.EFCore.PrecomputedViews&version=0.0.1                

Phoesion.EFCore.PrecomputedViews

Phoesion.EFCore.PrecomputedViews is an Entity Framework Core library that enables the automatic computation and updating of dependent views based on changes in the database. This approach is particularly useful for scenarios where derived or computed properties need to stay in sync with related entities without manual intervention.

Features

  • Automatic View Updates: Automatically recompute and update dependent properties or views when related entities are modified.
  • Seamless Integration: Built on EF Core's SaveChanges pipeline, requiring minimal configuration.
  • Optimized Performance: Only updates affected views, reducing unnecessary computations.
  • Support for Dependency Events: Trigger recomputations based on entity events like addition, modification, or deletion.
  • Custom Handlers: Easily define handlers to compute views using your business logic.

Getting Started

Prerequisites

  • .NET 6 or later
  • Entity Framework Core

Installation

Add the library to your project:

dotnet add package Phoesion.EFCore.PrecomputedViews

Configuration

1. Enable Precomputed Views in your DbContext

Use the AddPrecomputedViews extension method to enable the interceptor,

either from your AddDbContext<> in the application host builder:

builder.Services.AddDbContext<dbContext>(optionsBuilder =>
{
    //setup context..
    //optionsBuilder.UseSqlite("Data Source=mydb.db");

    //add pre-computed views interceptor
    optionsBuilder.AddPrecomputedViews();
});

or in OnConfiguring in you dbContext class:

public class mydbContext : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        //setup context..
        //optionsBuilder.UseSqlite("Data Source=mydb.db");

        //Add precomputed-views interceptor
        optionsBuilder.AddPrecomputedViews();
    }
}

2. Define Models with Dependencies

Annotate your models with [DependsOn] to specify the dependencies and handlers.

Example 1: Pre-compute the Enrollments.Count() whenever an Enrollment is added
[DependsOn<Enrollment>(DependencyEvents.Added | DependencyEvents.Removed, typeof(EnrollmentAddedRemovedHandler))]
public class Student
{
    [Key]
    public int Id { get; set; }
    public string LastName { get; set; }
    public ICollection<Enrollment> Enrollments { get; set; }

    //have a pre-computed count of enrollments (this can be a read-heavy and compute-heavy count that we want to have pre-computed)
    public int EnrollmentCount { get; set; }

    //the handler that will update EnrollmentCount whenever an Enrollment is added/deleted
    class EnrollmentAddedRemovedHandler : IComputableView<dbContext, Enrollment>
    {
        public async ValueTask ComputeView(dbContext context, DependencyEvents ev, IEnumerable<Enrollment> changedDependencies)
        {
            //find students that are affected
            var studentIds = changedDependencies.Select(e => e.StudentId);

            //execute update 
            await context.Students
                        .Where(s => studentIds.Contains(s.Id))
                        .ExecuteUpdateAsync(e => e.SetProperty(
                            s => s.EnrollmentCount,
                            s => context.Enrollments.Count(e => e.StudentId == s.Id)));
        }
    }
}
Example 2: Pre-compute a complex query whenever an Enrollment is added/removed (keep latest id)
[DependsOn<Enrollment>(DependencyEvents.Added | DependencyEvents.Removed, typeof(EnrollmentAddedRemovedHandler))]
public class Course
{
    [Key]
    public int Id { get; set; }

    public ICollection<Enrollment> Enrollments { get; set; }

    //This will point to the latest Enrollment added, without needing to update it manually, using the CourseViewEnrollmentAddedHandler.ComputeView() method.
    public int? LatestEnrollmentIdAddedId { get; set; }
    public Enrollment? LatestEnrollmentIdAdded { get; set; }

    //=======================
    // View handler
    //=======================
    class EnrollmentAddedRemovedHandler : IComputableView<dbContext, Enrollment>
    {
        public async ValueTask ComputeView(dbContext context, DependencyEvents ev, IEnumerable<Enrollment> changedDependencies)
        {
            //find courses that are affected
            var courseIds = changedDependencies.Select(e => e.CourseId);

            //execute update
            await context.Courses
                        .Where(c => courseIds.Contains(c.Id))
                        .ExecuteUpdateAsync(e => e.SetProperty(
                            c => c.LatestEnrollmentIdAddedId,
                            c => context.Enrollments
                                         .Where(e => e.CourseId == c.Id)
                                         .OrderByDescending(e => e.Id)
                                         .Select(e => (int?)e.Id)
                                         .FirstOrDefault()));
        }
    }
}

Example Project

The repository includes a sample console application demonstrating how to use Phoesion.EFCore.PrecomputedViews. Check out the Program.cs file for detailed examples.


Contributing

Contributions are welcome! Feel free to open issues, suggest improvements, or submit pull requests.


License

This project is licensed under the MIT License.

Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 is compatible.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
0.0.1 42 12/18/2024