Sumapap.Persistence.DependencyInjection 1.2.1

The owner has unlisted this package. This could mean that the package is deprecated, has security vulnerabilities or shouldn't be used anymore.
dotnet add package Sumapap.Persistence.DependencyInjection --version 1.2.1
                    
NuGet\Install-Package Sumapap.Persistence.DependencyInjection -Version 1.2.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="Sumapap.Persistence.DependencyInjection" Version="1.2.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Sumapap.Persistence.DependencyInjection" Version="1.2.1" />
                    
Directory.Packages.props
<PackageReference Include="Sumapap.Persistence.DependencyInjection" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Sumapap.Persistence.DependencyInjection --version 1.2.1
                    
#r "nuget: Sumapap.Persistence.DependencyInjection, 1.2.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.
#:package Sumapap.Persistence.DependencyInjection@1.2.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Sumapap.Persistence.DependencyInjection&version=1.2.1
                    
Install as a Cake Addin
#tool nuget:?package=Sumapap.Persistence.DependencyInjection&version=1.2.1
                    
Install as a Cake Tool

Sumapap.Persistence.DependencyInjection

NuGet Version NuGet Downloads License GitHub Issues GitHub Stars GitHub Forks Contributions Welcome

๐Ÿ’ก Overview

Sumapap.Persistence.DependencyInjection provides fluent dependency injection builders and extensions for configuring Sumapap persistence repositories and caching. The package focuses on:

  • Type-safe fluent API for repository registration (scoped, transient, generic)
  • Visitor pattern architecture for extensible repository decoration
  • Opt-in caching configuration with fine-grained method control
  • Separation of DI concerns from core persistence abstractions
  • Modern C# 14 extension syntax for better IntelliSense

The goal is to enable clean Infrastructure layer DI configuration while keeping Sumapap.Persistence.Domain abstractions safe for the Domain layer.

โœจ Why use Sumapap.Persistence.DependencyInjection?

  • Clean Architecture Compliance: Keeps DI configuration separate from domain abstractions, allowing Sumapap.Persistence.Domain to remain in the Domain layer
  • Fluent Registration API: Type-safe, discoverable fluent syntax for repository registration with compile-time safety
  • Visitor Pattern Extensibility: Add caching, logging, validation, or other cross-cutting concerns via visitors without modifying core code
  • Opt-in Caching: Per-repository cache configuration with granular method control (not forced globally)
  • Generic Repository Support: Register open generic repositories for automatic entity resolution
  • Provider-Agnostic: Cache metadata stored in registry for consumption by any cache provider (FusionCache, Redis, etc.)

๐Ÿš€ Quick start

  1. Add the package to your Infrastructure layer project:
dotnet add package Sumapap.Persistence.DependencyInjection
  1. Register repositories with the fluent builder:
builder.Services.AddSumapap()
    .WithRepositories(repos => repos
        .AddScopedRepository<UserRepository, User>()
        .AddTransientRepository<IProductRepository, ProductRepository, Product>()
    );
  1. Enable opt-in caching for specific repositories:
builder.Services.AddSumapap()
    .WithRepositories(repos => repos
        .AddScopedRepository<UserRepository, User>()
        .AllowCaching(config =>
        {
            config.Duration = TimeSpan.FromMinutes(10);
        })
        .Builder
        
        .UseRepositoryCaching() // Register visitor
    );
  1. Add a cache provider to consume the registry (optional):
builder.Services.AddSumapap()
    .WithRepositories(repos => repos
        // ... registrations with AllowCaching()
        .UseRepositoryCaching()
    )
    .UseFusionCache(); // Provider decorates based on metadata

๐Ÿ›  Features and usage

Fluent Repository Registration

AddScopedRepository() - Register scoped repository (most common):

.WithRepositories(repos => repos
    // Concrete implementation only
    .AddScopedRepository<UserRepository, User>()
    
    // With abstraction
    .AddScopedRepository<IOrderRepository, OrderRepository, Order>()
)

AddTransientRepository() - Register transient repository:

.WithRepositories(repos => repos
    .AddTransientRepository<IProductRepository, ProductRepository, Product>()
)

AddSingletonRepository() - Register singleton repository (rare):

.WithRepositories(repos => repos
    .AddSingletonRepository<ICatalogRepository, CatalogRepository, Catalog>()
)

Generic Repository Registration

AddGenericRepository() - Register open generic repositories:

.WithRepositories(repos => repos
    // Register IRepository<> for all entities
    .AddGenericRepository(
        typeof(IRepository<>), 
        typeof(EfRepository<>), 
        ServiceLifetime.Scoped
    )
    .AllowCaching() // Applies to all entity types
)

AddGenericRepositories() - Register multiple EF Core generic repository types:

.WithRepositories(repos => repos
    .AddGenericRepositories(ServiceLifetime.Scoped) // Registers IReadRepository<>, IWriteRepository<>, etc.
)

Visitor Pattern Architecture

The library uses the Visitor Pattern for extensible repository decoration:

Repository Registration โ†’ Visitor Processing โ†’ Service Registration
                              โ†“
                     IRepositoryRegistrationVisitor
                              โ†“
                 (e.g., CachingRepositoryVisitor)

IRepositoryRegistrationVisitor - Interface for processing registrations:

public interface IRepositoryRegistrationVisitor
{
    void Visit(RepositoryRegistrationEntry entry, IServiceCollection services);
}

UseRepositoryCaching() - Register the caching visitor:

.WithRepositories(repos => repos
    .AddScopedRepository<UserRepository, User>()
    .AllowCaching()
    .Builder
    
    .UseRepositoryCaching() // Registers CachingRepositoryVisitor
)

Custom Visitors - Implement for logging, validation, etc.:

public class LoggingRepositoryVisitor : IRepositoryRegistrationVisitor
{
    public void Visit(RepositoryRegistrationEntry entry, IServiceCollection services)
    {
        Console.WriteLine($"Registered {entry.ImplementationType.Name} for {entry.EntityType.Name}");
    }
}

// Register custom visitor
.WithRepositories(repos => repos
    .AddScopedRepository<UserRepository, User>()
    .Builder
    .UseVisitor(new LoggingRepositoryVisitor())
)

Opt-In Caching Configuration

AllowCaching() - Enable caching with default configuration:

.AddScopedRepository<UserRepository, User>()
.AllowCaching() // Default: 5 minutes, all read methods

AllowCaching(config โ‡’ ...) - Fine-grained cache configuration:

.AddScopedRepository<ProductRepository, Product>()
.AllowCaching(config =>
{
    config.Duration = TimeSpan.FromMinutes(10);
    config.KeyPrefix = "product";
    
    // Granular method control
    config.Methods.Clear();
    config.Methods["FindAsync"] = true;
    config.Methods["GetAllAsync"] = true;
    config.Methods["CountAsync"] = false; // Don''t cache Count
    
    // Provider-specific metadata
    config.Metadata["Priority"] = "High";
    config.Metadata["Tags"] = new[] { "catalog", "inventory" };
})

Repository Registration Entry

RepositoryRegistrationEntry - Metadata about a registered repository:

public sealed class RepositoryRegistrationEntry
{
    public Type? AbstractionType { get; init; }
    public required Type ImplementationType { get; init; }
    public required Type EntityType { get; init; }
    public ServiceLifetime Lifetime { get; init; }
    public bool AllowCaching { get; set; }
    public RepositoryCacheConfiguration? CachingConfiguration { get; set; }
}

Repository Configurator

RepositoryConfigurator - Fluent configurator returned after registration:

public sealed class RepositoryConfigurator<TImpl, TEntity>
{
    public RepositoryConfigurator<TImpl, TEntity> AllowCaching(
        Action<RepositoryCacheConfiguration>? configure = null);
    
    public RepositoryRegistrationBuilder Builder { get; }
}

Chaining - Return to builder for next registration:

.AddScopedRepository<UserRepository, User>()
.AllowCaching()
.Builder // Return to RepositoryRegistrationBuilder

.AddScopedRepository<ProductRepository, Product>()
.AllowCaching()
.Builder

.UseRepositoryCaching()

Complete Example

Full DI setup with repositories, generics, and caching:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddSumapap()
    .WithRepositories(repos => repos
        // Specific repositories with caching
        .AddScopedRepository<IUserRepository, UserRepository, User>()
        .AllowCaching(config =>
        {
            config.Duration = TimeSpan.FromMinutes(15);
            config.KeyPrefix = "user";
            config.Methods.EnableAllReads();
        })
        .Builder
        
        .AddScopedRepository<IProductRepository, ProductRepository, Product>()
        .AllowCaching(config =>
        {
            config.Duration = TimeSpan.FromMinutes(30);
            config.KeyPrefix = "product";
        })
        .Builder
        
        // Generic repositories for other entities
        .AddGenericRepository(
            typeof(IRepository<>), 
            typeof(EfRepository<>), 
            ServiceLifetime.Scoped
        )
        .AllowCaching(config =>
        {
            config.Duration = TimeSpan.FromMinutes(5);
        })
        .Builder
        
        // Register caching visitor
        .UseRepositoryCaching()
    )
    .UseFusionCache(); // Cache provider decorates based on metadata

var app = builder.Build();
app.Run();

Cache Registry Inspection

Access the cache registry for testing or runtime inspection:

var registry = serviceProvider.GetRequiredService<RepositoryCacheRegistry>();

foreach (var entry in registry.CachedRepositories)
{
    Console.WriteLine($"Repository: {entry.RepositoryType.Name}");
    Console.WriteLine($"Entity: {entry.EntityType.Name}");
    Console.WriteLine($"Duration: {entry.Configuration.Duration}");
    Console.WriteLine($"Methods: {string.Join(", ", entry.Configuration.Methods.Keys)}");
}

โš ๏ธ Notes & best practices

โœ… Do

  • Reference from Infrastructure layer only - this package should never be referenced from Domain or Application layers
  • Use Sumapap.Persistence.Domain in Domain layer for abstractions (IEntity, repository interfaces)
  • Use scoped lifetime for most repositories (aligns with EF Core DbContext lifetime)
  • Call UseRepositoryCaching() after all AllowCaching() calls to register the visitor
  • Chain via .Builder to return to RepositoryRegistrationBuilder for next registration
  • Customize cache duration based on data volatility (shorter for frequently changing data)
  • Use generic repositories for entities without custom query logic

โŒ Don''t

  • Never reference this package from Domain layer - violates Clean Architecture dependency rules
  • Avoid calling AllowCaching() without UseRepositoryCaching() - cache metadata is recorded but never consumed
  • Don''t cache write operations - only read methods should be cached (enforced by default)
  • Avoid singleton lifetime for repositories that depend on scoped DbContext
  • Don''t forget .Builder when chaining registrations - IntelliSense won''t show next registration methods

Migration from Previous Versions

If you were using Sumapap.Persistence directly for DI:

Before:

using Sumapap.Persistence; // Everything in one package

After:

using Sumapap.Persistence.Domain; // Domain abstractions
using Sumapap.Persistence.DependencyInjection; // DI configuration

Registration code remains the same - only package references change.

Testing Recommendations

When testing DI configuration:

  1. Verify registrations by resolving services from IServiceProvider
  2. Inspect cache registry via RepositoryCacheRegistry to validate caching configuration
  3. Test visitor behavior by implementing custom visitors and asserting side effects
  4. Mock repository implementations in unit tests, not DI configuration

โญ License

Distributed under the MIT License. See the LICENSE file in the repository for more information.

๐Ÿšฉ Contact

GitHub @muhammadirwanto-dev
Project Url https://github.com/muhammadirwanto-dev/sumapap

โ˜• Support

If you like this project and want to support it, you can buy me a coffee๏ธŽ. Your coffee will keep me awake while developing this project โ˜•.

<p align="center"> <a href="https://buymeacoffee.com/muhirwanto.dev"> <img src="https://img.buymeacoffee.com/button-api/?text=Buy%20me%20a%20coffee&emoji=&slug=muhirwanto.dev&button_colour=FFDD00&font_colour=000000&font_family=Comic&outline_colour=000000&coffee_colour=ffffff" alt="Buy Me A Coffee"> </a> </p>

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
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