[CSHARP-3153] Add support for serialization of proxy models Created: 07/Jul/20  Updated: 31/Mar/22

Status: Backlog
Project: C# Driver
Component/s: Serialization
Affects Version/s: 3.0.0
Fix Version/s: None

Type: New Feature Priority: Major - P3
Reporter: Mirko Da Corte Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Backwards Compatibility: Fully Compatible

 Description   

I'm working on a project where we use proxy models pattern for intercept actions to models. It's a common practice for automate some operations, for example related to security read/write access, or for implement lazy loading of information in a transparent way. For the context, we are using Castle.Core library.

Currently it's evident that serialization system of drivers is not designed for handle proxy models, and I'm going to explain why.

Aside of relative simple to solve problems, there are others that needs hard workarounds.

On simple problems, we can report the need of explicit a

classMap.SetCreator(() => myProxyGenerator.CreateInstance(modelType));

at every classMap declaration. This is feasible, but a global class map configuration with something like

BsonClassMap.UseDefaultCreator(modelType => myProxyGenerator.CreateInstance(modelType));

could be better.
 

A much more difficult problem to solve is serialization. Let say that we have a classMap for User type. The relative dynamic type will be UserProxy (keeping name style of Castle.Core library).

When we try to serialize an User model, the BsonClassMapSerializer performs this action:

var actualType = value.GetType();
if (actualType == typeof(TClass))
{
    SerializeClass(context, args, value);
}
else
{
    var serializer = BsonSerializer.LookupSerializer(actualType);
    serializer.Serialize(context, args, value);
}

the declaration of typeof(TClass) is directly compared to actual type of value, where instead a "procy model tolerant" check would be preferable. So it could be changed with something like this:

var actualType = value.GetType();
if (proxyModelFacade.PurgeProxyType(actualType) == typeof(TClass))
{
    SerializeClass(context, args, value);
}
else
{
    var serializer = BsonSerializer.LookupSerializer(actualType);
    serializer.Serialize(context, args, value);
}

where proxyModelFacade.PurgeProxyType(typeof(UserProxy)) will return for example typeof(User).

This is a problem because this system will start a lookup of a new serializer, and where it will not be found, this will lead to find a BsonClassMap for proxy type, where an Automap() on a new classmap will lead to configuration errors (tests done still with Castle.Core library). So we have to register at least an adeguate class map for proxy type ahead, but issues are not ended.

Even registering correctly a class map, we still have issue that creating a document on a collection with base type User, the document will be crated with discriminator "_t": "UserProxy". This because a proxy model is actually a derived type to the original one. So we should register a DiscriminatorConvention whit the scope to ignore any proxy type, and handle them has their base types.

But issues are not ended, because we cannot register discriminator conventions for typeof(object), because on BsonSerializer.LookupDiscriminatorConvention(Type type) registration for typeof(object) are ignored! (WHY??)

// inherit the discriminator convention from the closest parent (that isn't object) that has one
// otherwise default to the standard hierarchical convention
Type parentType = type.GetTypeInfo().BaseType;
while (convention == null)
{
    if (parentType == typeof(object))
    {
        convention = StandardDiscriminatorConvention.Hierarchical;
        break;
    }
    if (__discriminatorConventions.TryGetValue(parentType, out convention))
    {
        break;
    }
    parentType = parentType.GetTypeInfo().BaseType;
}

So we have to choose a common base type, different than object, and use it for register the convention.

It's clear that this system has not been designed with use of proxies in mind. Please consider it for next major, it would be a great feature improvement.



 Comments   
Comment by Esha Bhargava [ 03/Aug/20 ]

mirko@etherna.io Thanks for the suggestion! This is not currently on our roadmap, but we are moving this to our backlog to see if there is more interest from the community on this!

Generated at Wed Feb 07 21:44:31 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.