MongoDB · Create
Catch the errors a create meets
The errors a create meets most, in three scenarios: a duplicate (a unique index), a value ARX refuses before sending anything, and an id MongoDB can't make.
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.
1 A duplicate
A second account with the same number: ARX3001, with the index's name.
Already in scope: service (ArxMongoDbService)
var account = new ArxEntity("account");
account["name"] = "Proseware, Inc.";
account["account_number"] = "ACC-M301";
await service.CreateAsync(account);
var again = new ArxEntity("account");
again["name"] = "Proseware copy";
again["account_number"] = "ACC-M301";
try
{
await service.CreateAsync(again);
}
catch (ArxDatabaseException exception) when (exception.Code == ArxErrorCode.UniqueViolation)
{
Console.WriteLine(exception.Message);
Console.WriteLine($"index {exception.ConstraintName}, MongoDB error {exception.NativeCode}");
}Check it in the database
Run it in mongosh connected to the database, after the code has run.
db.account.countDocuments({ account_number: "ACC-M301" })Expect: 1.
2 A date that means "not set"
default(DateTime) means "not set": ARX1013 before anything is sent.
Already in scope: service (ArxMongoDbService)
var account = new ArxEntity("account");
account["account_number"] = "ACC-M302";
account["created_on"] = default(DateTime);
try
{
await service.CreateAsync(account);
}
catch (ArxValidationException exception) when (exception.Code == ArxErrorCode.InvalidDateTimeValue)
{
Console.WriteLine(exception.Message);
}Check it in the database
Run it in mongosh connected to the database, after the code has run.
db.account.find({ account_number: "ACC-M302" })Expect: no documents.
3 A generated number id
A generated number id: MongoDB has no sequences, so ARX8001; use an ObjectId or a Guid.
Already in scope: connectionString (string)
var schema = ArxSchemaCatalog.Create()
.Table("activity_log", ArxIdColumn.Generated<long>("log_id"))
.Build();
var service = new ArxMongoDbService(connectionString, schema);
var entry = new ArxEntity("activity_log");
entry["action"] = "signed in";
try
{
await service.CreateAsync(entry);
}
catch (ArxNotSupportedException exception)
{
Console.WriteLine(exception.Message);
}Check it in the database
Run it in mongosh connected to the database, after the code has run.
db.activity_log.find({ action: "signed in" })Expect: no documents.
Good to know
Every error is an ArxException with a stable code. The message names the index, never the value (MongoDB's own text quotes it). A plain insert never replaces a document.
See also
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.Create.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 Basic3_CatchAnError().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.Create.Basics;
/// <summary>
/// The errors a create meets most, in three scenarios: a duplicate (a unique index), a value ARX refuses before
/// sending anything, and an id MongoDB can't make.
/// </summary>
/// <remarks>
/// Every error is an <see cref="ArxException"/> with a stable code. The message names the index, never the value
/// (MongoDB's own text quotes it). A plain insert never replaces a document.
/// </remarks>
public sealed class Basic3_CatchAnError : ISample
{
public string Name => "create-basics-3-catch-an-error";
public string Title => "Catch the errors a create meets";
public async Task RunAsync(SampleContext dbContext)
{
await Scenario1_ADuplicateAsync(dbContext.Service);
await Scenario2_ADateThatMeansNotSetAsync(dbContext.Service);
await Scenario3_AGeneratedNumberAsync(dbContext.ConnectionString);
}
/// <summary>1. A second account with the same number: ARX3001, with the index's name.</summary>
private static async Task Scenario1_ADuplicateAsync(ArxMongoDbService service)
{
Step("1. A duplicate");
var account = new ArxEntity("account");
account["name"] = "Proseware, Inc.";
account["account_number"] = "ACC-M301";
await service.CreateAsync(account);
var again = new ArxEntity("account");
again["name"] = "Proseware copy";
again["account_number"] = "ACC-M301";
try
{
await service.CreateAsync(again);
}
catch (ArxDatabaseException exception) when (exception.Code == ArxErrorCode.UniqueViolation)
{
ShowError(exception);
Show($"index {exception.ConstraintName}, MongoDB error {exception.NativeCode}");
}
/* ===========================================================
* [DIRECT QUERY CHECK]
* -----------------------------------------------------------
* db.account.countDocuments({ account_number: "ACC-M301" })
* -- Expect: 1.
* ===========================================================
*/
}
/// <summary>2. default(DateTime) means "not set": ARX1013 before anything is sent.</summary>
private static async Task Scenario2_ADateThatMeansNotSetAsync(ArxMongoDbService service)
{
Step("2. A date that means \"not set\"");
var account = new ArxEntity("account");
account["account_number"] = "ACC-M302";
account["created_on"] = default(DateTime);
try
{
await service.CreateAsync(account);
}
catch (ArxValidationException exception) when (exception.Code == ArxErrorCode.InvalidDateTimeValue)
{
ShowError(exception);
}
/* ===========================================================
* [DIRECT QUERY CHECK]
* -----------------------------------------------------------
* db.account.find({ account_number: "ACC-M302" })
* -- Expect: no documents.
* ===========================================================
*/
}
/// <summary>3. A generated number id: MongoDB has no sequences, so ARX8001; use an ObjectId or a Guid.</summary>
private static async Task Scenario3_AGeneratedNumberAsync(string connectionString)
{
Step("3. A generated number id");
var schema = ArxSchemaCatalog.Create()
.Table("activity_log", ArxIdColumn.Generated<long>("log_id"))
.Build();
var service = new ArxMongoDbService(connectionString, schema);
var entry = new ArxEntity("activity_log");
entry["action"] = "signed in";
try
{
await service.CreateAsync(entry);
}
catch (ArxNotSupportedException exception)
{
ShowError(exception);
}
/* ===========================================================
* [DIRECT QUERY CHECK]
* -----------------------------------------------------------
* db.activity_log.find({ action: "signed in" })
* -- Expect: no documents.
* ===========================================================
*/
}
}
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. A duplicate
caught ArxDatabaseException: ARX3001 UniqueViolation: Create on 'account': a record with the same value in unique index 'account_number_1' already exists.
index account_number_1, MongoDB error 11000
> 2. A date that means "not set"
caught ArxValidationException: ARX1013 InvalidDateTimeValue: Attribute 'created_on' on 'account' is default(DateTime) (DateTime.MinValue), which means "not set". Set a real value or null.
> 3. A generated number id
caught ArxNotSupportedException: ARX8001 NotSupported: Table 'activity_log' declares id 'log_id' as a generated Int64. MongoDB has no sequences: ARX makes ObjectId (ArxIdColumn.Generated<string>) and Guid (ArxIdColumn.Generated<Guid>) ids, or give the id yourself and declare it ArxIdColumn.Supplied<Int64>.