CloudflareD1.NET.CodeFirst 1.0.3

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

CloudflareD1.NET.CodeFirst

Code-First ORM for CloudflareD1.NET. Define your database schema using C# classes and attributes, similar to Entity Framework Core.

NuGet NuGet Downloads

Features

  • Entity Attributes: Define tables, columns, keys, and relationships using attributes
  • DbContext Pattern: Familiar API for developers coming from Entity Framework
  • Type-Safe Queries: LINQ support through integration with CloudflareD1.NET.Linq
  • Automatic Migration Generation: Generate migrations from your model classes with dotnet d1 migrations add --code-first (snapshot-based, no DB required at generation time)
  • Fluent API: Configure entities using the fluent configuration API
  • Snapshot-Based Diffs: Compare your model with the last saved migration snapshot to detect changes

Installation

dotnet add package CloudflareD1.NET.CodeFirst

Quick Start

Define Your Entities

using CloudflareD1.NET.CodeFirst.Attributes;

[Table("users")]
public class User
{
    [Key]
    [Column("id")]
    public int Id { get; set; }

    [Required]
    [Column("username")]
    public string Username { get; set; } = string.Empty;

    [Column("email")]
    public string? Email { get; set; }

    [Column("created_at")]
    public DateTime CreatedAt { get; set; }

    // Navigation property
    public List<Order> Orders { get; set; } = new();
}

[Table("orders")]
public class Order
{
    [Key]
    [Column("id")]
    public int Id { get; set; }

    [Required]
    [Column("order_number")]
    public string OrderNumber { get; set; } = string.Empty;

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

    // Navigation property
    public User User { get; set; } = null!;
}

Create a DbContext

using CloudflareD1.NET;
using CloudflareD1.NET.CodeFirst;

public class MyDbContext : D1Context
{
    public MyDbContext(D1Client client) : base(client)
    {
    }

    // Entity sets
    public D1Set<User> Users { get; set; } = null!;
    public D1Set<Order> Orders { get; set; } = null!;

    // Recommended: Configure relationships with Fluent API
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<User>()
            .ToTable("users")
            .HasKey(u => u.Id);

        modelBuilder.Entity<Order>()
            .ToTable("orders")
            .HasKey(o => o.Id)
            .HasForeignKey(o => o.UserId);
    }
}

Use the DbContext

// Initialize D1Client
var options = new D1Options
{
    AccountId = "your-account-id",
    DatabaseId = "your-database-id",
    ApiToken = "your-api-token"
};
var client = new D1Client(options);

// Create context
var context = new MyDbContext(client);

// Query entities using LINQ
var users = await context.Users
    .AsQueryable()
    .Where("username LIKE ?", "john%")
    .ToListAsync();

// Find by primary key
var user = await context.Users.FindAsync(1);

// Get all orders
var allOrders = await context.Orders.ToListAsync();

Change tracking and SaveChanges

You can add, update, and remove entities and persist changes with SaveChangesAsync:

// Insert
var user = new User { Username = "john_doe", Email = "john@example.com", CreatedAt = DateTime.UtcNow };
context.Users.Add(user);
await context.SaveChangesAsync();
// user.Id is populated if it's an auto-increment key

// Update - only changed properties
user.Email = "new@example.com";  // Only Email changed
context.Users.Update(user);
await context.SaveChangesAsync();  // Generates: UPDATE users SET email = ? WHERE id = ?

// Update multiple properties
user.Email = "another@example.com";
user.Username = "jane_doe";
context.Users.Update(user);
await context.SaveChangesAsync();  // Generates: UPDATE users SET email = ?, username = ? WHERE id = ?

// No changes - no UPDATE generated
context.Users.Update(user);  // No properties modified
await context.SaveChangesAsync();  // 0 rows affected, no SQL executed

// Delete
context.Users.Remove(user);
await context.SaveChangesAsync();

Per-Property Change Detection: Update intelligently detects which properties have changed since the entity was last saved (via snapshot comparison). Only changed columns are included in the UPDATE statement, improving performance and reducing unnecessary writes.

Foreign Key-Aware Operation Ordering

SaveChangesAsync automatically orders INSERT and DELETE operations based on foreign key dependencies to prevent constraint violations:

// Example: Adding related entities in any order
var customer = new Customer { Name = "Acme Corp", Email = "contact@acme.com" };
var order = new Order { OrderNumber = "ORD-001", CustomerId = customer.Id, Total = 99.99m };

// Add in any order - SaveChanges will insert Customer first
context.Orders.Add(order);
context.Customers.Add(customer);  // Added second, but will be inserted first!
await context.SaveChangesAsync();  // Customer → Order (correct FK order)

// Deleting also respects FK constraints
context.Customers.Remove(customer);  // Added first
context.Orders.Remove(order);        // Added second
await context.SaveChangesAsync();    // Order → Customer (deletes child first)

How it works:

  • Inserts: Parent entities (referenced by FKs) are inserted before children
  • Deletes: Child entities (with FKs) are deleted before parents
  • Updates: No reordering (FK values should not change during updates)
  • Circular dependencies: If detected, an InvalidOperationException is thrown

Notes:

  • Primary keys are required for updates and deletes.
  • For auto-increment keys, if you don't set the key before insert, it will be populated from the database.
  • Per-property change detection: Update only modifies columns that have changed since the entity was last saved. If no properties change, no UPDATE statement is generated.
  • Insert/Update/Delete are executed sequentially when you call SaveChangesAsync (to satisfy the Cloudflare D1 API semantics).

Attributes

Table Attributes

  • [Table("name")]: Specifies the database table name for an entity
  • [NotMapped]: Excludes a property from database mapping

Column Attributes

  • [Column("name", TypeName="TEXT")]: Specifies column name and SQL type
  • [Key]: Marks a property as the primary key
  • [Required]: Makes a column NOT NULL
  • [ForeignKey("PropertyName")]: Defines a foreign key relationship

Entity Conventions

If you don't use attributes, the framework follows these conventions:

  • Table Names: Pluralized class name in snake_case (Userusers)
  • Column Names: Property name in snake_case (UserIduser_id)
  • Primary Keys: Properties named Id or {ClassName}Id become primary keys
  • Types: C# types map to SQLite types automatically:
    • int, long, short, byte, boolINTEGER
    • double, float, decimalREAL
    • string, DateTime, GuidTEXT
    • byte[]BLOB
  • Navigation properties & collections: Reference navigations (e.g., Order.Customer) and collections (e.g., User.Orders) are ignored by default and are not mapped to columns. You can also explicitly exclude any property with [NotMapped].

Fluent API

Configure entities programmatically in OnModelCreating:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<User>()
        .ToTable("users")
        .Property(u => u.Username)
        .IsRequired()
        .HasColumnName("user_name");
}

Generating Migrations (Code-First)

Build your project, then generate a migration from your models:

dotnet d1 migrations add InitialCreate --code-first \
    --context MyNamespace.MyDbContext \
    --assembly bin/Release/net8.0/MyApp.dll

This compares your model against the JSON snapshot at Migrations/.migrations-snapshot.json and generates only the delta.

To apply migrations, use the CLI:

dotnet d1 migrations apply

Roadmap

  • ✅ Entity attributes (Table, Column, Key, Required, NotMapped)
  • ✅ D1Context base class
  • ✅ D1Set entity collections
  • ✅ ModelBuilder and metadata system
  • ✅ Fluent configuration API
  • ✅ Code-first migration generation (snapshot-based)
  • ✅ Foreign keys via Fluent API
  • ✅ Basic change tracking (Add/Update/Remove) and SaveChanges
  • ✅ Foreign key-aware operation ordering (automatic INSERT/DELETE ordering)
  • ⏳ Index configuration helpers
  • ⏳ Data annotations validation

License

MIT License - see LICENSE file for details

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 netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.1 is compatible. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen 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
1.1.0 220 10/28/2025
1.0.3 201 10/28/2025
1.0.1 199 10/28/2025
1.0.0 202 10/28/2025

v1.0.3: Added per-property change detection in SaveChangesAsync. UPDATE operations now only modify columns that changed, improving performance and reducing unnecessary writes.