Last active
February 6, 2023 13:07
-
-
Save JamisonWhite/cc0eae9e0ebfe8e62ec3 to your computer and use it in GitHub Desktop.
C# MongoDb custom field and value discriminator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <summary> | |
/// Map a resource "keyType" to polymorphic classes. | |
/// </summary> | |
/// <remarks> | |
/// Followed example here: | |
/// http://pastebin.com/9UweEKBe | |
/// </remarks> | |
public class CustomFieldDiscriminatorConvention : IDiscriminatorConvention | |
{ | |
private readonly string elementName; | |
private readonly Type defaultType; | |
private readonly Dictionary<string, Type> typeMap; | |
public CustomFieldDiscriminatorConvention(string elementName, Type defaultType, Dictionary<string, Type> typeMap) | |
{ | |
this.elementName = elementName; | |
this.defaultType = defaultType; | |
this.typeMap = typeMap; | |
} | |
/// <summary> | |
/// Element Name | |
/// </summary> | |
public string ElementName | |
{ | |
get { return elementName; } | |
} | |
/// <summary> | |
/// Return the type that matches the string | |
/// </summary> | |
/// <param name="bsonReader"></param> | |
/// <param name="nominalType"></param> | |
/// <returns></returns> | |
public Type GetActualType(MongoDB.Bson.IO.BsonReader bsonReader, Type nominalType) | |
{ | |
Type type = defaultType; | |
var bookmark = bsonReader.GetBookmark(); | |
bsonReader.ReadStartDocument(); | |
if (bsonReader.FindElement(ElementName)) | |
{ | |
var value = bsonReader.ReadString(); | |
if (typeMap.ContainsKey(value.ToLower())) | |
type = typeMap[value]; | |
} | |
bsonReader.ReturnToBookmark(bookmark); | |
return type; | |
} | |
public BsonValue GetDiscriminator(Type nominalType, Type actualType) | |
{ | |
return actualType.Name; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var dis = new CustomFieldDiscriminatorConvention( | |
"keyType", | |
typeof(Resource), | |
new Dictionary<string, Type>(3) | |
{ | |
{"domain", typeof (DomainResource)}, | |
{"ip", typeof (IpResource)}, | |
{"uri", typeof (UriResource)} | |
} | |
); | |
BsonSerializer.RegisterDiscriminatorConvention(typeof(Resource), dis); |
Midway through implementing this, I found out that you can just do BsonSerializer.RegisterDiscriminatorConvention(typeof(YourType), new ScalarDiscriminatorConvention("NameOfYourField"));
Which seems to work fine for me
@razzeee That makes sense, but I couldn't find a way to provide a default value for documents saved to the database before the discriminators were implemented using ScalarDiscriminatorConvention.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks for this! I applied the same code in my solution, but then when writing integration tests i faced with interesting problem:
If i write a document to DB and then read it in the same db context - this code doesn't work as expected. After a day struggling with this and debugging driver sources i found out that the following happens:
BUT, when you read this data in same context - for some reason BsonReader still contains both lines for "keyType" - i can clearly see this. Which obviousely lead to unexpected result in here:
Just because at 1st place BsonReader finds a line "keyType" == actualType.Name.
I've fixed it with the following change:
In this case discriminator is not serialized. I hope this will help someone.