SQL Server · Delete
Soft delete: close and restore an account
A soft delete keeps the row and marks it closed: it's an update of a status column, not a delete. Two scenarios: close an account; restore it.
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).
Before scenario 1 the sample runs this, so the service and the rows the scenarios use are there:
foreach (var number in new[] { "ACC-D601", "ACC-D602" })
{
var account = new ArxEntity("account");
account["name"] = "Soft deleted customer " + number;
account["account_number"] = number;
await service.CreateAsync(account);
}1 Close the account instead of deleting it
"Delete" in the app closes the account: an update of its status, by the account number.
Already in scope: service (ArxMsSqlDbService)
await service.UpdateAsync(Status("ACC-D601", "CLOSED"), new ArxUpdateOptions { Match = ArxMatch.Key("account_number") });
Console.WriteLine("ACC-D601 closed; its history stays");Defined once in this sample.
private static ArxEntity Status(string number, string status)
{
var change = new ArxEntity("account");
change["account_number"] = number;
change["status"] = new ArxSelectionSetValue("AccountStatus", status);
return change;
}Check it in the database
Run it in any SQL tool connected to the database, after the code has run.
SELECT account_number, status
FROM account
WHERE account_number = 'ACC-D601';Expect: 1 row, CLOSED.
2 Restore it
Undo is another update.
Already in scope: service (ArxMsSqlDbService)
var byNumber = new ArxUpdateOptions { Match = ArxMatch.Key("account_number") };
await service.UpdateAsync(Status("ACC-D602", "CLOSED"), byNumber);
await service.UpdateAsync(Status("ACC-D602", "ACTIVE"), byNumber);
Console.WriteLine("ACC-D602 closed, then active again");Defined once in this sample.
private static ArxEntity Status(string number, string status)
{
var change = new ArxEntity("account");
change["account_number"] = number;
change["status"] = new ArxSelectionSetValue("AccountStatus", status);
return change;
}Check it in the database
Run it in any SQL tool connected to the database, after the code has run.
SELECT account_number, status
FROM account
WHERE account_number = 'ACC-D602';Expect: 1 row, ACTIVE.
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
- .NET SDK: .NET SDK 10.0.100 or later 10.0.x (target
net10.0). - Create the project:
Terminal dotnet new console -n ArxSample -f net10.0 cd ArxSample - Install the packages:
Terminal dotnet add package ARX.SDK.SQL --version 1.0.0-rc.1 - Database: a SQL Server 2022 (tested with 16.0.4225.2) database with the sample schema.
- Configuration: set
ARX_SAMPLES_SQLSERVERto its connection string. Nothing is hard-coded. - Source code: add the files of the sample application this sample uses (
SampleContext.cs,SampleOutput.cs,SampleContext.Application.cs,SampleCatalog.cs), then replaceProgram.csand add the other file below, with the file names shown. - Run it:
Terminal dotnet run - Expected output: below.
The files
Runs the sample. Generated for this page; it reads the connection string from the environment.
using ArxSamples.MSSQL;
using ArxSamples.MSSQL.Operations.Delete.RealCases;
// The connection string comes from the environment, never from the code (see "Configure the connection").
var connectionString = Environment.GetEnvironmentVariable("ARX_SAMPLES_SQLSERVER")
?? throw new InvalidOperationException("Set the ARX_SAMPLES_SQLSERVER environment variable to your SQL Server connection string.");
await new RealCase4_SoftDelete().RunAsync(new SampleContext(connectionString));
The sample, as it is in the repository.
using ARX.SDK.Services;
using static ArxSamples.MSSQL.SampleOutput;
namespace ArxSamples.MSSQL.Operations.Delete.RealCases;
/// <summary>
/// A soft delete keeps the row and marks it closed: it's an update of a status column, not a delete. Two
/// scenarios: close an account; restore it.
/// </summary>
public sealed class RealCase4_SoftDelete : ISample
{
public string Name => "delete-real-4-soft-delete";
public string Title => "Soft delete: close and restore an account";
public async Task RunAsync(SampleContext dbContext)
{
// ACC-D601 is closed and stays closed; ACC-D602 is closed, then restored.
foreach (var number in new[] { "ACC-D601", "ACC-D602" })
{
var account = new ArxEntity("account");
account["name"] = "Soft deleted customer " + number;
account["account_number"] = number;
await dbContext.Service.CreateAsync(account);
}
await Scenario1_CloseTheAccountAsync(dbContext);
await Scenario2_RestoreItAsync(dbContext);
}
private static ArxEntity Status(string number, string status)
{
var change = new ArxEntity("account");
change["account_number"] = number;
change["status"] = new ArxSelectionSetValue("AccountStatus", status);
return change;
}
/// <summary>1. "Delete" in the app closes the account: an update of its status, by the account number.</summary>
private static async Task Scenario1_CloseTheAccountAsync(SampleContext dbContext)
{
Step("1. Close the account instead of deleting it");
await dbContext.Service.UpdateAsync(Status("ACC-D601", "CLOSED"), new ArxUpdateOptions { Match = ArxMatch.Key("account_number") });
Show("ACC-D601 closed; its history stays");
/* ===========================================================
* [DIRECT QUERY CHECK]
* -----------------------------------------------------------
* SELECT account_number, status
* FROM account
* WHERE account_number = 'ACC-D601';
* -- Expect: 1 row, CLOSED.
* ===========================================================
*/
}
/// <summary>2. Undo is another update.</summary>
private static async Task Scenario2_RestoreItAsync(SampleContext dbContext)
{
Step("2. Restore it");
var byNumber = new ArxUpdateOptions { Match = ArxMatch.Key("account_number") };
await dbContext.Service.UpdateAsync(Status("ACC-D602", "CLOSED"), byNumber);
await dbContext.Service.UpdateAsync(Status("ACC-D602", "ACTIVE"), byNumber);
Show("ACC-D602 closed, then active again");
/* ===========================================================
* [DIRECT QUERY CHECK]
* -----------------------------------------------------------
* SELECT account_number, status
* FROM account
* WHERE account_number = 'ACC-D602';
* -- Expect: 1 row, ACTIVE.
* ===========================================================
*/
}
}
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.
> 1. Close the account instead of deleting it
ACC-D601 closed; its history stays
> 2. Restore it
ACC-D602 closed, then active again