Cloning an Entity with Mongo Driver — Digitteck
Cloning an Entity with Mongo Driver
dotnet·19 November 2019·2 min read

Cloning an Entity with Mongo Driver

The MongoDB C# driver's BSON serializer can handle entity classes that Newtonsoft JSON cannot deserialize without a custom JsonConverter — non-parameterless constructors and private setters included. That makes it an easy, correct deep-clone mechanism when you are already using the driver.

The Entities

Two derived classes — both use constructors with parameters that do not match the property names (done intentionally to show the serializer's capability), and all setters are private:

csharp
// Base entity with a Clone method stub
public class Entity<T> where T : Entity<T>
{
    public T Clone()
    {
        // To be defined
    }
}

// Both derived classes use non-parameterless constructors and private setters
public class Card : Entity<Card>
{
    public int Value { get; private set; }

    public Card(int v) => Value = v;
}

public class Deck : Entity<Deck>
{
    public List<Card> Cards { get; private set; }

    private Deck(Card[] c) => Cards = c.ToList() ?? new List<Card>();

    internal static Deck Create(params Card[] cards) => new Deck(cards);
}

Visual Studio will suggest removing the private setters since the properties are only set in the constructor, but the BSON deserialization process needs them to assign values during rehydration — keep them.

Using Newtonsoft on these classes fails silently — the deserialized object has default values because JSON.NET cannot find a matching parameterless constructor or inject through the private setters without a custom converter.

BsonSerializer Clone

Serialize the instance to a MemoryStream using BsonBinaryWriter, then deserialize the bytes back into a fresh instance of T:

csharp
// Clone using BsonSerializer — handles non-parameterless constructors and private setters
public class Entity<T> where T : Entity<T>
{
    public T Clone()
    {
        byte[] serialized;

        using (var stream = new MemoryStream())
        using (var writer = new BsonBinaryWriter(stream))
        {
            BsonSerializer.Serialize(writer, typeof(T), this);
            stream.Seek(0, SeekOrigin.Begin);
            serialized = stream.ToArray();
        }

        return BsonSerializer.Deserialize<T>(serialized);
    }
}

// Works even with:
//   - Non-parameterless constructors (BsonSerializer matches by convention or attribute)
//   - Private setters (BSON bypasses accessibility via reflection)
//   - Nested complex types

var original = Deck.Create(new Card(1), new Card(2), new Card(3));
Deck copy    = original.Clone();

// copy is a deep clone — different object references, same values

Tags

.NETC#MongoDBSerialization
digitteck

© 2026 Digitteck