EDK4Net2 4.1.11

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

EDK4Net2

EDK4Net2 is a framework designed to simplify the development of applications and libraries in C# / VB.NET, targeting .NET 10.0.

NuGet License

💡 Tip: Instead of writing classes by hand, use the EDK4Net Code Generator to generate them automatically from your database schema.


Features

  • ORM – Active Record pattern via BusinessObject<T> with automatic change tracking (IsNew, IsDirty, IsDeleted)
  • Query Language – LINQ-style fluent queries and a Classic SQL-like engine, both always available
  • Validation – Imperative CheckRules() / CheckDeleteRules() override with BrokenRuleException
  • Transactions (CNWrapper) – Atomic multi-object, multi-database transactions managed automatically by Save()
  • Auditing – Per-property change recording with old/new values, user ID, and timestamp
  • Object Locking – Pessimistic record locking (LockExclusive / RemoveLock) for multi-user environments
  • Logging – File-based logger with email delivery, buffering and Microsoft.Extensions.Logging integration
  • Code Generator – Windows WPF tool that generates all BusinessObject<T> classes from your DB schema

Supported Databases

Database Provider
SQL Server Microsoft.Data.SqlClient
MySQL MySqlConnector

Installation

dotnet add package EDK4Net2

Or via Package Manager Console in Visual Studio:

Install-Package EDK4Net2

Quick Start

1 – Configuration

Set the connection string and provider in Program.cs:

// Program.cs (.NET 10)
var builder = WebApplication.CreateBuilder(args);

EDK4NetConfig.DefaultCnString = builder.Configuration
    .GetConnectionString("DefaultConnection");
EDK4NetConfig.DefaultDataProvider = DataProviderType.SqlServer;
EDK4NetConfig.EnableAudit = true;
// appsettings.json
{
  "ConnectionStrings": {
    "DefaultConnection": "Server=.;Database=MyDb;Trusted_Connection=True;"
  }
}

2 – Define a Business Object

Every class inherits from BusinessObject<T>. Properties are automatically tracked.

using EDK4Net.Core;
using EDK4Net.Data;

public class Customer : BusinessObject<Customer>
{
    [PrimaryKey]
    public int    CustomerId { get; set; }
    public string FirstName  { get; set; } = "";
    public string LastName   { get; set; } = "";
    public string Email      { get; set; } = "";
    public bool   IsActive   { get; set; } = true;

    // Factory methods called by the DataPortal
    public static Customer GetObject(int id)
        => DataPortal.Fetch<Customer>(id);

    public static Customer NewObject()
        => DataPortal.Create<Customer>();
}

// Collection class
public class CustomerCollection : SortableCollectionBase<Customer>
{
    public static CustomerCollection GetCollection()
        => DataPortal.FetchCollection<CustomerCollection, Customer>();
}

3 – CRUD Operations

// --- CREATE ---
var customer = Customer.NewObject();
customer.FirstName = "Mario";
customer.LastName  = "Rossi";
customer.Email     = "mario.rossi@example.com";

if (customer.IsValid)
    customer.Save();  // automatic INSERT

Console.WriteLine($"Created with ID: {customer.CustomerId}");

// --- READ ---
var single = Customer.GetObject(1);
Console.WriteLine($"{single.FirstName} {single.LastName}");

var all = CustomerCollection.GetCollection();
Console.WriteLine($"Total customers: {all.Count}");

// --- UPDATE ---
single.Email = "new@example.com";
Console.WriteLine($"Modified: {single.IsDirty}");  // true
single.Save();  // automatic UPDATE

// --- DELETE ---
single.Delete();
single.Save();  // automatic DELETE

4 – Validation Rules

Override CheckRules() to define save conditions and CheckDeleteRules() for delete conditions. Save() calls CheckRules() automatically and throws BrokenRuleException on violations.

public class Customer : BusinessObject<Customer>
{
    // ... properties ...

    protected override void CheckRules()
    {
        if (string.IsNullOrEmpty(FirstName))
            BrokenRules.Add(new BrokenRule(this,
                "First name is required", FirstName));

        if (LastName?.Length > 50)
            BrokenRules.Add(new BrokenRule(this,
                "Last name cannot exceed 50 characters", LastName));
    }

    protected override void CheckDeleteRules()
    {
        if (Orders.Count > 0)
            BrokenRules.Add(new BrokenRule(this,
                "Cannot delete: associated orders exist", Orders.Count));
    }
}

// Usage
try
{
    customer.Save();
}
catch (BrokenRuleException ex)
{
    foreach (var rule in ex.BrokenRules)
        Console.WriteLine(rule.RuleName);
    // Output: "First name is required"
}

5 – Transactions (CNWrapper)

CNWrapper is the framework's internal connection and transaction coordinator — never instantiated directly. It is created and managed automatically by Save(). All objects involved are committed together or rolled back atomically. It also supports multi-database scenarios: objects on different databases are coordinated in the same atomic transaction.

public class Order : BusinessObject<Order>
{
    // PreSaveMethod — called before the DB write; prepare related objects
    protected override void PreSaveMethod(CNWrapper cnWrapper)
    {
        if (IsDeleted)
        {
            foreach (var item in Items)
            {
                item.Delete();
                CurrentCNWrapper.AddObject(item);
            }
        }
    }

    // PostSaveMethod — called after the parent write, before commit; save children
    protected override void PostSaveMethod(CNWrapper cnWrapper, SavingType saveMethod)
    {
        if (saveMethod == SavingType.Insert)
        {
            foreach (var note in newNotes)
            {
                note.OrderId = OrderId; // propagate the new parent ID
                note.Save(cnWrapper);   // same transaction as the parent
            }
            newCommissions.Save(cnWrapper);
        }

        Lines.Save(cnWrapper); // always save lines in the same transaction
    }

    // AfterSaveMethod — called after commit; CNWrapper is already closed
    protected override void AfterSaveMethod(bool saved, SavingType saveMethod)
    {
        if (saved)
            Cache.Invalidate(this);
    }

    // Add a related object to the current transaction from a business method
    public void Approve()
    {
        relatedAsset.TotalAvailable += Amount;
        CurrentCNWrapper.AddObject(relatedAsset);
        Save();
    }
}

6 – Query Language

Two query engines are always available.

// --- LINQ Query (recommended) ---

// Simple filter + ordering + pagination
var query = Order.Query()
    .Where(o => o.Status == "Active")
    .OrderByDescending(o => o.OrderDate)
    .Take(20)
    .ToSelectQuery();

var orders = new OrderCollection();
orders.GetBySelectQuery(query);

// OR conditions
var query2 = Order.Query()
    .Where(o => o.Status == "Pending")
    .WhereOr(o => o.Status == "Processing")
    .ToSelectQuery();

// Join between entities with filters on both
var query3 = Order.Query()
    .Join<OrderLine, int>(o => o.Id, l => l.OrderId)
    .Where(o => o.CustomerId == 42)
    .Where<OrderLine>(l => l.Quantity > 0)
    .Distinct()
    .ToSelectQuery();

// Pagination
var page = Order.Query()
    .Where(o => o.Status == "Active")
    .OrderBy(o => o.Id)
    .Skip(20).Take(10)
    .ToSelectQuery();

// ToSelectQuery() converts to the classic engine:
// use with GetBySelectQuery() to load collections,
// or with GetCustomQueryDV() to get a DataView.

// --- Classic QueryLanguage (always available) ---
// More complete, SQL-like syntax.
// Supports SelectQuery, CustomQuery, DeleteQuery,
// aggregate functions (TOP, COUNT, MAX, MIN, SUM, AVG)
// and direct access to Properties and Operators.

7 – EDK4Net Code Generator

The EDK4Net Code Generator is a Windows WPF (.NET 10) application that automatically generates all BusinessObject<T> classes and their collections directly from your database schema (SQL Server or MySQL), eliminating all boilerplate code. It will soon also be available as a Visual Studio add-on.

Typical workflow:

  1. Configure the DB connection (DB Options)
  2. Reload the schema (Reload)
  3. Customise class names, namespaces, and property types
  4. Generate the code (Generate Code) — .cs files are written to disk
  5. Add your business logic in the separate partial files
// File BASE (auto-generated — never edit this file)
[Serializable]
[ObjectMetaData(TableName = "Customers")]
public partial class Customer : BusinessObject<Customer>
{
    [PrimaryKey, AutoKey]
    public int    CustomerId { get; set; }
    public string FirstName  { get; set; } = "";
    public string LastName   { get; set; } = "";
    public string Email      { get; set; } = "";
    public bool   IsActive   { get; set; }

    protected override object GetIdValue() => CustomerId;
    protected override string GetDefaultCNString()
        => EDK4NetConfig.DefaultCnString;
    // ... FetchMethod, InsertMethod, UpdateMethod, DeleteMethod generated here
}

// File PARTIAL (your logic — never overwritten by the generator)
public partial class Customer
{
    protected override void CheckRules()
    {
        if (string.IsNullOrEmpty(FirstName))
            BrokenRules.Add(new BrokenRule(this,
                "First name is required", FirstName));
    }

    public static Customer GetCustomer(int id)
    {
        var e = CreateExampleObject();
        e.CustomerId = id;
        return GetByExample(e);
    }
}

Key features:

  • TreeView with table, class, or namespace view
  • Live search in the tables panel
  • Inline editing: class name, namespace, column type
  • Configurable [Obsolete] attribute per column
  • DB schema comparison (detects added/removed tables)
  • Import of legacy projects (.e4ncg.e4nproj)
  • Coming soon: Visual Studio add-on

⬇ Download the Code Generator


8 – Auditing

Enable auditing on a class by overriding AuditEnabled() and KindOfAudit(). Every property change is automatically recorded as a DbChange entry (old value, new value, user, timestamp).

public class Product : BusinessObject<Product>
{
    protected override bool AuditEnabled() => true;

    protected override AuditKind KindOfAudit()
        => AuditKind.All; // Insert + Update + Delete
}

// Read the change history
var product = Product.GetObject(1);
var history = product.AuditHistory;

foreach (var change in history)
{
    Console.WriteLine($"{change.ChangeDate}: {change.UserName}");
    foreach (var prop in change.Properties)
        Console.WriteLine($"  {prop.PropertyName}: {prop.OldValue} -> {prop.NewValue}");
}

9 – Object Locking

Pessimistic locking for multi-user environments. Locks are managed in the audit database.

var document = Document.GetObject(documentId);

if (document.IsLocked())
{
    Console.WriteLine("Document is being edited by another user!");
    return;
}

var lockReceipt = document.LockExclusive();

if (!string.IsNullOrEmpty(lockReceipt.Ticket))
{
    try
    {
        document.Title = "New Title";
        document.Save();
    }
    finally
    {
        document.RemoveLock(lockReceipt.Ticket);
    }
}

10 – Logging

using EDK4Net.Log;

var log = Logger.NewLogger("MyComponent");
log.SubPath   = "AppLogs\\";
log.Logformat = Logger.LogFormat.Frequent;
log.WriteLine("Operation completed.");
log.Save();

To integrate with Microsoft.Extensions.Logging:

// Program.cs
Logger.ConfigureLoggerFactory(loggerFactory);

Configuration Reference

Configuration is read automatically from appsettings.json (and environment-specific overrides such as appsettings.Development.json). JSON values take priority over App.config / Web.config <appSettings> entries, which are used as fallback.

Bool flags accept true, false, Test (enable only in Test environment), or !Test (enable outside Test environment).

Connection String

Configured via the standard .NET ConnectionStrings section — no EDK4Net-specific key required.

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=myserver;Database=mydb;Trusted_Connection=True;"
  }
}

The generated GetDefaultCNString() method reads ConnectionStrings:DefaultConnection by name; you can override it per class to point to a different connection string.


Framework Settings

Property JSON key (EDK4Net:…) AppSettings key Type Default Description
ApplicationName AppName EDK4Net-AppName string Application name used in log file names and entries
DataProviderType DataProviderType EDK4Net-DataProviderType Sql | MySql Database provider
Environment EnvironmentType EDK4Net-EnvironmentType Production | Test | Demo auto-detected Runtime environment; auto-detected from ProdServers/DemoServers if not set
ProdServers ProdServers EDK4Net-ProdServers string (CSV) Comma-separated server names treated as Production (evaluated only when EnvironmentType is not set)
DemoServers DemoServers EDK4Net-DemoServers string (CSV) Comma-separated server names treated as Demo (evaluated only when EnvironmentType is not set)
EnableAudit EnableAudit EDK4Net-EnableAudit bool false Enable automatic change auditing for all objects
DBLogEnabled DBLogging EDK4Net-DBLogging bool false Log generated SQL statements to file
DBLogType DBLogType EDK4Net-DBLogType All | Fetch | Delete All Scope of SQL logging when DBLogging is enabled
CNWrapperLogEnabled CNWrapperLog EDK4Net-CNWrapperLog bool false Log transaction coordinator (CNWrapper) operations
LogBrokenRules LogBrokenRules EDK4Net-LogBrokenRules bool false Log broken validation rules on each Save()
EntityCheckEnabled EntityCheck EDK4Net-EntityCheck bool false Log incorrect property-set operations on BusinessObject
CacheDuration CacheDuration EDK4Net-CacheDuration int (minutes) 20 In-memory object cache duration
LockDuration LockDuration EDK4Net-LockDuration int (seconds) 20 Pessimistic lock auto-expiry duration
LongQueryTimeout LongQueryTimeout EDK4Net-LongQueryTimeout int (seconds) 120 Command timeout for long-running queries
ReadOnly ReadOnly EDK4Net-ReadOnly bool false Simulate saves without writing to DB (also accepts Demo / !Demo)
DebugMode DebugMode EDK4Net-DebugMode bool false Verbose debug output; exceptions are logged and optionally emailed
LogPath logPath EDK4Net-logPath string app dir Base directory for log files
NetDomain NetDomain EDK4Net-NetDomain string Network domain for impersonation
NetUser NetUser EDK4Net-NetUser string Network username for impersonation
NetKey NetKey EDK4Net-NetKey string Network key / password for impersonation
SmtpHost SmtpHost EDK4Net-SmtpHost string SMTP server hostname for email delivery
SmtpPort SmtpPort EDK4Net-SmtpPort int 25 SMTP server port

Log Settings

Property JSON key (EDK4Net:Log:…) AppSettings key Type Default Description
LogFormat LogFormat EDK4Net.Log-LogFormat Blank | Frequent | … Blank Default log entry format
DateFormat DateFormat EDK4Net.Log-DateFormat string "G" Date/time format string used in log entries
DefaulFileName DefaultFileName EDK4Net.Log-DefaultFileName string EDK4Net.LogFile Default log file name (without extension)
DefaulExtension DefaultExtension EDK4Net.Log-DefaultExtension string .log Default log file extension
FileNameWithAppName FileNameWithAppName EDK4Net.Log-FileNameWithAppName bool false Prefix log file name with ApplicationName
FileNameWithDate FileNameWithDate EDK4Net.Log-FileNameWithDate bool false Append current date to log file name
FileNameWithTime FileNameWithTime EDK4Net.Log-FileNameWithTime bool false Append current time to log file name
PostSend PostSend EDK4Net.Log-PostSend bool false Batch email sending at a fixed interval
PostSendInterval PostSendInterval EDK4Net.Log-PostSendInterval int (ms) 60000 Interval between batched email deliveries
GroupBy GroupBy EDK4Net.Log-GroupBy Message | Subject Message Email grouping strategy when PostSend is enabled
Sender EmailSender EDK4Net.Log-EmailSender string Default sender email address
Receivers EmailReceivers EDK4Net.Log-EmailReceivers string (;-sep) Default recipient email addresses (semicolon-separated)
EmailDebugLog EmailDebugLog EDK4Net.Log-EmailDebugLog bool false Email debug log entries (requires DebugMode = true)
LogExceptionStack LogExceptionStack EDK4Net.Log-LogExceptionStack bool false Include stack trace when logging exceptions
LogStartEndTime LogStartEndTime EDK4Net.Log-LogStartEndTime bool false Write start/end timestamp lines in each log session
ResolveHostName ResolveHostName EDK4Net.Log-ResolveHostName None | All | Remote | Local None DNS resolution of IP addresses in log entries

Requirements

  • .NET 10.0
  • SQL Server 2012+ or MySQL 5.7+


License

Copyright © Edika 2003–2025. All rights reserved.

Product Compatible and additional computed target framework versions.
.NET 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
4.1.11 58 6/11/2026
4.1.0 62 4/18/2026

Net 10 public release