Wie man Properties beim Schreiben in MongoDB ignoriert

Beim Arbeiten mit MongoDB und dem offiziellen C# Driver bin ich auf ein Verhalten gestoßen, das im ersten Moment unerwartet war:

Properties, die während des Lesevorgangs aus anderen Quellen befüllt wurden (z. B. durch Aggregation), wurden beim Speichern des Dokuments wieder mit in die Datenbank geschrieben – obwohl sie ursprünglich nicht Bestandteil des gespeicherten Dokuments waren.

Das kann zu inkonsistenten oder redundanten Daten führen. Die Lösung: Eine eigene Convention in Kombination mit einem benutzerdefinierten Attribut, das explizit markiert, welche Properties nicht gespeichert werden sollen.


🛠️ 1. Das Attribut BsonIgnoreWhenSaving & die Convention

[AttributeUsage(AttributeTargets.Property)]
public class BsonIgnoreWhenSavingAttribute : Attribute
{
}

public class IgnoreWhenSavingConvention : ConventionBase, IMemberMapConvention
{
    public void Apply(BsonMemberMap memberMap)
    {
        if (memberMap.MemberInfo.GetCustomAttributes(typeof(BsonIgnoreWhenSavingAttribute), false).Any())
        {
            // verhindert die Serialisierung beim Schreiben
            memberMap.SetShouldSerializeMethod(_ => false);
        }
    }
}

🧩 2. Registrierung der Convention beim App Startup

Die Convention wird beim Start der Anwendung registriert – z. B. innerhalb deiner Infrastructure-Schicht:

private static void RegisterMongoDBConventions()
{
    ConventionRegistry.Register(
        nameof(CamelCaseElementNameConvention),
        new ConventionPack { new CamelCaseElementNameConvention() },
        _ => true
    );

    ConventionRegistry.Register(
        nameof(IgnoreWhenSavingConvention),
        new ConventionPack { new IgnoreWhenSavingConvention() },
        _ => true
    );
}

private static void AddMongoDB(this IServiceCollection services)
{
    services.AddSingleton<MongoDbService>();
    RegisterMongoDBConventions();
}

✅ 3. Anwendung des Attributs

Nun kann das Attribut auf beliebige Properties gesetzt werden, die beim Schreiben ignoriert werden sollen:

public class TransactionReport : BaseEntity
{
    [BsonRepresentation(BsonType.ObjectId)]
    public required string CustomerId { get; set; }

    [BsonIgnoreWhenSaving]
    public CustomerDetails? Customer { get; set; }

    [BsonRepresentation(BsonType.ObjectId)]
    public required string AccountId { get; set; }

    [BsonIgnoreWhenSaving]
    public AccountDetails? Account { get; set; }
}