MySQL · Update

An edit form changes some account details

An "edit account" form, in three scenarios: the fields the user changed are written and the rest kept; a field the user emptied is cleared; the form sends the id it was opened with.

Set up for this page

The scenarios build on the sample application: SampleContext.cs (the dbContext every sample is given); SampleOutput.cs (Step and Show, which print); SampleContext.Application.cs (dbContext.Service, the application's ARX service); SampleCatalog.cs (the tables' ids, keys, lookups and display names).

1 Write only the fields the user changed

The form posts only what the user changed. Each posted field becomes an attribute; nothing else is touched.

Already in scope: service (ArxMySqlDbService), id (ArxRecordId)

C#
var posted = new Dictionary<string, object>
{
    ["employee_count"] = 75,
    ["annual_revenue"] = 4_800_000.00m
};

var change = new ArxEntity("account");
change.RecordId = id;

foreach (var (field, value) in posted)
{
    change[field] = value;
}

var changed = await service.UpdateAsync(change);
Console.WriteLine($"{changed} row changed: {string.Join(", ", posted.Keys)}");

Check it in the database

SQL

Run it in any SQL tool connected to the database, after the code has run.

SELECT name, website, employee_count, annual_revenue
FROM account
WHERE account_number = 'ACC-U601';

Expect: 1 row, Alpine Ski House, https://alpineskihouse.example, employee_count 75, annual_revenue 4800000.00.

2 An emptied field is cleared

The user emptied the website box: the form sends null, and the column is cleared.

Already in scope: service (ArxMySqlDbService), id (ArxRecordId)

C#
var change = new ArxEntity("account");
change.RecordId = id;
change["website"] = null;
await service.UpdateAsync(change);

Console.WriteLine("website cleared");

Check it in the database

SQL

Run it in any SQL tool connected to the database, after the code has run.

SELECT website
FROM account
WHERE account_number = 'ACC-U602';

Expect: 1 row, NULL.

3 The id comes back with the form

The form was opened with the account's id in a hidden field: it comes back as the id attribute.

Already in scope: service (ArxMySqlDbService), id (ArxRecordId)

C#
var hiddenField = id.AsInt64().ToString();

var change = new ArxEntity("account");
change["id"] = long.Parse(hiddenField);
change["name"] = "Alpine Ski House Ltd.";
await service.UpdateAsync(change);

Console.WriteLine($"account {hiddenField} renamed");

Check it in the database

SQL

Run it in any SQL tool connected to the database, after the code has run.

SELECT name
FROM account
WHERE account_number = 'ACC-U603';

Expect: 1 row, Alpine Ski House Ltd.

Run the complete sample

The code above comes from one program, built in a fresh project and run against the database. To run it yourself, here is every file and step.

The program, its files and how to run it

Run it locally

  1. .NET SDK: .NET SDK 10.0.100 or later 10.0.x (target net10.0).
  2. Create the project:
    Terminal
    dotnet new console -n ArxSample -f net10.0
    cd ArxSample
  3. Install the packages:
    Terminal
    dotnet add package ARX.SDK.MySQL --version 1.0.0-rc.1
  4. Database: a MySQL 8.4 (tested with 8.4.13) database with the sample schema.
  5. Configuration: set ARX_SAMPLES_MYSQL to its connection string. Nothing is hard-coded.
  6. Source code: add the files of the sample application this sample uses (SampleContext.cs, SampleOutput.cs, SampleContext.Application.cs, SampleCatalog.cs), then replace Program.cs and add the other file below, with the file names shown.
  7. Run it:
    Terminal
    dotnet run
  8. Expected output: below.

The files

Program.cs

Runs the sample. Generated for this page; it reads the connection string from the environment.

using ArxSamples.MySQL;
using ArxSamples.MySQL.Operations.Update.RealCases;

// The connection string comes from the environment, never from the code (see "Configure the connection").
var connectionString = Environment.GetEnvironmentVariable("ARX_SAMPLES_MYSQL")
    ?? throw new InvalidOperationException("Set the ARX_SAMPLES_MYSQL environment variable to your MySQL connection string.");

await new RealCase1_ChangeAccountDetails().RunAsync(new SampleContext(connectionString));
RealCase1_ChangeAccountDetails.cs

The sample, as it is in the repository.

using ARX.SDK.Services;
using static ArxSamples.MySQL.SampleOutput;

namespace ArxSamples.MySQL.Operations.Update.RealCases;

/// <summary>
/// An "edit account" form, in three scenarios: the fields the user changed are written and the rest kept; a field the
/// user emptied is cleared; the form sends the id it was opened with.
/// </summary>
public sealed class RealCase1_ChangeAccountDetails : ISample
{
    public string Name => "update-real-1-change-account-details";

    public string Title => "An edit form changes some account details";

    public async Task RunAsync(SampleContext dbContext)
    {
        // One account per scenario, so each scenario's check holds after the whole sample has run.
        await Scenario1_OnlyTheChangedFieldsAsync(dbContext, await CreateAccountAsync(dbContext, "ACC-U601"));

        await Scenario2_AnEmptiedFieldIsClearedAsync(dbContext, await CreateAccountAsync(dbContext, "ACC-U602"));

        await Scenario3_TheFormSendsTheIdAsync(dbContext, await CreateAccountAsync(dbContext, "ACC-U603"));
    }

    private static async Task<ArxRecordId> CreateAccountAsync(SampleContext dbContext, string number)
    {
        var account = new ArxEntity("account");
        account["name"] = "Alpine Ski House";
        account["account_number"] = number;
        account["website"] = "https://alpineskihouse.example";
        account["employee_count"] = 60;
        account["annual_revenue"] = 4_200_000.00m;
        return await dbContext.Service.CreateAsync(account);
    }

    /// <summary>1. The form posts only what the user changed. Each posted field becomes an attribute; nothing else is touched.</summary>
    private static async Task Scenario1_OnlyTheChangedFieldsAsync(SampleContext dbContext, ArxRecordId id)
    {
        Step("1. Write only the fields the user changed");

        var posted = new Dictionary<string, object> 
        { 
            ["employee_count"] = 75, 
            ["annual_revenue"] = 4_800_000.00m 
        };

        var change = new ArxEntity("account");
        change.RecordId = id;

        foreach (var (field, value) in posted)
        {
            change[field] = value;
        }

        var changed = await dbContext.Service.UpdateAsync(change);
        Show($"{changed} row changed: {string.Join(", ", posted.Keys)}");

        /* ===========================================================
         * [DIRECT QUERY CHECK]
         * -----------------------------------------------------------
         * SELECT name, website, employee_count, annual_revenue
         * FROM account
         * WHERE account_number = 'ACC-U601';
         * -- Expect: 1 row, Alpine Ski House, https://alpineskihouse.example, employee_count 75, annual_revenue 4800000.00.
         * ===========================================================
         */
    }

    /// <summary>2. The user emptied the website box: the form sends null, and the column is cleared.</summary>
    private static async Task Scenario2_AnEmptiedFieldIsClearedAsync(SampleContext dbContext, ArxRecordId id)
    {
        Step("2. An emptied field is cleared");

        var change = new ArxEntity("account");
        change.RecordId = id;
        change["website"] = null;
        await dbContext.Service.UpdateAsync(change);

        Show("website cleared");

        /* ===========================================================
         * [DIRECT QUERY CHECK]
         * -----------------------------------------------------------
         * SELECT website
         * FROM account
         * WHERE account_number = 'ACC-U602';
         * -- Expect: 1 row, NULL.
         * ===========================================================
         */
    }

    /// <summary>3. The form was opened with the account's id in a hidden field: it comes back as the id attribute.</summary>
    private static async Task Scenario3_TheFormSendsTheIdAsync(SampleContext dbContext, ArxRecordId id)
    {
        Step("3. The id comes back with the form");

        var hiddenField = id.AsInt64().ToString();

        var change = new ArxEntity("account");
        change["id"] = long.Parse(hiddenField);
        change["name"] = "Alpine Ski House Ltd.";
        await dbContext.Service.UpdateAsync(change);

        Show($"account {hiddenField} renamed");

        /* ===========================================================
         * [DIRECT QUERY CHECK]
         * -----------------------------------------------------------
         * SELECT name
         * FROM account
         * WHERE account_number = 'ACC-U603';
         * -- Expect: 1 row, Alpine Ski House Ltd.
         * ===========================================================
         */
    }
}

Expected output

What dotnet run printed when the sample was verified, on a freshly created sample schema. Ids differ when the tables already hold rows.

Output
  > 1. Write only the fields the user changed
      1 row changed: employee_count, annual_revenue
  > 2. An emptied field is cleared
      website cleared
  > 3. The id comes back with the form
      account 3 renamed