MongoDB · Update
Choose how the documents are found
The three ways to find the documents to change, in four scenarios: by a declared key, by any fields (several documents), with RequireSingleRow, and many entities at once, all or nothing.
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); SampleContext.Direct.cs (dbContext.Direct); MongoDirectHelper.cs (the checks behind dbContext.Direct).
dbContext.Direct is the samples' own checking code on the plain driver, not part of ARX: leave it out of your application.
Before scenario 1 the sample runs this, so the service and the rows the scenarios use are there:
foreach (var (name, number) in new[] { ("Adatum", "ACC-M811"), ("Margie's Travel", "ACC-M812"), ("Lucerne Publishing", "ACC-M813") })
{
var account = new ArxEntity("account");
account["name"] = name;
account["account_number"] = number;
account["region"] = "M81";
await service.CreateAsync(account);
}1 By a declared key
By the account number, a declared key.
Already in scope: service (ArxMongoDbService)
var change = new ArxEntity("account");
change["account_number"] = "ACC-M811";
change["employee_count"] = 120;
await service.UpdateAsync(change, new ArxUpdateOptions { Match = ArxMatch.Key("account_number") });
Console.WriteLine("ACC-M811 changed");Check it in the database
Run it in mongosh connected to the database, after the code has run.
db.account.find({ account_number: "ACC-M811" }, { employee_count: 1 })Expect: 1 document, employee_count 120.
2 By any fields
By any fields: every account in region M81 changes, and the call says how many.
Already in scope: service (ArxMongoDbService)
var change = new ArxEntity("account");
change["region"] = "M81";
change["status"] = new ArxSelectionSetValue("AccountStatus", "ACTIVE");
int changed = await service.UpdateAsync(change, new ArxUpdateOptions { Match = ArxMatch.Columns("region") });
Console.WriteLine($"{changed} documents changed");Check it in the database
Run it in mongosh connected to the database, after the code has run.
db.account.countDocuments({ region: "M81", status: "ACTIVE" })Expect: 3.
3 Require exactly one
RequireSingleRow: the region matches three documents, so nothing changes (ARX2003).
Already in scope: service (ArxMongoDbService)
var change = new ArxEntity("account");
change["region"] = "M81";
change["status"] = new ArxSelectionSetValue("AccountStatus", "CLOSED");
try
{
await service.UpdateAsync(change, new ArxUpdateOptions { Match = ArxMatch.Columns("region"), RequireSingleRow = true });
}
catch (ArxException exception) when (exception.Code == ArxErrorCode.MultipleRecordsMatched)
{
Console.WriteLine(exception.Message);
}Check it in the database
Run it in mongosh connected to the database, after the code has run.
db.account.countDocuments({ region: "M81", status: "CLOSED" })Expect: 0.
4 Many at once, all or nothing
Many entities at once by their key: one of them has no document, so none changes (ARX2002).
Already in scope: service (ArxMongoDbService)
var changes = new[] { "ACC-M812", "ACC-M813", "ACC-M819" }.Select(number =>
{
var change = new ArxEntity("account");
change["account_number"] = number;
change["employee_count"] = 9;
return change;
}).ToList();
try
{
await service.UpdateMultipleAsync(changes, new ArxUpdateOptions { Match = ArxMatch.Key("account_number") });
}
catch (ArxException exception) when (exception.Code == ArxErrorCode.RecordNotFound)
{
Console.WriteLine(exception.Message);
}Check it in the database
Run it in mongosh connected to the database, after the code has run.
db.account.countDocuments({ region: "M81", employee_count: 9 })Expect: 0.
Good to know
ArxMatch.Key needs a key the schema declares; ArxMatch.Columns takes any fields. Match fields are never written. UpdateMultiple runs in a transaction: if any entity finds nothing, nothing changes.
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.MongoDb --version 1.0.0-rc.1 - Database: a MongoDB 7.0, a replica set (tested with 7.0.45, one node) database with the sample schema.
- Configuration: set
ARX_SAMPLES_MONGODBto 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,SampleContext.Direct.cs,MongoDirectHelper.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 and first remakes the sample database from sample-schema.json.
using ArxSamples.MongoDb;
using ArxSamples.MongoDb.Operations.Update.Basics;
// The connection string comes from the environment, never from the code (see "Configure the connection").
var connectionString = Environment.GetEnvironmentVariable("ARX_SAMPLES_MONGODB")
?? throw new InvalidOperationException("Set the ARX_SAMPLES_MONGODB environment variable to your MongoDB connection string.");
var dbContext = new SampleContext(connectionString);
// Every run starts from the sample data: this deletes and makes again what sample-schema.json lists
// (save the file in the project folder, see Get started).
await dbContext.Direct.ResetAsync();
await new Basic2_HowToFindTheRows().RunAsync(dbContext);
The sample, as it is in the repository.
using ARX.SDK;
using ARX.SDK.MongoDb.Services;
using ARX.SDK.Services;
using static ArxSamples.MongoDb.SampleOutput;
namespace ArxSamples.MongoDb.Operations.Update.Basics;
/// <summary>
/// The three ways to find the documents to change, in four scenarios: by a declared key, by any fields (several
/// documents), with RequireSingleRow, and many entities at once, all or nothing.
/// </summary>
/// <remarks>
/// <c>ArxMatch.Key</c> needs a key the schema declares; <c>ArxMatch.Columns</c> takes any fields. Match fields are never
/// written. UpdateMultiple runs in a transaction: if any entity finds nothing, nothing changes.
/// </remarks>
public sealed class Basic2_HowToFindTheRows : ISample
{
public string Name => "update-basics-2-how-to-find-the-rows";
public string Title => "Choose how the documents are found";
public async Task RunAsync(SampleContext dbContext)
{
foreach (var (name, number) in new[] { ("Adatum", "ACC-M811"), ("Margie's Travel", "ACC-M812"), ("Lucerne Publishing", "ACC-M813") })
{
var account = new ArxEntity("account");
account["name"] = name;
account["account_number"] = number;
account["region"] = "M81";
await dbContext.Service.CreateAsync(account);
}
await Scenario1_ByADeclaredKeyAsync(dbContext.Service);
await Scenario2_ByAnyFieldsAsync(dbContext.Service);
await Scenario3_RequireOneAsync(dbContext.Service);
await Scenario4_ManyAtOnceAsync(dbContext.Service);
}
/// <summary>1. By the account number, a declared key.</summary>
private static async Task Scenario1_ByADeclaredKeyAsync(ArxMongoDbService service)
{
Step("1. By a declared key");
var change = new ArxEntity("account");
change["account_number"] = "ACC-M811";
change["employee_count"] = 120;
await service.UpdateAsync(change, new ArxUpdateOptions { Match = ArxMatch.Key("account_number") });
Show("ACC-M811 changed");
/* ===========================================================
* [DIRECT QUERY CHECK]
* -----------------------------------------------------------
* db.account.find({ account_number: "ACC-M811" }, { employee_count: 1 })
* -- Expect: 1 document, employee_count 120.
* ===========================================================
*/
}
/// <summary>2. By any fields: every account in region M81 changes, and the call says how many.</summary>
private static async Task Scenario2_ByAnyFieldsAsync(ArxMongoDbService service)
{
Step("2. By any fields");
var change = new ArxEntity("account");
change["region"] = "M81";
change["status"] = new ArxSelectionSetValue("AccountStatus", "ACTIVE");
int changed = await service.UpdateAsync(change, new ArxUpdateOptions { Match = ArxMatch.Columns("region") });
Show($"{changed} documents changed");
/* ===========================================================
* [DIRECT QUERY CHECK]
* -----------------------------------------------------------
* db.account.countDocuments({ region: "M81", status: "ACTIVE" })
* -- Expect: 3.
* ===========================================================
*/
}
/// <summary>3. RequireSingleRow: the region matches three documents, so nothing changes (ARX2003).</summary>
private static async Task Scenario3_RequireOneAsync(ArxMongoDbService service)
{
Step("3. Require exactly one");
var change = new ArxEntity("account");
change["region"] = "M81";
change["status"] = new ArxSelectionSetValue("AccountStatus", "CLOSED");
try
{
await service.UpdateAsync(change, new ArxUpdateOptions { Match = ArxMatch.Columns("region"), RequireSingleRow = true });
}
catch (ArxException exception) when (exception.Code == ArxErrorCode.MultipleRecordsMatched)
{
ShowError(exception);
}
/* ===========================================================
* [DIRECT QUERY CHECK]
* -----------------------------------------------------------
* db.account.countDocuments({ region: "M81", status: "CLOSED" })
* -- Expect: 0.
* ===========================================================
*/
}
/// <summary>4. Many entities at once by their key: one of them has no document, so none changes (ARX2002).</summary>
private static async Task Scenario4_ManyAtOnceAsync(ArxMongoDbService service)
{
Step("4. Many at once, all or nothing");
var changes = new[] { "ACC-M812", "ACC-M813", "ACC-M819" }.Select(number =>
{
var change = new ArxEntity("account");
change["account_number"] = number;
change["employee_count"] = 9;
return change;
}).ToList();
try
{
await service.UpdateMultipleAsync(changes, new ArxUpdateOptions { Match = ArxMatch.Key("account_number") });
}
catch (ArxException exception) when (exception.Code == ArxErrorCode.RecordNotFound)
{
ShowError(exception);
}
/* ===========================================================
* [DIRECT QUERY CHECK]
* -----------------------------------------------------------
* db.account.countDocuments({ region: "M81", employee_count: 9 })
* -- 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. By a declared key
ACC-M811 changed
> 2. By any fields
3 documents changed
> 3. Require exactly one
caught ArxException: ARX2003 MultipleRecordsMatched: Update of 'account': the entity matched several documents by columns (region), but RequireSingleRow needs exactly one. Nothing was changed.
> 4. Many at once, all or nothing
caught ArxException: ARX2002 RecordNotFound: UpdateMultiple of 'account': entities[2] matched no document. Nothing was changed.