PostgreSQL

Get started

Everything the samples need on your machine, once: the .NET SDK, a project, the packages, the database and the connection string.

  1. Install the .NET SDK

    .NET SDK 10.0.100 or later 10.0.x. The samples target net10.0. Check what you have:

    Terminal
    dotnet --version
  2. Create a project

    Terminal
    dotnet new console -n ArxSample -f net10.0
    cd ArxSample
  3. Add the packages

    • ARX.SDK.PostgreSql1.0.0-rc.1: The PostgreSQL provider. It brings ARX.SDK and Npgsql 8.0.3 with it.
    • ARX.SDK1.0.0-rc.1: The core: entities, queries, the schema catalog, errors. Comes with the provider.
    • Npgsql8.0.3: The PostgreSQL driver. Comes with the provider.
    Terminal
    dotnet add package ARX.SDK.PostgreSql --version 1.0.0-rc.1

    Some advanced samples need one more package; their pages give the exact command.

  4. Create the database and the sample schema

    PostgreSQL 16 (tested with 16.15), through Npgsql 8.0.3 or later (the lowest version, which the provider is built on; Versions and compatibility lists the versions it is tested on). With Docker, start one and create the database (choose your own password):

    Terminal
    docker run --name arx-samples-postgres -e POSTGRES_PASSWORD=your-password -p 5432:5432 -d postgres:16-alpine
    docker exec arx-samples-postgres createdb -U postgres arx_samples

    Then create the sample tables: download sample-schema.sql, or copy it below, and run it. Run it again whenever you want to start over: it drops and recreates the sample tables, empty.

    Terminal

    macOS and Linux (bash, zsh)

    docker exec -i arx-samples-postgres psql -U postgres -d arx_samples -v ON_ERROR_STOP=1 < sample-schema.sql
    PowerShell

    Windows PowerShell

    Get-Content sample-schema.sql -Raw | docker exec -i arx-samples-postgres psql -U postgres -d arx_samples -v ON_ERROR_STOP=1
    sample-schema.sql

    The sample schema the samples run against.

    -- Schema for the ARX.SDK PostgreSQL documentation samples: a small CRM and product catalogue, and a table of every
    -- data type. Run it in a database of its own (arx_samples). Re-runnable: it drops, by name, only the tables, views,
    -- types and routines it creates below, and the reporting schema once nothing else is left in it, so running it again
    -- starts over and nothing else in the database is touched. Run it with psql or any SQL tool, for example:
    --   psql "host=localhost dbname=arx_samples user=postgres" -v ON_ERROR_STOP=1 -f sample-schema.sql
    --
    -- ARX conventions used here: the table name is the entity name, and a lookup column stores the referenced row's id.
    -- Every table's id column is declared in SampleCatalog.cs: most are a generated bigint "id"; country and device show a
    -- natural id and a generated uuid; price_list and activity_log have no id column (NoIdColumn). Selection set columns
    -- hold the option's code itself.
    
    DROP PROCEDURE IF EXISTS sp_recalculate_order_totals();
    DROP FUNCTION IF EXISTS fn_accounts_with_status(text), fn_account_totals(), fn_order_count(bigint), fn_account_rows(), fn_log_action(bigint, text);
    DROP MATERIALIZED VIEW IF EXISTS mv_account_sales_summary;
    DROP VIEW IF EXISTS reporting.vw_product_catalogue;
    -- The reporting schema is the script's own: it goes only when the script's view was the last thing in it.
    DO $$
    BEGIN
        IF EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'reporting')
            AND NOT EXISTS (SELECT 1 FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname = 'reporting')
            AND NOT EXISTS (SELECT 1 FROM pg_proc p JOIN pg_namespace n ON n.oid = p.pronamespace WHERE n.nspname = 'reporting') THEN
            DROP SCHEMA reporting;
        END IF;
    END $$;
    DROP TABLE IF EXISTS web_visit;
    DROP VIEW IF EXISTS vw_key_accounts;
    DROP VIEW IF EXISTS vw_contact_directory;
    DROP FUNCTION IF EXISTS fn_contacts_of_account(bigint);
    DROP VIEW IF EXISTS vw_account_overview;
    DROP TABLE IF EXISTS legacy_customer;
    DROP TABLE IF EXISTS activity_log;
    DROP TABLE IF EXISTS price_list;
    DROP TABLE IF EXISTS device;
    DROP TABLE IF EXISTS country;
    DROP TABLE IF EXISTS order_line;
    DROP TABLE IF EXISTS sales_order;
    DROP TABLE IF EXISTS data_type_showcase;
    DROP TABLE IF EXISTS product;
    DROP TABLE IF EXISTS product_category;
    ALTER TABLE IF EXISTS account DROP CONSTRAINT IF EXISTS account_primary_contact_fkey;
    DROP TABLE IF EXISTS contact;
    DROP TABLE IF EXISTS account;
    DROP TYPE IF EXISTS showcase_mood;
    
    -- The routines this script creates, whatever their arguments (so a changed signature needs no new DROP line), and no
    -- other: only these names, only in public.
    DO $$
    DECLARE routine record;
    BEGIN
        FOR routine IN
            SELECT p.oid::regprocedure AS signature, p.prokind FROM pg_proc p JOIN pg_namespace n ON n.oid = p.pronamespace
            WHERE n.nspname = 'public' AND p.proname IN (
                'fn_account_rows', 'fn_account_totals', 'fn_accounts_with_status', 'fn_contact_count', 'fn_contacts_named', 'fn_contacts_of_account',
                'fn_log_action', 'fn_order_count', 'fn_products_like', 'fn_touch_account', 'sp_account_report', 'sp_account_totals',
                'sp_import_progress', 'sp_order_with_lines', 'sp_place_order', 'sp_products', 'sp_products_page', 'sp_recalculate_order_totals',
                'sp_recalculate_totals', 'sp_rename_account', 'sp_reserve_credit', 'sp_slow')
        LOOP
            EXECUTE format('DROP %s IF EXISTS %s', CASE routine.prokind WHEN 'p' THEN 'PROCEDURE' ELSE 'FUNCTION' END, routine.signature);
        END LOOP;
    END $$;
    
    -- Customers. industry is an Integer selection set built from a .NET enum; status a String set the CHECK also guards;
    -- segments a multiple-choice String set stored as a text[] array.
    CREATE TABLE account (
        id                bigserial     PRIMARY KEY,
        guid              uuid          NOT NULL DEFAULT gen_random_uuid(),
        name              text          NOT NULL,
        account_number    varchar(20)   UNIQUE,
        industry          smallint,
        status            text          NOT NULL DEFAULT 'ACTIVE' CHECK (status IN ('ACTIVE', 'ON_HOLD', 'CLOSED')),
        segments          text[],
        website           varchar(200),
        annual_revenue    numeric(18, 2),
        employee_count    int           CHECK (employee_count >= 0),
        credit_limit      numeric(18, 2),
        is_key_account    boolean       NOT NULL DEFAULT false,
        founded_on        date,
        parent_account_id bigint        REFERENCES account (id),
        primary_contact_id bigint,       -- references contact (added after contact below; the preload samples follow it)
        created_on        timestamptz   NOT NULL DEFAULT now(),
        modified_on       timestamptz
    );
    
    -- People at a customer. account_id is a lookup (ArxEntityReference); email is unique. preferred_channel, lead_source
    -- and rating are selection sets (lead_source and rating come from the catalog's JSON).
    CREATE TABLE contact (
        id                bigserial     PRIMARY KEY,
        guid              uuid          NOT NULL DEFAULT gen_random_uuid(),
        account_id        bigint        REFERENCES account (id),
        first_name        text          NOT NULL,
        last_name         text          NOT NULL,
        job_title         varchar(100),
        email             varchar(254)  UNIQUE,
        phone             varchar(30),
        preferred_channel char(1),
        lead_source       text,
        rating            smallint,
        birthday          date,
        do_not_email      boolean       NOT NULL DEFAULT false,
        consent_given_at  timestamptz,
        notes             text,
        created_on        timestamptz   NOT NULL DEFAULT now()
    );
    
    -- account.primary_contact_id: a contact of the account, the reference a preload follows to add contact.
    ALTER TABLE account ADD CONSTRAINT account_primary_contact_fkey FOREIGN KEY (primary_contact_id) REFERENCES contact (id);
    
    CREATE TABLE product_category (
        id    bigserial PRIMARY KEY,
        name  text      NOT NULL UNIQUE
    );
    
    -- The catalogue. attributes is free-form JSON (ArxJsonB); image is binary (byte[] or ArxBytea).
    CREATE TABLE product (
        id              bigserial      PRIMARY KEY,
        guid            uuid           NOT NULL DEFAULT gen_random_uuid(),
        sku             varchar(32)    NOT NULL UNIQUE,
        name            text           NOT NULL,
        description     text,
        category_id     bigint         REFERENCES product_category (id),
        unit_price      numeric(12, 2) NOT NULL CHECK (unit_price >= 0),
        cost            numeric(12, 4),
        weight_kg       double precision,
        stock_quantity  int            NOT NULL DEFAULT 0,
        is_active       boolean        NOT NULL DEFAULT true,
        tags            text[],
        attributes      jsonb,
        image           bytea,
        launched_on     date,
        created_on      timestamptz    NOT NULL DEFAULT now()
    );
    
    -- An order and its lines, for the "one process, one transaction" samples.
    CREATE TABLE sales_order (
        id           bigserial      PRIMARY KEY,
        order_number varchar(20)    NOT NULL UNIQUE,
        account_id   bigint         NOT NULL REFERENCES account (id),
        ordered_on   timestamptz    NOT NULL,
        total        numeric(14, 2) NOT NULL DEFAULT 0
    );
    
    CREATE TABLE order_line (
        id          bigserial      PRIMARY KEY,
        order_id    bigint         NOT NULL REFERENCES sales_order (id),
        product_id  bigint         NOT NULL REFERENCES product (id),
        quantity    int            NOT NULL CHECK (quantity > 0),
        unit_price  numeric(12, 2) NOT NULL
    );
    
    -- One column for every kind of value the PostgreSQL provider writes (README, "Type mapping").
    CREATE TYPE showcase_mood AS ENUM ('happy', 'neutral', 'sad');
    
    CREATE TABLE data_type_showcase (
        id                 bigserial        PRIMARY KEY,
        text_value         text,
        varchar_value      varchar(50),
        char_value         char(3),
        smallint_value     smallint,
        int_value          int,
        bigint_value       bigint,
        numeric_value      numeric(30, 6),
        real_value         real,
        double_value       double precision,
        bool_value         boolean,
        uuid_value         uuid,
        timestamptz_value  timestamptz,
        timestamp_value    timestamp,
        date_value         date,
        time_value         time,
        interval_value     interval,
        bytea_value        bytea,
        jsonb_value        jsonb,
        text_array         text[],
        int_array          int[],
        numeric_array      numeric[],
        uuid_array         uuid[],
        date_array         date[],
        timestamptz_array  timestamptz[],
        mood               showcase_mood,
        account_id         bigint           REFERENCES account (id),
        industry           smallint
    );
    
    -- Ids that aren't a generated bigint: a natural text id and a generated uuid. Then two tables with no id column: a
    -- price identified by region and SKU together, and a log whose rows may repeat. All declared in SampleCatalog.cs:
    -- ARX reads ids and keys from the database only when asked to preload them (the DynamicTableSchemaServiceSetup samples).
    CREATE TABLE country (
        code  char(2)  PRIMARY KEY,
        name  text     NOT NULL
    );
    
    CREATE TABLE device (
        id             uuid    PRIMARY KEY DEFAULT gen_random_uuid(),
        serial_number  text    NOT NULL UNIQUE,
        account_id     bigint  REFERENCES account (id)
    );
    
    CREATE TABLE price_list (
        region      char(2)        NOT NULL REFERENCES country (code),
        sku         varchar(32)    NOT NULL,
        unit_price  numeric(12, 2) NOT NULL CHECK (unit_price >= 0),
        valid_from  date           NOT NULL,
        PRIMARY KEY (region, sku)
    );
    
    -- No primary key at all: the same action can be logged twice.
    CREATE TABLE activity_log (
        account_id  bigint       REFERENCES account (id),
        action      text         NOT NULL,
        logged_at   timestamptz  NOT NULL DEFAULT now()
    );
    
    -- A table made outside ARX, its columns named outside ARX's name rule: used through aliases (Create Advanced10).
    CREATE TABLE legacy_customer (
        "Customer Code"  text           PRIMARY KEY,
        "first-name"     text           NOT NULL,
        "last-name"      text,
        "order.total"    numeric(12, 2)
    );
    
    -- Naming: views vw_, materialized views mv_, functions fn_, procedures sp_; tables have no prefix. The name says what
    -- an object is where only the name is visible (a query, a log, an error message), and ARX reads them all by name.
    
    -- Retrieve: a view an application reads like a table, and a function that returns a table.
    CREATE VIEW vw_account_overview AS
    SELECT a.id, a.name, a.account_number, a.status, count(c.id) AS contacts
    FROM account a
    LEFT JOIN contact c ON c.account_id = a.id
    GROUP BY a.id;
    
    CREATE FUNCTION fn_contacts_of_account(p_account_id bigint)
    RETURNS TABLE (id bigint, first_name text, last_name text, email varchar)
    LANGUAGE sql STABLE
    AS $$ SELECT id, first_name, last_name, email FROM contact WHERE account_id = p_account_id $$;
    
    -- Retrieve from every kind of row source (Advanced8_ReadOtherRowSources, Advanced9_ReadFunctionsInDepth).
    
    -- A materialized view: a stored snapshot, filled by REFRESH (created empty here, as after a deployment).
    CREATE MATERIALIZED VIEW mv_account_sales_summary AS
    SELECT a.id AS account_id, a.name, a.account_number, count(o.id) AS orders, coalesce(sum(o.total), 0) AS revenue
    FROM account a
    LEFT JOIN sales_order o ON o.account_id = a.id
    GROUP BY a.id, a.name, a.account_number
    WITH NO DATA;
    
    -- A view in another schema: read as "reporting.vw_product_catalogue".
    CREATE SCHEMA IF NOT EXISTS reporting;
    CREATE VIEW reporting.vw_product_catalogue AS
    SELECT p.id, p.sku, p.name, p.unit_price, c.name AS category
    FROM product p
    LEFT JOIN product_category c ON c.id = p.category_id
    WHERE p.is_active;
    
    -- A partitioned table, one partition per half year (declared NoIdColumn in SampleCatalog.cs).
    CREATE TABLE web_visit (
        account_id  bigint  REFERENCES account (id),
        page        text    NOT NULL,
        visited_on  date    NOT NULL
    ) PARTITION BY RANGE (visited_on);
    CREATE TABLE web_visit_2026_h1 PARTITION OF web_visit FOR VALUES FROM ('2026-01-01') TO ('2026-07-01');
    CREATE TABLE web_visit_2026_h2 PARTITION OF web_visit FOR VALUES FROM ('2026-07-01') TO ('2027-01-01');
    
    -- Function shapes: rows of a table's type, one row of OUT values, one value, rows of an undeclared record type.
    CREATE FUNCTION fn_accounts_with_status(p_status text) RETURNS SETOF account
    LANGUAGE sql STABLE
    AS $$ SELECT * FROM account WHERE status = p_status $$;
    
    CREATE FUNCTION fn_account_totals(OUT accounts bigint, OUT contacts bigint)
    LANGUAGE sql STABLE
    AS $$ SELECT (SELECT count(*) FROM account), (SELECT count(*) FROM contact) $$;
    
    CREATE FUNCTION fn_order_count(p_account_id bigint) RETURNS bigint
    LANGUAGE sql STABLE
    AS $$ SELECT count(*) FROM sales_order WHERE account_id = p_account_id $$;
    
    CREATE FUNCTION fn_account_rows() RETURNS SETOF record
    LANGUAGE sql STABLE
    AS $$ SELECT id, name, account_number FROM account $$;
    
    -- A function that writes: refused by a read unless the source says AllowWrites().
    CREATE FUNCTION fn_log_action(p_account_id bigint, p_action text) RETURNS SETOF activity_log
    LANGUAGE sql VOLATILE
    AS $$ INSERT INTO activity_log (account_id, action) VALUES (p_account_id, p_action) RETURNING * $$;
    
    -- A procedure: not a row source (Execute's, not Retrieve's).
    CREATE PROCEDURE sp_recalculate_order_totals()
    LANGUAGE sql
    AS $$ UPDATE sales_order o SET total = coalesce((SELECT sum(l.quantity * l.unit_price) FROM order_line l WHERE l.order_id = o.id), 0) $$;
    
    -- Views in depth (Advanced10_ViewsInDepth): a directory of contacts without their private columns (email, phone,
    -- birthday, notes), and a view built on another view.
    CREATE VIEW vw_contact_directory AS
    SELECT id, first_name, last_name, job_title, account_id
    FROM contact;
    
    CREATE VIEW vw_key_accounts AS
    SELECT id, name, account_number, contacts
    FROM vw_account_overview
    WHERE contacts >= 2;
    
    -- Execute: procedures (sp_) and functions (fn_) the samples call.
    
    -- Rename an account (no result); its totals for a year (IN, INOUT and OUT values).
    CREATE PROCEDURE sp_rename_account(p_account_id bigint, p_name text, p_reason text DEFAULT 'not given')
        LANGUAGE sql AS $$ UPDATE account SET name = p_name, modified_on = now() WHERE id = p_account_id $$;
    CREATE PROCEDURE sp_account_totals(p_account_id bigint, INOUT p_year int, OUT p_orders bigint, OUT p_revenue numeric)
        LANGUAGE plpgsql AS $$
        BEGIN
            p_year := coalesce(p_year, extract(year FROM now())::int);
            SELECT count(*), coalesce(sum(total), 0) INTO p_orders, p_revenue
            FROM sales_order WHERE account_id = p_account_id AND extract(year FROM ordered_on) = p_year;
        END $$;
    
    -- A value; a function run for its effect (void); rows.
    CREATE FUNCTION fn_contact_count(p_account_id bigint) RETURNS bigint
        LANGUAGE sql STABLE AS $$ SELECT count(*) FROM contact WHERE account_id = p_account_id $$;
    CREATE FUNCTION fn_touch_account(p_account_id bigint) RETURNS void
        LANGUAGE sql AS $$ UPDATE account SET modified_on = now() WHERE id = p_account_id $$;
    CREATE FUNCTION fn_contacts_named(p_last_name text) RETURNS TABLE (id bigint, first_name text, job_title varchar)
        LANGUAGE sql STABLE AS $$ SELECT id, first_name, job_title FROM contact WHERE last_name = p_last_name ORDER BY first_name $$;
    
    -- Place an order: writes sales_order and order_line, returns the new id and the total.
    CREATE PROCEDURE sp_place_order(p_account_id bigint, p_order_number text, p_skus text[], p_quantity int, OUT p_order_id bigint, OUT p_total numeric)
        LANGUAGE plpgsql AS $$
        BEGIN
            INSERT INTO sales_order (order_number, account_id, ordered_on) VALUES (p_order_number, p_account_id, now()) RETURNING id INTO p_order_id;
            INSERT INTO order_line (order_id, product_id, quantity, unit_price)
                SELECT p_order_id, p.id, p_quantity, p.unit_price FROM product p WHERE p.sku = ANY (p_skus);
            SELECT coalesce(sum(quantity * unit_price), 0) INTO p_total FROM order_line WHERE order_id = p_order_id;
            UPDATE sales_order SET total = p_total WHERE id = p_order_id;
        END $$;
    
    -- Tables from a procedure: an order and its lines; an account report with three tables.
    CREATE PROCEDURE sp_order_with_lines(p_order_id bigint, INOUT p_order refcursor DEFAULT NULL, INOUT p_lines refcursor DEFAULT NULL)
        LANGUAGE plpgsql AS $$
        BEGIN
            OPEN p_order FOR SELECT o.order_number, o.ordered_on, o.total, a.name AS account FROM sales_order o JOIN account a ON a.id = o.account_id WHERE o.id = p_order_id;
            OPEN p_lines FOR SELECT p.sku, l.quantity, l.unit_price FROM order_line l JOIN product p ON p.id = l.product_id WHERE l.order_id = p_order_id ORDER BY p.sku;
        END $$;
    CREATE PROCEDURE sp_account_report(p_account_id bigint, INOUT p_contacts refcursor DEFAULT NULL, INOUT p_orders refcursor DEFAULT NULL, INOUT p_summary refcursor DEFAULT NULL)
        LANGUAGE plpgsql AS $$
        BEGIN
            OPEN p_contacts FOR SELECT first_name, last_name, job_title FROM contact WHERE account_id = p_account_id ORDER BY first_name;
            OPEN p_orders FOR SELECT order_number, total FROM sales_order WHERE account_id = p_account_id ORDER BY order_number;
            OPEN p_summary FOR SELECT (SELECT count(*) FROM contact WHERE account_id = p_account_id) AS contacts,
                                      (SELECT count(*) FROM sales_order WHERE account_id = p_account_id) AS orders,
                                      (SELECT coalesce(sum(total), 0) FROM sales_order WHERE account_id = p_account_id) AS revenue;
        END $$;
    
    -- Paging: a procedure that pages itself; a SQL function ARX pages; a procedure's table (a cursor) ARX pages.
    CREATE PROCEDURE sp_products_page(p_prefix text, p_page int, p_page_size int, INOUT p_rows refcursor DEFAULT NULL, INOUT p_total bigint DEFAULT NULL)
        LANGUAGE plpgsql AS $$
        BEGIN
            SELECT count(*) INTO p_total FROM product WHERE sku LIKE p_prefix || '%';
            OPEN p_rows FOR SELECT sku, name, unit_price FROM product WHERE sku LIKE p_prefix || '%' ORDER BY sku LIMIT p_page_size OFFSET (p_page - 1) * p_page_size;
        END $$;
    CREATE FUNCTION fn_products_like(p_prefix text) RETURNS TABLE (sku varchar, name text, unit_price numeric)
        LANGUAGE sql STABLE AS $$ SELECT sku, name, unit_price FROM product WHERE sku LIKE p_prefix || '%' $$;
    CREATE PROCEDURE sp_products(p_prefix text, INOUT p_rows refcursor DEFAULT NULL)
        LANGUAGE plpgsql AS $$ BEGIN OPEN p_rows FOR SELECT sku, name, unit_price FROM product WHERE sku LIKE p_prefix || '%' ORDER BY sku; END $$;
    
    -- Business rules the database enforces: a credit limit (RAISE with a hint), an account on hold (its own code).
    CREATE PROCEDURE sp_reserve_credit(p_account_id bigint, p_amount numeric)
        LANGUAGE plpgsql AS $$
        DECLARE v_limit numeric; v_status text;
        BEGIN
            SELECT credit_limit, status INTO v_limit, v_status FROM account WHERE id = p_account_id;
            IF v_status = 'ON_HOLD' THEN
                RAISE EXCEPTION 'account % is on hold', p_account_id USING ERRCODE = 'AH001', HINT = 'release the hold first';
            END IF;
            IF p_amount > coalesce(v_limit, 0) THEN
                RAISE EXCEPTION 'credit limit exceeded' USING DETAIL = format('limit %s, asked %s', v_limit, p_amount), HINT = format('reserve %s or less', v_limit);
            END IF;
            UPDATE account SET credit_limit = credit_limit - p_amount, modified_on = now() WHERE id = p_account_id;
        END $$;
    
    -- A nightly job that commits each batch and reports progress; a slow procedure.
    CREATE PROCEDURE sp_recalculate_totals(p_prefix text, p_batch int)
        LANGUAGE plpgsql AS $$
        DECLARE v_done int := 0; v_batch int;
        BEGIN
            LOOP
                UPDATE sales_order o SET total = coalesce((SELECT sum(l.quantity * l.unit_price) FROM order_line l WHERE l.order_id = o.id), 0)
                WHERE o.id IN (SELECT id FROM sales_order WHERE order_number LIKE p_prefix || '%' AND total < 0 ORDER BY id LIMIT p_batch);
                GET DIAGNOSTICS v_batch = ROW_COUNT;
                EXIT WHEN v_batch = 0;
                v_done := v_done + v_batch;
                COMMIT;
                RAISE NOTICE 'recalculated % orders', v_done;
            END LOOP;
            RAISE INFO 'done: % orders', v_done;
        END $$;
    CREATE PROCEDURE sp_import_progress(p_rows int)
        LANGUAGE plpgsql AS $$
        BEGIN
            FOR i IN 1..p_rows LOOP
                IF i % 250 = 0 THEN RAISE NOTICE 'imported % of % rows', i, p_rows; END IF;
            END LOOP;
            IF p_rows > 900 THEN RAISE WARNING 'a large import: % rows', p_rows; END IF;
        END $$;
    CREATE PROCEDURE sp_slow(p_seconds double precision)
        LANGUAGE sql AS $$ SELECT pg_sleep(p_seconds) $$;
    
  5. Configure the connection

    The samples read the connection string from the ARX_SAMPLES_POSTGRESQL environment variable, never from the code. Set it in the terminal you run them from (with your password, where the database has one):

    Terminal

    macOS and Linux (bash, zsh)

    export ARX_SAMPLES_POSTGRESQL="Host=localhost;Port=5432;Database=arx_samples;Username=postgres;Password=your-password"
    PowerShell

    Windows PowerShell

    $env:ARX_SAMPLES_POSTGRESQL = "Host=localhost;Port=5432;Database=arx_samples;Username=postgres;Password=your-password"
  6. Add a sample and run it

    Each sample page lists its files: copy each into the project with the file name shown, then run:

    Terminal
    dotnet run

    The page shows the output to expect. Ids may differ from yours if the tables already hold rows. Most samples write values that must be unique (an account number, a code), so a second run of the same sample fails with ARX3001: run the schema script again first, to start from empty sample tables.