Moclawr.EfCore 1.0.3

There is a newer version of this package available.
See the version list below for details.
dotnet add package Moclawr.EfCore --version 1.0.3
                    
NuGet\Install-Package Moclawr.EfCore -Version 1.0.3
                    
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="Moclawr.EfCore" Version="1.0.3" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Moclawr.EfCore" Version="1.0.3" />
                    
Directory.Packages.props
<PackageReference Include="Moclawr.EfCore" />
                    
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 Moclawr.EfCore --version 1.0.3
                    
#r "nuget: Moclawr.EfCore, 1.0.3"
                    
#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.
#addin nuget:?package=Moclawr.EfCore&version=1.0.3
                    
Install Moclawr.EfCore as a Cake Addin
#tool nuget:?package=Moclawr.EfCore&version=1.0.3
                    
Install Moclawr.EfCore as a Cake Tool

Moclawr.EfCore

NuGet

Overview

Moclawr.EfCore provides a comprehensive set of tools and abstractions for working with Entity Framework Core in .NET applications. It implements the repository pattern with separate command and query repositories (CQRS), fluent query builders, and advanced querying capabilities, making database operations more maintainable and efficient.

Features

  • Repository Pattern Implementation: Separate repositories for commands and queries
  • CQRS Approach: Command and Query Responsibility Segregation with specialized repositories
  • Fluent Query Building: Expressive, chainable API for building complex queries
  • Advanced Filtering: Specifications pattern for encapsulating filtering logic
  • Projection Support: Map entities to DTOs directly in queries for optimal performance
  • Transaction Management: Simplified transaction handling across multiple operations
  • Pagination: Built-in support for paginated results
  • Dapper Integration: Combine the power of EF Core and Dapper for complex queries
  • Auditing Support: Automatic tracking of create/update timestamps and users
  • Soft Delete: Built-in support for soft delete operations

Installation

Install the package via NuGet Package Manager:

dotnet add package Moclawr.EfCore

Usage

Setting Up the DbContext

Create a DbContext that inherits from BaseDbContext:

using EfCore;
using Microsoft.EntityFrameworkCore;

public class ApplicationDbContext : BaseDbContext
{
    public ApplicationDbContext(DbContextOptions options) : base(options)
    {
    }
    
    public DbSet<Product> Products { get; set; }
    public DbSet<Category> Categories { get; set; }
    
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        
        // Configure entities
        modelBuilder.Entity<Product>()
            .HasOne(p => p.Category)
            .WithMany(c => c.Products)
            .HasForeignKey(p => p.CategoryId);
    }
}

Registering Services

In your Program.cs or Startup.cs:

using EfCore;
using Microsoft.EntityFrameworkCore;

// Register EF Core services
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

// Register repositories
builder.Services.AddScoped<ICommandRepository<Product, int>, CommandRepository<Product, int>>();
builder.Services.AddScoped<IQueryRepository<Product, int>, QueryRepository<Product, int>>();

Using the Query Repository

using EfCore.Repositories;
using Shared.Utils;

public class ProductService
{
    private readonly IQueryRepository<Product, int> _queryRepository;
    
    public ProductService(IQueryRepository<Product, int> queryRepository)
    {
        _queryRepository = queryRepository;
    }
    
    public async Task<Product?> GetProductByIdAsync(int id, CancellationToken cancellationToken = default)
    {
        return await _queryRepository.GetByIdAsync(
            id,
            builder: query => query
                .Include(p => p.Category)
                .Include(p => p.Tags),
            cancellationToken: cancellationToken
        );
    }
    
    public async Task<PagedResult<ProductDto>> GetPaginatedProductsAsync(
        string? searchTerm, 
        int page, 
        int pageSize,
        CancellationToken cancellationToken = default)
    {
        return await _queryRepository.GetPagedListAsync<ProductDto>(
            page,
            pageSize,
            filter: p => string.IsNullOrEmpty(searchTerm) || p.Name.Contains(searchTerm),
            orderBy: q => q.OrderByDescending(p => p.CreatedAt),
            builder: query => query.Include(p => p.Category),
            cancellationToken: cancellationToken
        );
    }
    
    public async Task<List<ProductSummaryDto>> GetFeaturedProductsAsync(CancellationToken cancellationToken = default)
    {
        return await _queryRepository.GetProjectedListAsync<ProductSummaryDto>(
            filter: p => p.IsFeatured && p.IsActive,
            orderBy: q => q.OrderBy(p => p.DisplayOrder),
            cancellationToken: cancellationToken
        );
    }
}

Using the Command Repository

using EfCore.Repositories;

public class ProductManagementService
{
    private readonly ICommandRepository<Product, int> _commandRepository;
    
    public ProductManagementService(ICommandRepository<Product, int> commandRepository)
    {
        _commandRepository = commandRepository;
    }
    
    public async Task<int> CreateProductAsync(Product product, CancellationToken cancellationToken = default)
    {
        await _commandRepository.AddAsync(product, cancellationToken);
        await _commandRepository.SaveChangesAsync(cancellationToken);
        return product.Id;
    }
    
    public async Task UpdateProductAsync(Product product, CancellationToken cancellationToken = default)
    {
        _commandRepository.Update(product);
        await _commandRepository.SaveChangesAsync(cancellationToken);
    }
    
    public async Task DeleteProductAsync(int id, CancellationToken cancellationToken = default)
    {
        await _commandRepository.DeleteAsync(id, cancellationToken);
        await _commandRepository.SaveChangesAsync(cancellationToken);
    }
    
    public async Task<bool> ExecuteInTransactionAsync(Func<Task<bool>> operation, CancellationToken cancellationToken = default)
    {
        return await _commandRepository.ExecuteInTransactionAsync(operation, cancellationToken);
    }
}

Using Fluent Query Builders

using EfCore.Builders;

public async Task<List<Product>> GetProductsByComplexCriteriaAsync(
    string? categoryName, 
    decimal? minPrice,
    decimal? maxPrice,
    bool inStock,
    CancellationToken cancellationToken = default)
{
    return await _queryRepository.GetListAsync(
        builder: query => query
            .Include(p => p.Category)
            .Include(p => p.Inventory)
            .WhereIf(
                !string.IsNullOrEmpty(categoryName),
                p => p.Category.Name == categoryName
            )
            .WhereIf(
                minPrice.HasValue,
                p => p.Price >= minPrice.Value
            )
            .WhereIf(
                maxPrice.HasValue,
                p => p.Price <= maxPrice.Value
            )
            .WhereIf(
                inStock,
                p => p.Inventory.StockQuantity > 0
            ),
        cancellationToken: cancellationToken
    );
}

Using Dapper for Complex Queries

public async Task<List<ProductSalesReport>> GetProductSalesReportAsync(
    DateTime startDate,
    DateTime endDate,
    CancellationToken cancellationToken = default)
{
    var sql = @"
        SELECT p.Id, p.Name, p.Price, 
               COUNT(o.Id) AS OrderCount, 
               SUM(oi.Quantity) AS TotalQuantity,
               SUM(oi.Quantity * p.Price) AS TotalRevenue
        FROM Products p
        JOIN OrderItems oi ON p.Id = oi.ProductId
        JOIN Orders o ON oi.OrderId = o.Id
        WHERE o.OrderDate BETWEEN @StartDate AND @EndDate
        GROUP BY p.Id, p.Name, p.Price
        ORDER BY TotalRevenue DESC";
    
    var parameters = new { StartDate = startDate, EndDate = endDate };
    
    return await _queryRepository.QueryAsync<ProductSalesReport>(
        sql, 
        parameters, 
        CommandType.Text,
        cancellationToken
    );
}

Integration with Other Moclawr Packages

This package works seamlessly with other packages in the Moclawr ecosystem:

  • Moclawr.Core: Provides core utilities and extensions
  • Moclawr.Domain: Contains base entity types and builder interfaces
  • Moclawr.Shared: Contains interfaces for entities and pagination utilities

Requirements

  • .NET 9.0 or higher
  • Microsoft.EntityFrameworkCore 9.0.5 or higher
  • Microsoft.EntityFrameworkCore.Relational 9.0.5 or higher
  • Dapper 2.1.66 or higher
  • Mapster 7.4.0 or higher

License

This package is licensed under the MIT License.

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  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
2.0.1 179 5/24/2025
2.0.0 63 5/24/2025
1.0.3 60 5/24/2025
1.0.2 136 5/22/2025
1.0.1 140 5/21/2025
1.0.0 211 5/15/2025

Added improved XML documentation and bug fixes.