PostgreSQL · Delete
Undo an import of 5,000 contacts
A contact import went wrong and must be undone, in two scenarios: all 5,000 imported contacts by their lead source in one call; and the same call again, which finds nothing and says so with 0.
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:
var imported = Enumerable.Range(1, 5_000).Select(i =>
{
var contact = new ArxEntity("contact");
contact["first_name"] = "Imported";
contact["last_name"] = $"Row {i}";
contact["email"] = $"bad-import-{i}@example.com";
contact["lead_source"] = "Import 2026-11-08 (bad)";
return contact;
}).ToList();
await service.CreateMultipleAsync(imported);1 Every contact of the bad import
The import tagged every row with its lead source: delete by that column. One statement.
Already in scope: service (ArxPgDbService)
var deleted = await service.DeleteAsync(TheBadImport(), new ArxDeleteOptions { Match = ArxMatch.Columns("lead_source") });
Console.WriteLine($"{deleted} contacts deleted");Defined once in this sample.
private static ArxEntity TheBadImport()
{
var match = new ArxEntity("contact");
match["lead_source"] = "Import 2026-11-08 (bad)";
return match;
}Check it in the database
Run it in any SQL tool connected to the database, after the code has run.
SELECT count(*)
FROM contact
WHERE lead_source = 'Import 2026-11-08 (bad)';Expect: 0.
2 Run the clean-up again
Running the clean-up again is safe: nothing matches, 0 deleted, no error.
Already in scope: service (ArxPgDbService)
var deleted = await service.DeleteAsync(TheBadImport(), new ArxDeleteOptions { Match = ArxMatch.Columns("lead_source") });
Console.WriteLine($"{deleted} deleted the second time");Defined once in this sample.
private static ArxEntity TheBadImport()
{
var match = new ArxEntity("contact");
match["lead_source"] = "Import 2026-11-08 (bad)";
return match;
}Check it in the database
Run it in any SQL tool connected to the database, after the code has run.
SELECT count(*)
FROM contact
WHERE email LIKE 'bad-import-%';Expect: 0.
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.PostgreSql --version 1.0.0-rc.1 - Database: a PostgreSQL 16 (tested with 16.15) database with the sample schema.
- Configuration: set
ARX_SAMPLES_POSTGRESQLto 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.PostgreSql;
using ArxSamples.PostgreSql.Operations.Delete.RealCases;
// The connection string comes from the environment, never from the code (see "Configure the connection").
var connectionString = Environment.GetEnvironmentVariable("ARX_SAMPLES_POSTGRESQL")
?? throw new InvalidOperationException("Set the ARX_SAMPLES_POSTGRESQL environment variable to your PostgreSQL connection string.");
await new RealCase2_CleanUpAnImport().RunAsync(new SampleContext(connectionString));
The sample, as it is in the repository.
using ARX.SDK.Services;
using static ArxSamples.PostgreSql.SampleOutput;
namespace ArxSamples.PostgreSql.Operations.Delete.RealCases;
/// <summary>
/// A contact import went wrong and must be undone, in two scenarios: all 5,000 imported contacts by their lead source in
/// one call; and the same call again, which finds nothing and says so with 0.
/// </summary>
public sealed class RealCase2_CleanUpAnImport : ISample
{
public string Name => "delete-real-2-clean-up-an-import";
public string Title => "Undo an import of 5,000 contacts";
public async Task RunAsync(SampleContext dbContext)
{
var imported = Enumerable.Range(1, 5_000).Select(i =>
{
var contact = new ArxEntity("contact");
contact["first_name"] = "Imported";
contact["last_name"] = $"Row {i}";
contact["email"] = $"bad-import-{i}@example.com";
contact["lead_source"] = "Import 2026-11-08 (bad)";
return contact;
}).ToList();
await dbContext.Service.CreateMultipleAsync(imported);
await Scenario1_EveryImportedContactAsync(dbContext);
await Scenario2_RunItAgainAsync(dbContext);
}
private static ArxEntity TheBadImport()
{
var match = new ArxEntity("contact");
match["lead_source"] = "Import 2026-11-08 (bad)";
return match;
}
/// <summary>1. The import tagged every row with its lead source: delete by that column. One statement.</summary>
private static async Task Scenario1_EveryImportedContactAsync(SampleContext dbContext)
{
Step("1. Every contact of the bad import");
var deleted = await dbContext.Service.DeleteAsync(TheBadImport(), new ArxDeleteOptions { Match = ArxMatch.Columns("lead_source") });
Show($"{deleted} contacts deleted");
/* ===========================================================
* [DIRECT QUERY CHECK]
* -----------------------------------------------------------
* SELECT count(*)
* FROM contact
* WHERE lead_source = 'Import 2026-11-08 (bad)';
* -- Expect: 0.
* ===========================================================
*/
}
/// <summary>2. Running the clean-up again is safe: nothing matches, 0 deleted, no error.</summary>
private static async Task Scenario2_RunItAgainAsync(SampleContext dbContext)
{
Step("2. Run the clean-up again");
var deleted = await dbContext.Service.DeleteAsync(TheBadImport(), new ArxDeleteOptions { Match = ArxMatch.Columns("lead_source") });
Show($"{deleted} deleted the second time");
/* ===========================================================
* [DIRECT QUERY CHECK]
* -----------------------------------------------------------
* SELECT count(*)
* FROM contact
* WHERE email LIKE 'bad-import-%';
* -- Expect: 0.
* ===========================================================
*/
}
}
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. Every contact of the bad import
5000 contacts deleted
> 2. Run the clean-up again
0 deleted the second time