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

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

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

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


person Jan Ohlson    schedule 07.06.2011    source источник


Ответы (3)


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

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

Оператор Insert также можно было бы написать так:

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]);

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

person Robert Stam    schedule 08.06.2011
comment
Большое спасибо. Именно то, что я искал. - person Jan Ohlson; 08.06.2011
comment
Очень хороший ответ, единственный, который я нашел полным для десериализации неизвестных классов во время компиляции. - person Tok'; 07.09.2013

Я написал вспомогательный класс, улучшив отличный ответ Роберта Стэма и разрешив те же параметры, что и статический метод 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);

or

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

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

person Tok'    schedule 07.09.2013

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

person Andrew Orsich    schedule 07.06.2011
comment
Хм.. Я не понимаю, как это сделать без отражения с неизвестными типами. - person mnemosyn; 07.06.2011