Извлечение унаследованных экземпляров из MongoDB с помощью C#
Я использую официальный драйвер MongoDb C#.
Мой сценарий: я сохраняю объекты в MongoDb. Все объекты являются экземплярами классов, которые наследуются от одного и того же корневого класса. Во время разработки я не знаю всех классов, которые могут быть сохранены (т. е. они могут быть подключены) - поэтому мне нужно каким-то образом сообщить сериализатору/драйверу, как сопоставить классы с документами (дескриминаторами в документе).
У кого-нибудь есть идеи?
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#.
Загляните в документацию по сериализации драйверов здесь.