MongoFramework icon indicating copy to clipboard operation
MongoFramework copied to clipboard

Deserializing to an Interface

Open dthk-cogmatix opened this issue 2 years ago • 5 comments

I'm knocking my head around for something I think should work, but it's not.

I have a class with a list of interfaces (events).

public myclass { List<IEvents> Events; }

Mongoframework can serialize the events and store them properly with a discriminator/type.

However, I can't get it to deserialize back to MyClass.

I've tried BsonDiscriminator/BsonKnownType/RuntimeTypeDiscovery with no luck.

Is this possible with an interface or only base class?

dthk-cogmatix avatar Aug 28 '21 02:08 dthk-cogmatix

Hey @dthk-cogmatix - sorry that this isn't working properly - can you share more of the class and property? If it is something like:

public class MyClass
{
    public List<SomeOtherObject> Events { get; set; }
}

That should work without any issues. Are you getting any errors etc either or just that the property isn't set?

Turnerj avatar Aug 28 '21 03:08 Turnerj

Thanks for the quick response. The example you shared definitely works - so long as SomeOtherObject is an object and not an interface.

public class MyClass
{
        private List<INotification> _domainEvents;

//I don't think this matters - but sharing since it is in my implementation
        public List<INotification> DomainEvents {
            get
            {
                return _domainEvents;
            }
            set
            {
                _domainEvents = value;
            }
         }
}

public interface INotification { }

public class ConcreteDomainEvent1 : INotification
{
        public int EntityId { get; private set; }

        public ConcreteDomainEvent1(int entityId)
        {
            EntityId = entityId;
        }
}
public class ConcreteDomainEvent2 : INotification
{
        public int EntityId { get; private set; }

        public ConcreteDomainEvent2(int entityId)
        {
            EntityId = entityId;
        }
}

I don't have issues serializing MyClass. It will create a document in MongoDB no problem, including if there are instances of ConcreteDomainEvent1 or ConcreteDomainEvent2. When storing the document to the database, I get the appropriate discriminator value _t of "ConcreteDomainEvent1" or "ConcreteDomainEvent2." However, when I go to deserialize this object, if there is any value in DomainEvents, it will throw an exception not recognizing the discriminator value. There is where I've manually added the Discriminator to the ConcreteDomainEvent classes to see if that helps, but nothing.

I'm actually a bit confused when it can't recognize the discriminator during deserialization if it added the discriminator during serializing...

dthk-cogmatix avatar Aug 28 '21 12:08 dthk-cogmatix

Hmmm, I can't seem to recreate the issue you're hitting. This is the test case I was trying with:

[TestClass]
public class InterfacesOnProperties : TestBase
{
	interface IExampleInterface
	{
		public string InterfaceDefinedProperty { get; }
	}

	class ExampleImplOne : IExampleInterface
	{
		public string ImplOneProperty { get; }
		public string InterfaceDefinedProperty { get; }

		public ExampleImplOne(string implOneProperty, string interfaceDefinedProperty)
		{
			ImplOneProperty = implOneProperty;
			InterfaceDefinedProperty = interfaceDefinedProperty;
		}
	}

	class ExampleImplTwo : IExampleInterface
	{
		public string ImplTwoProperty { get; set; }
		public string InterfaceDefinedProperty { get; set; }
	}

	class ListOfInterfacesModel
	{
		public string Id { get; set; }
		public List<IExampleInterface> Items { get; set; }
	}

	[TestMethod]
	public void ListOfInterfaces()
	{
		var connection = TestConfiguration.GetConnection();
		var context = new MongoDbContext(connection);
		var dbSet = new MongoDbSet<ListOfInterfacesModel>(context)
		{
			new ListOfInterfacesModel
			{
				Items = new List<IExampleInterface>
				{
					new ExampleImplOne("ImplOne", "ImplOne"),
					new ExampleImplTwo
					{
						ImplTwoProperty = "ImplTwo",
						InterfaceDefinedProperty = "ImplTwo"
					}
				}
			}
		};

		context.SaveChanges();

		var data = dbSet.ToArray();
		Assert.AreEqual(1, data.Length);
		Assert.AreEqual("ImplOne", data[0].Items[0].InterfaceDefinedProperty);
		Assert.AreEqual("ImplOne", (data[0].Items[0] as ExampleImplOne).ImplOneProperty);
		Assert.AreEqual("ImplTwo", data[0].Items[1].InterfaceDefinedProperty);
		Assert.AreEqual("ImplTwo", (data[0].Items[1] as ExampleImplTwo).ImplTwoProperty);
	}
}

That said, what you described sounds similar to an issue I've seen before with MongoDB (see this Jira issue: https://jira.mongodb.org/browse/CSHARP-1907 ). I don't know if they have made any more progress on that issue though.

Turnerj avatar Aug 29 '21 05:08 Turnerj

CSHARP-1907 is EXACTLY the problem and what is happening. Reading through that, I can see your experience with it as well ;)

Apologies for thinking this was a MongoFramework issue, which is why I somewhat I approached this as: "Should this work?"

This also explains why I couldn't find anything significant with discriminators looking through the framework: it's in the driver.

My naive question: Can you overcome this limitation and behavior within the framework? I was looking at TypeDiscoverySerializer and specifically:


						if (!assembly.IsDynamic)
						{
							foreach (var type in assembly.GetTypes())
							{
								if (type.Name == name && expectedAssignableType.IsAssignableFrom(type))
								{
									AssignableTypes.Add(type);
									return type;
								}
							}
						}

It seems like you're handling the convention names and which serializer here. The driver is incorrectly providing the convention name (in that exact format listed: Namespace.Something.Class, Namespace.Something), but could it just be handled here? I'm sure it's more complicated than this...

dthk-cogmatix avatar Aug 29 '21 14:08 dthk-cogmatix

CSHARP-1907 is EXACTLY the problem and what is happening. Reading through that, I can see your experience with it as well ;)

Yep - your issue was familiar as I tried to do something similar a few years back for a project of mine. It was quite an annoying experience!

Apologies for thinking this was a MongoFramework issue, which is why I somewhat I approached this as: "Should this work?"

This also explains why I couldn't find anything significant with discriminators looking through the framework: it's in the driver.

No need to apologise - it could have been an issue with MongoFramework but yeah in this case the issue is the driver.

My naive question: Can you overcome this limitation and behavior within the framework? I was looking at TypeDiscoverySerializer and specifically:


						if (!assembly.IsDynamic)
						{
							foreach (var type in assembly.GetTypes())
							{
								if (type.Name == name && expectedAssignableType.IsAssignableFrom(type))
								{
									AssignableTypes.Add(type);
									return type;
								}
							}
						}

It seems like you're handling the convention names and which serializer here. The driver is incorrectly providing the convention name (in that exact format listed: Namespace.Something.Class, Namespace.Something), but could it just be handled here? I'm sure it's more complicated than this...

It might be possible, yeah. I'd have to do a bit of an investigation to see how I could do it but in theory, it might work. Can't give a solid time frame on when I can get around to that unfortunately - sorry!

Turnerj avatar Aug 30 '21 02:08 Turnerj