Reputation: 664
We are using Bson to serialize/deserialize on either side of our RabbitMq Rpc client server calls. We have a implemented our SimpleRpcClient/Server as suggested here:
However, our Subscription object is implemented as so:
public class SubscriberRequest<TKey> : SubscriberRequest
{
public TKey[] Keys { get; set; }
public SubscriberRequest(){}
public SubscriberRequest(IEnumerable<TKey> keys) : base()
{
this.Keys = keys.ToArray();
this.Hostname = Dns.GetHostName();
}
}
public class SubscriberRequest
{
public static SubscriberRequest Default = new SubscriberRequest() { Hostname = Dns.GetHostName() };
public string Hostname { get; set; }
}
Allowing us to send request response objects back and forth in a typed manor. This seems to work well, when our serializer is only responsible for dealing with one type of "Request" objects.
BsonClassMap.LookupClassMap(typeof(SubscriberRequest<MessageType1Request>));
However, when we try to use the serializer for multiple types of request objects, i cannot seem to set up the ClassMap enough to satisfy the deserializer.
BsonClassMap.LookupClassMap(typeof(SubscriberRequest<MessageType1Request>));
BsonClassMap.LookupClassMap(typeof(SubscriberRequest<MessageType2Request>));
I consistently get a MongoDB.Bson.BsonSerializationException: Ambiguous discriminator 'SubscriberRequest`1'
I have tried to explicitly tell the BsonClassMap how to handle this like so:
BsonClassMap.RegisterClassMap<SubscriberRequest<MessageType1Request>>(cm =>
{
cm.MapCreator(p => new SubscriberRequest<MessageType1Request>(p.Keys));
});
with no avail.
How can i properly satisfy the discriminator?
Upvotes: 3
Views: 3677
Reputation: 667
I found this and made a minor modification MongoDB C# driver type discriminators with generic class inheriting from non-generic base class
Setting the disciminator as suggested by @Bruce Nielsen is not very flexible
public class DiscriminatorConvention<T> : IDiscriminatorConvention
{
public string ElementName => "_t";
public Type GetActualType(IBsonReader bsonReader, Type nominalType)
{
if (!typeof(T).IsAssignableFrom(nominalType))
throw new Exception($"Cannot use DiscriminatorConvention<{typeof(T).Name}> for type " + nominalType);
var ret = nominalType;
var bookmark = bsonReader.GetBookmark();
bsonReader.ReadStartDocument();
if (bsonReader.FindElement(ElementName))
{
var value = bsonReader.ReadString();
ret = Type.GetType(value);
if (ret == null)
throw new Exception("Could not find type from " + value);
if (!typeof(T).IsAssignableFrom(ret) && !ret.IsSubclassOf(typeof(T)))
throw new Exception("type is not an IRestriction");
}
bsonReader.ReturnToBookmark(bookmark);
return ret;
}
public BsonValue GetDiscriminator(Type nominalType, Type actualType)
{
if (nominalType != typeof(Setting))
throw new Exception($"Cannot use {GetType().Name} for type " + nominalType);
return actualType.AssemblyQualifiedName; // ok to use since assembly version is ignored when deserialized
}
}
Upvotes: 1
Reputation: 1758
Just ran into this myself.
The solution is to override the discriminator for each generic type, so when serialized they get unique values.
For your case, something like this should work:
BsonClassMap.RegisterClassMap<SubscriberRequest<MessageType1Request>>(cm =>
{
cm.AutoMap();
cm.SetDiscriminator("SubscriberRequest`MessageType1Request");
};
BsonClassMap.RegisterClassMap<SubscriberRequest<MessageType2Request>>(cm =>
{
cm.AutoMap();
cm.SetDiscriminator("SubscriberRequest`MessageType2Request");
};
Upvotes: 3
Reputation: 146
I think you need something like this: Serialize Documents with the C# Driver - Specifying Known Types
Basically, you need to map all of your known types in order for the discriminator to determine what you want.
BsonClassMap.RegisterClassMap<SubscriberRequest<MessageType1Request>>();
BsonClassMap.RegisterClassMap<SubscriberRequest<MessageType2Request>>();
Upvotes: 0