JobTech.Forte.Data 3.0.3

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

Forte NuGet version (JobTech.Forte.Data)

Project Overview

Forte (JobTech.Forte.Data) is a C# ORM library — Fast Object Relational Translation Engine — that provides a generic Repository pattern over ADO.NET. It uses convention-based stored procedure mapping and compiled expression trees for high performance, eliminating runtime reflection after the first call for any given type.

Modern .NET projects often reach for a full-featured ORM like Entity Framework Core, NHibernate, or even the lighter Dapper. Each is a solid choice — but each comes with trade-offs that Forte is designed to avoid.

Performance

Forte compiles all property access, object construction, and method invocation into delegates on first use. After the initial call for any given type, there is zero reflection overhead at runtime. Compare this to:

  • Entity Framework Core — change tracking, query translation, and the LINQ pipeline add measurable overhead even for simple reads. EF is optimized for developer ergonomics, not raw throughput.
  • NHibernate — a powerful but heavyweight framework with session management, proxies, and a significant startup cost.
  • Dapper — lightweight and fast, but it relies on ILGenerator-based emit at runtime. Forte's compiled expression tree approach is equally fast and more transparent.

Forte is purpose-built for high-throughput scenarios where stored procedures are already in use and you need mapping to be as close to hand-rolled ADO.NET speed as possible.

Lightweight and Elegant

The entire library is a single project with one public class (Repository<T>) and a single NuGet dependency (System.Configuration.ConfigurationManager). There is no migration engine, no change tracker, no model builder, no code-generation tooling, and no design-time assembly.

  • EF Core ships with dozens of packages (Microsoft.EntityFrameworkCore, *Design, *SqlServer, *Tools, etc.) and requires a DbContext subclass, entity configuration, and often migrations.
  • NHibernate requires mapping files or attribute decoration, a SessionFactory, and an ISession lifetime to manage.
  • With Forte, drop in the NuGet package, call Configure() once, and write a stored procedure that matches the naming convention. That's it.

Simplicity and Extensibility

Convention over configuration means no mapping attributes, no fluent configuration, no XML. If your stored procedure is named SelectCustomer, Forte finds it automatically. Need to customize parameter binding or result mapping? Override a single virtual method in your repository subclass:

// Custom input binding — override just what you need
protected override void BindInput(DbCommand cmd, IDictionary parameters)
{
    base.BindInput(cmd, parameters);
    cmd.Parameters["@AsOfDate"].Value = DateTime.UtcNow;
}

This is in contrast to EF Core's IEntityTypeConfiguration<T> fluent API or NHibernate's ClassMap<T>, both of which require learning a separate configuration DSL before writing a single query.

Open Source and NuGet Access

Forte is MIT-licensed and published to NuGet as JobTech.Forte.Data. There are no commercial tiers, no licensing restrictions, and no runtime fees. The full source is auditable in a single directory — useful in regulated environments where dependency vetting is required.

Forte Dapper EF Core NHibernate
Runtime reflection after first call No Minimal Yes Yes
NuGet dependencies 1 1 10+ 10+
Requires stored procedures Yes No No No
Change tracking No No Yes Yes
Migration tooling No No Yes Yes
Learning curve Low Low Medium High

Forte is the right choice when your data access layer is already built around stored procedures, you need predictable performance, and you want a thin mapping layer with no magic.


A Short Example

Consider the following simplified console application making use of Forte to retrieve a list of customers.

public class Customer
{
	public int ID { get; set; }
	public string FirstName { get; set; }
	public string LastName { get; set; }
}
using JobTech.Forte.Data;
using System.Configuration;

class Program
{
    // Note: alternatively this can be placed in appsettings if using configuration builder.
    var connSettings = new ConnectionStringSettingsCollection()
    {
        { new ConnectionStringSettings("Default", "Server=<server>;Database=<db>;Trusted_Connection=True;") }
    };
    
    // Call once at startup.
    RepositoryService.Configure(connSettings);

	static void Main(string[] args)
	{
		var repository = new Repository<Customer>();
		var results = repository.SelectAsync().Result;

		foreach (var customer in results)
		{
			Console.WriteLine("First: " + customer.FirstName);
			Console.WriteLine("Last: " + customer.LastName);
		}
	}
}

...and the SQL

CREATE TABLE Customer
(
	ID int IDENTITY(1,1) NOT NULL,
	LastName varchar(100) NOT NULL,
	FirstName varchar(100) NOT NULL
)

CREATE PROCEDURE SelectCustomer
AS
	BEGIN
	SELECT ID,
		   FirstName,
		   LastName
	FROM   Customer
END

Deriving from Repository<T>

The example above uses Repository<T> directly, which works for simple cases. In a real application you will almost always want to create your own base class that derives from Repository<T>. This gives you a single place to inject cross-cutting concerns — authentication context, cancellation, audit parameters, logging — shared across every repository in your project.

public class RepositoryDerived<T> : Repository<T>
{
    public ClaimsIdentity Identity { get; set; }
    public CancellationToken CancelToken { get; set; }

    public RepositoryDerived()
    {
        this.CancelToken = default;
    }

    // Add a shared output parameter to every SelectAsync call
    protected override void BindInput(DbCommand cmd, IDictionary parameters)
    {
        base.BindInput(cmd, parameters);

        var rowCount = cmd.CreateParameter();
        rowCount.ParameterName = nameof(rowCount);
        rowCount.Direction = ParameterDirection.Output;
        rowCount.DbType = DbType.Int32;

        cmd.Parameters.Add(rowCount);
    }

    // Wrap ExecuteAsync to inject identity and return the mutated item
    public virtual async Task<IList<T>> ExecuteAsync(T item,
        TransactionType transactionType = TransactionType.Insert, IDictionary parameters = null)
    {
        try
        {
            parameters ??= new Dictionary<string, object>();

            if (this.Identity != null)
            {
                parameters[nameof(Identity.Name)] = this.Identity.Name;
            }

            await base.ExecuteAsync(item, transactionType, parameters, this.CancelToken)
                      .ConfigureAwait(false);

            return new List<T> { item };
        }
        catch (OperationCanceledException)
        {
            throw;
        }
    }
}

Key extension points shown above:

Override Purpose
BindInput(DbCommand, IDictionary) Appends a shared rowCount output parameter to every SelectAsync call without touching individual repositories
Custom ExecuteAsync Pulls ClaimsIdentity.Name from the instance and passes it as a named parameter, keeping auth concerns out of calling code
CancelToken property Stores a CancellationToken on the instance so callers don't need to thread it through every call site

Your concrete repositories then derive from this class, not from Repository<T> directly:

public class CustomerRepository : RepositoryDerived<Customer>
{
    // Only override what is specific to Customer — everything else is inherited
}
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 was computed.  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
3.0.3 110 4/20/2026
Loading failed

* Target .NET 8.0 LTS