Bowtie 0.0.3

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

**Note**: This is a very early version of Bowtie ORM. For preview purposes only
Some of the APIs are still in flux and may change without warning.
And some of the features are not fully implemented or fully tested yet.
Please use it at your own risk.

Bowtie is a lightweight, fluent Object-Relational Mapper (ORM) for .NET.
It makes interacting with your database straightforward by combining basic Active Record patterns with type-safe Lambda Expressions.
It targets `.NET Standard 2.0` and aiming to provide max compatibility, and supports both SQLServer and MySQL database dialects.

## Configuration

Bowtie automatically reads database connection settings from either `bowtie.json` or `appsettings.json` located in the application's root directory:

```json
{
  "connections": [
    {
      "name": "myconn",
      "connectionString": "Server=localhost;Database=mydb;Uid=sa;Pwd=mypassword;",
      "databaseType": "MYSQL" // Supported values: MYSQL, MSSQL
    }
  ]
} 

Connection Management

You can initialize a connection instance anywhere in your code like this:

var db = DB.Connect();  // Uses the first connection from the config by default
var db = DB.Connect("myconn");  // Connects using the named connection from the config

And the connection would be released after the first query is executed. To keep the connection alive for multiple operations, you can call db.KeepAlive() to prevent auto-disposal after the first query, and then call db.Dispose() manually when you're done with all your database operations.

var db = DB.Connect().KeepAlive();
// your database operations here
db.Dispose();

or you can use using statement to automatically dispose the connection when it's out of scope:

using (var db = DB.Connect())
{
    // your database operations here
}

Connection Auto-Disposal

When using functions directly from DB class, the database connection would be automatically created and disposed for you. For example:

var users = DB.From<User>()
  .Where(u => u.Age > 18)
  .Select();

Defining Entities

Map your domain models to database tables by inheriting from BaseEntity. Use [Table] to specify table names, and [Column] to map properties to database columns.

// Maps this class to the "orders" table in the database
// If the table name is the same as the class name, you can omit the attribute
[Table("orders")]
public class Order : BaseEntity
{
    [Column(PK = true, Ignore = When.Insert | When.Update)]
    public int Id { get; set; }

    [Column("user_id")]
    public long UserId { get; set; }

    [Column("total_amount")]
    public decimal TotalAmount { get; set; }

    [Column("status")]
    public OrderStatus Status { get; set; } = OrderStatus.Pending;

    // use Ignore attribute to exclude this property from being included in INSERT or UPDATE statements
    [Column("created_at", Ignore = When.Update)]
    public DateTime CreatedAt { get; set; } = DateTime.UtcNow;

    // IsNew determines whether calling Save() generates an INSERT or UPDATE query
    public override bool IsNew(Connection conn = null)
    {
        return Id == 0;
    }
}

Creating, Updating, and Deleting (Active Record)

Using the BaseEntity allows you to call .Save() directly on your objects. Bowtie executes either an INSERT or UPDATE depending on your IsNew() logic.

var myOrder = new Order { UserId = 1, TotalAmount = 99.99m };
myOrder.Save(); // Evaluates IsNew() which returns true (Id == 0), executing INSERT

myOrder.TotalAmount = 89.99m;
myOrder.Save(); // Id != 0, executing UPDATE

Querying Data (Raw SQL)

If you prefer writing raw SQL, Bowtie provides a simple API for that as well. You can execute any SQL query and map the results back to your entities or DTOs.

var users = DB.Query<User>("SELECT * FROM users WHERE age > @age", new { age = 18 });

And for count and pagination, you can just call the corresponding methods with normal select queries without worrying about the underlying SQL generation for count and pagination, Bowtie will handle that for you.

int totalUsers = DB.Count("SELECT * FROM users WHERE age > @age", new { age = 18 });
var pagedUsers = DB.SelectPage<User>(2, 10, "SELECT * FROM users WHERE age > @age", new { age = 18 });

Querying Data (Lambda Expressions)

Bowtie's lambda query API allows you to safely construct complex SQL queries entirely in C# without string manipulation. Data can be seamlessly mapped back into DTOs or original Models.

Simple Queries

If you only need to return the entity type being queried, you can omit the generic parameter from the initial DB call.

var users = DB
  .From<User>()
  .Where(u => u.Age > 18)
  .OrderBy(u => u.Id)
  .Select();

Mapping Queries (DTOs)

If you are querying a table but wish to map the results into a different type (like a DTO), specify the output type in DB.Query<T>().

var users1 = DB.Query<UserDTO>()
  .From<User>()
  .Where<User>(u => u.Age > 18)
  .OrderBy<User>(u => u.Age)
  .Select();

Advanced Joins & Selectors

You can perform robust inner or left joins across multiple tables smoothly. You can define a selector function to tell Bowtie exactly how to hydrate your output objects.

var summaries = DB.Query<ItemSold>()
  .From<User>()
  .Join<Order, User>((o, u) => u.Id == o.UserId && o.Status == OrderStatus.Paid)
  .LeftJoin<Order, OrderItem>((o, oi) => o.Id == oi.OrderId)
  .Where<Order, User>((o, u) => u.Age > 10 || o.Id > 0)
  .OrderBy<Order>(o => o.Id)
  .OrderByDescending<User>(u => u.Id)
  .Select<Order, OrderItem, User>((o, oi, u) => GetSummary(oi, o, u));

// Mapper example for the query above
public ItemSold GetSummary(OrderItem oi, Order o, User u)
{
    return new ItemSold
    {
        OrderId = oi.OrderId,
        Status = o.Status,
        SubTotal = oi.Quantity * oi.Price,
        ItemName = oi.Goods,
        Quantity = oi.Quantity,
        SoldTo = u.Name
    };
}

Updating Records

Update records using conditions via the lambda builder:

db.From<User>()
    .Join<Order, User>((o, u) => o.UserId == u.Id && o.Status == OrderStatus.Paid)
    .Set(u => u.Name == "test")
    .Where(u => u.Age > 18)
    .Update();

Deleting Records

Delete records using conditions via the lambda builder:

db.From<User>()
    .Join<User, Order>((u, o)=>u.Id == o.UserId)
    .Where<Order, User>((o, u) => u.Age > 60 && o.Status == OrderStatus.Paid)
    .Delete();
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  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 was computed.  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 was computed.  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. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos 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
0.0.3 105 5/1/2026
0.0.2 104 4/30/2026
0.0.1 119 4/24/2026