How-to guide

Stored procedures

Call stored procedures through a typed C# class, including output values and multiple result sets.

ORM Current 2 min read Updated

Prerequisites

  • A connected context and a stored procedure in the database

Tip

Included in the free, source-available edition. See Editions and features.

Stored procedures are supported on every backend. Instead of positional parameters, you call them through a typed C# contract: input, output and return values map to a plain class, and results are typed, including multiple result sets.

Declare the procedure#

Annotate a class with [StoredProcInfo] and the procedure's name, and describe each parameter with [ParamInfo]. Output and return values use ParamDirection. A result set is described by a row class whose properties carry [ColumnInfo]:

csharp
using System.Data;
using Kiwify.Kiwi.ORM.Core.DbObjects;

[DbContextObject("Sales")]      // registered as a stored procedure because of [StoredProcInfo]
[StoredProcInfo("get_customer_orders")]
public class GetCustomerOrders
{
    [ParamInfo("customer_id")]
    public int CustomerId { get; set; }                                   // input

    [ParamInfo("total_orders", ParamDirection = ParameterDirection.Output)]
    public int? TotalOrders { get; set; }                                 // output

    // Result set 1: read it with ExecuteRead<OrderRow>()
    public class OrderRow
    {
        [ColumnInfo("orderid")]  public int OrderId { get; set; }
        [ColumnInfo("total")]    public decimal Total { get; set; }
    }
}

You rarely write these by hand: kiwi orm generate produces them from the database, including parameter directions and result-set row classes. Non-default schemas become part of the call name (schema.procedure). See Generate entities and migrations.

Call it#

csharp
var cmd = db.StoredProc<GetCustomerOrders>(p => p.CustomerId = 42);

List<GetCustomerOrders.OrderRow> orders = cmd.ExecuteRead<GetCustomerOrders.OrderRow>();
int? total = cmd.ProcObject!.TotalOrders;     // output value mapped back onto the object

Choose the call that matches what the procedure returns:

Method Returns
Execute() Nothing (runs the procedure; outputs are mapped to the class)
ExecuteScalar<T>() A single value
ExecuteRead<T>() A list from one result set
ExecuteReadMultiResults<T1, T2>() and up to five types A tuple of lists, one per result set
csharp
var (orders, items) = db.StoredProc<GetCustomerOrders>(p => p.CustomerId = 42)
                        .ExecuteReadMultiResults<OrderRow, ItemRow>();   // one list per result set, in order

Async variants are available for the same operations.

Passing an existing object#

StoredProc<T>(T storedProc) accepts an already populated instance, which is convenient when the same parameter object is reused.

Notes#

  • Stored procedure calls take part in the current transaction and, in the Enterprise edition, in the audit trail. See Transactions.
  • Procedures are described in your context with [DbContextObject], like entities, so they are registered with AddDbObjects(...).

Expected result

  • A procedure is called with typed inputs and returns typed outputs and result sets