Извлечение унаследованных экземпляров из MongoDB с помощью C#


Я использую официальный драйвер MongoDb C#.

Мой сценарий: я сохраняю объекты в MongoDb. Все объекты являются экземплярами классов, которые наследуются от одного и того же корневого класса. Во время разработки я не знаю всех классов, которые могут быть сохранены (т. е. они могут быть подключены) - поэтому мне нужно каким-то образом сообщить сериализатору/драйверу, как сопоставить классы с документами (дескриминаторами в документе).

У кого-нибудь есть идеи?

3 5

3 ответа:

Официальный драйвер C# будет записывать значение дискриминатора " _t " всякий раз, когда фактический тип объекта отличается от номинального типа. Так, например:

MyRootClass obj = new MyDerivedClass();
collection.Insert(obj);

Заявление о вставке также можно было бы написать:

collection.Insert<MyRootClass>(obj);

, но это легче, чтобы позволить компилятору вывести тип параметра.

Поскольку фактический тип obj отличается от номинального типа, будет записан дискриминатор "_t".

При чтении объекта вы должны будете убедиться, что MyDerivedClass был должным образом зарегистрирован:

BsonClassMap.RegisterClassMap<MyDerivedClass>();

Или сериализатор не распознает дискриминатор (это может показаться ограничением, но логично, что сериализатор может работать только с типами, о которых он знает).

Вы упомянули, что не знаете классы во время компиляции, поэтому приведенный выше регистрационный код должен вызываться динамически. Один из способов сделать это:

Type myDerivedClass; // your plugged-in class
var registerClassMapDefinition = typeof(BsonClassMap).GetMethod("RegisterClassMap", new Type[0]);
var registerClassMapInfo = registerClassMapDefinition.MakeGenericMethod(myDerivedClass);
registerClassMapInfo.Invoke(null, new object[0]);

Технически сериализация не использует отражение; она управляется метаданными. Отражение используется один раз для построения карты классов, но после этого карта классов используется непосредственно без отражения, и накладные расходы довольно низки.

Я написал вспомогательный класс, улучшающий отличный ответ Роберта СТЭМа и позволяющий использовать те же параметры, что и статическая BsonClassMap.RegisterClassMap<...>() метод.

public class MyBsonClassMap
{
    public static void RegisterClassMap(Type type)
    {
        Type bsonClassMapType = typeof(BsonClassMap<>).MakeGenericType(new Type[] { type });
        BsonClassMap bsonClassMap = (BsonClassMap)Activator.CreateInstance(bsonClassMapType);
        BsonClassMap.RegisterClassMap(bsonClassMap);
    }

    public static void RegisterClassMap(Type type, Action<BsonClassMap> classMapInitializer)
    {
        Type bsonClassMapType = typeof(BsonClassMap<>).MakeGenericType(new Type[] { type });
        BsonClassMap bsonClassMap = (BsonClassMap)Activator.CreateInstance(bsonClassMapType);
        classMapInitializer(bsonClassMap);
        BsonClassMap.RegisterClassMap(bsonClassMap);
    }
}

Теперь я могу зарегистрировать тип, который был неизвестен во время компиляции, почти с тем же синтаксисом, что и известный:

Type unknownType; // is the type that was unknown at compile time
MyBsonClassMap.RegisterClassMap(unknownType);

Или

MyBsonClassMap.RegisterClassMap(unknownType, cm =>
     cm.AutoMap());

Эти методы должны быть доступны в драйвере C#.

Загляните в документацию по сериализации драйверов здесь.