Zonit.Extensions.Databases.SqlServer 1.0.8

dotnet add package Zonit.Extensions.Databases.SqlServer --version 1.0.8
                    
NuGet\Install-Package Zonit.Extensions.Databases.SqlServer -Version 1.0.8
                    
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="Zonit.Extensions.Databases.SqlServer" Version="1.0.8" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Zonit.Extensions.Databases.SqlServer" Version="1.0.8" />
                    
Directory.Packages.props
<PackageReference Include="Zonit.Extensions.Databases.SqlServer" />
                    
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 Zonit.Extensions.Databases.SqlServer --version 1.0.8
                    
#r "nuget: Zonit.Extensions.Databases.SqlServer, 1.0.8"
                    
#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=Zonit.Extensions.Databases.SqlServer&version=1.0.8
                    
Install Zonit.Extensions.Databases.SqlServer as a Cake Addin
#tool nuget:?package=Zonit.Extensions.Databases.SqlServer&version=1.0.8
                    
Install Zonit.Extensions.Databases.SqlServer as a Cake Tool

Zonit.Extensions.Databases

Zonit.Extensions.Databases is a flexible library for building repositories and handling CRUD operations on databases.
It provides abstractions and interfaces, making it easy to manage database access and to extend your repositories with custom logic, REST API data, or other external sources.
You can use your own repositories and expand them anytime with additional functions, while still keeping your codebase clean and modular.

📦 NuGet Packages

Abstraction

Install-Package Zonit.Extensions.Databases.Abstractions 

NuGet Version NuGet

SQL Server Implementation

Install-Package Zonit.Extensions.Databases.SqlServer

NuGet Version NuGet

🚀 Features

  • Speed up repository work � focus on your logic, not on database �plumbing�.
  • Simple abstractions for common CRUD operations.
  • Full support for DTO models.
  • Built-in support for query building (Where, Include, Select, paging, ordering, and more).
  • Plug-and-play: Easily extend repositories with custom methods or external data without touching the extension itself.

💡 New! Extension Methods for External Data

You can easily include properties and data from outside your database, for example, by pulling from an external API or service.
This is useful when your model should have extra fields, computed properties, or needs to include data fetched live (not loaded from your DB).

How it works

  1. Create an Extension Class implementing IDatabaseExtension<TModel>
    • In this class, implement business logic for fetching or computing the desired data.
using Zonit.Extensions.Databases.Examples.Entities;

namespace Zonit.Extensions.Databases.Examples.Extensions;

public class UserExtension : IDatabaseExtension<UserModel>
{
    public async Task<UserModel> InitializeAsync(Guid userId, CancellationToken cancellationToken = default)
    {
        // Here you can call a REST API or any other data source.
        var model = new UserModel { 
            Id = userId,
            Name = "UserName",
        };
        return await Task.FromResult(model);
    }
}
  1. Reference the extension using .Extension() in your repository query chain.
var user = await _userRepository.Extension(x => x.UserExtension).GetByIdAsync(userId);

The .Extension(x => x.UserExtension) call tells the repository to supply or load the UserExtension property for your entity.
This can be virtual, NotMapped, or simply a property populated on demand.

Use case:
Suppose your Blog entity has a UserModel? User property, but you want to always fetch the latest user data from an API instead of the DB.
Simply create an extension and reference it. The fetching, mapping, and attach process is handled by the extension system for you.

📖 Example Usage

Entity Model

public class Blog
{
    public Guid Id { get; set; }

    [NotMapped]
    public UserModel? User { get; set; }
    public Guid? UserId { get; set; }

    public string Title { get; set; } = string.Empty;
    public string Content { get; set; } = string.Empty;
    public DateTime Created { get; private set; } = DateTime.UtcNow;
}

DTO Example

internal class BlogDto(Blog x)
{
    public string Id { get; set; } = $"Id: {x.Id}";
    public string Title { get; set; } = $"Title: {x.Title}";
    public string Content { get; set; } = $"Content: {x.Content}";
    public string Created { get; set; } = $"Created: {x.Created:G}";
}

public class UserModel
{
    public Guid Id { get; set; }
    public string Name { get; set; } = string.Empty;
}

Repository Implementation

public interface IBlogRepository : IDatabaseRepository<Blog> { }

internal class BlogRepository(DatabaseContext _context)
    : DatabaseRepository<Blog>(_context), IBlogRepository { }

Service Registration / Dependency Injection

builder.Services.AddDbSqlServer<DatabaseContext>();

builder.Services.AddTransient<IBlogRepository, BlogRepository>();

⚙️ CRUD and Query Operations

Create

var blog = await _blogRepository.AddAsync(new Blog
{
    Title = "Hello World",
    Content = "Example content"
});

Read (Single or DTO)

var blogSingle = await _blogRepository.GetByIdAsync(blogId);
var blogSingleDto = await _blogRepository.GetByIdAsync<BlogDto>(blogId);

Query First (with conditions)

var firstBlog = await _blogRepository.Where(x => x.Title == "Hello World").GetFirstAsync();
var firstBlogDto = await _blogRepository.Where(x => x.Title == "Hello World").GetFirstAsync<BlogDto>();

Update

var updated = await _blogRepository.UpdateAsync(blog.Id, entity =>
{
    entity.Title = "New Title";
});

or

blog.Title = "New Title";
var updated = await _blogRepository.UpdateAsync(blog);

Delete

var deleted = await _blogRepository.DeleteAsync(blog.Id);

or

var deleted = await _blogRepository.DeleteAsync(blog);

Read All

var blogs = await _blogRepository.GetListAsync();
var blogsDto = await _blogRepository.GetListAsync<BlogDto>();

📃 Repository APIs

Below is an overview of the main interfaces and methods.
(See XML comments in code for details.)


IDatabaseRepository<TEntity>

// Queryable & CRUD interface for TEntity
IDatabaseAsQueryable<TEntity> AsQuery();

// Query customization:
IDatabaseQueryOperations<TEntity> Extension(Expression<Func<TEntity, object?>> extensionExpression);
IDatabaseQueryOperations<TEntity> Select<TDto>(Expression<Func<TEntity, TDto>> selector);

IDatabaseQueryOperations<TEntity> Include(Expression<Func<TEntity, object?>> includeExpression);
IDatabaseQueryOperations<TEntity> Where(Expression<Func<TEntity, bool>> whereExpression);

IDatabaseMultipleQueryable<TEntity> Skip(int count);
IDatabaseMultipleQueryable<TEntity> Take(int count);

IDatabaseMultipleRepository<TEntity> OrderBy(Expression<Func<TEntity, object>> keySelector);
IDatabaseMultipleRepository<TEntity> OrderByDescending(Expression<Func<TEntity, object>> keySelector);

// Single record access:
Task<TEntity?> GetByIdAsync(int id, CancellationToken cancellationToken = default);
Task<TDto?> GetByIdAsync<TDto>(int id, CancellationToken cancellationToken = default);
Task<TEntity?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default);
Task<TDto?> GetByIdAsync<TDto>(Guid id, CancellationToken cancellationToken = default);
Task<TEntity?> GetAsync(CancellationToken cancellationToken = default);
Task<TDto?> GetAsync<TDto>(CancellationToken cancellationToken = default);

// Existence check:
Task<bool> AnyAsync(CancellationToken cancellationToken = default);

// Add/Update/Delete:
Task<TEntity> AddAsync(TEntity entity, CancellationToken cancellationToken = default);
Task<TDto> AddAsync<TDto>(TEntity entity, CancellationToken cancellationToken = default);

Task<bool> UpdateAsync(int id, Action<TEntity> updateAction, CancellationToken cancellationToken = default);
Task<bool> UpdateAsync(Guid id, Action<TEntity> updateAction, CancellationToken cancellationToken = default);
Task<bool> UpdateAsync(TEntity entity, CancellationToken cancellationToken = default);

Task<bool> DeleteAsync(int entity, CancellationToken cancellationToken = default);
Task<bool> DeleteAsync(Guid entity, CancellationToken cancellationToken = default);
Task<bool> DeleteAsync(TEntity entity, CancellationToken cancellationToken = default);

// Multiple records access:
Task<IReadOnlyCollection<TEntity>?> GetListAsync(CancellationToken cancellationToken = default);
Task<IReadOnlyCollection<TDto>?> GetListAsync<TDto>(CancellationToken cancellationToken = default);

Task<TEntity?> GetFirstAsync(CancellationToken cancellationToken = default);
Task<TDto?> GetFirstAsync<TDto>(CancellationToken cancellationToken = default);

Task<int?> UpdateRangeAsync(Action<TEntity> updateAction, CancellationToken cancellationToken = default);
Task<int> GetCountAsync(CancellationToken cancellationToken = default);

Note:
The deprecated IDatabasesRepository<TEntity> interface has been removed and is not supported anymore. Please migrate to IDatabaseRepository<TEntity> and related interfaces for all new development.


🌟 Why use this extension?

  • Build repository pattern code faster and cleaner.
  • Decouple your database logic from your app logic.
  • Easily extend your repositories with custom business logic or fetch data from external APIs with zero changes to the

ℹ️ For more examples

See the Examples project included in the repository.

Product Compatible and additional computed target framework versions.
.NET 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.  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 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
1.0.8 29 5/28/2025
1.0.7 32 5/28/2025
1.0.6 40 5/24/2025
1.0.5 110 5/19/2025
1.0.4 132 5/7/2025
1.0.3 106 4/25/2025
1.0.2 156 4/23/2025
1.0.1 152 4/22/2025
1.0.0 74 4/19/2025
0.1.14 77 4/19/2025
0.1.13 84 4/19/2025
0.1.12 191 4/18/2025
0.1.11 177 4/17/2025
0.1.10 185 4/17/2025
0.1.9 182 4/17/2025
0.1.8 174 4/17/2025
0.1.7 174 4/17/2025
0.1.6 192 4/16/2025
0.1.5 97 1/29/2025
0.1.4 131 6/13/2024
0.1.3 113 6/5/2024
0.1.2 113 6/4/2024
0.1.1 109 6/4/2024
0.1.0 118 6/3/2024
0.0.13 108 5/1/2024
0.0.12 114 4/28/2024
0.0.11 132 4/24/2024
0.0.10 120 4/22/2024
0.0.9 116 4/21/2024
0.0.8 136 4/21/2024
0.0.7 125 4/18/2024
0.0.6 118 4/18/2024