[CSHARP-94] Structure support Created: 08/Nov/10  Updated: 20/Nov/20  Resolved: 20/Nov/20

Status: Closed
Project: C# Driver
Component/s: Feature Request
Affects Version/s: None
Fix Version/s: None

Type: New Feature Priority: Minor - P4
Reporter: Valera Kolupaev Assignee: Unassigned
Resolution: Done Votes: 14
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified
Environment:

Win7 x64, latest C# Driver trunk



 Description   

I have noticed that latest C# Driver has some failing tests, for structure support.
I have tried to fix it by myself, but I hit some framework limitations.
So, here they are, to save your's time:
1. IL/Expression Setters inside BsonMemberMap are not working for structs, because structs are passed by value (and not by ref)
2. I have tried to switch setter syntax from Action<object, object> to custom delegate that looks like Action<ref object, object>, but I came across FW limitations

  • Dynamic method (used for IL code) does not support not REF, nor OUT modifiers
  • I haven't found how to use LINQ.Expressions builder, to support REF or OUT modifiers
  • There is a way, to use Func<object, object, object> modifiers, that returns modified struct each time, but IMO it would be painful for GC


 Comments   
Comment by Jeffrey Yemin [ 20/Nov/20 ]

For anyone still watching this issue:  the driver does now support immutable structures and mutable structures that have a constructor annotated with [BsonConstructor].  As this covers the most common use cases for structures, we're going to close this issue.

If anyone needs further support for structures, please open a new Jira issue.

Comment by Jay Bennett [ 26/Jun/14 ]

Any updates on this?

Comment by Justin Caldicott [ 27/Jan/14 ]

That would be fine for my use-case, we already have constructors like that. I guess as long as the exception message you get when you try to serialize structs mentions the new convention, then it's still discoverable.

Thanks for the prompt attention!

Comment by Craig Wilson [ 27/Jan/14 ]

Hey Justin,
Thanks for the feedback. I said immutability, but that's not exactly true. What I meant to convey is that all values that are persisted to MongoDB would need a parameter in the constructor. In fact, it would be possible to have all your values be mutable as long as there was a constructor argument for each of properties you want to persist. In your case, simply keeping a dirty flag around is fine because you wouldn't persist your dirty flag to MongoDB.

Hope that clears it up... I'll work on a convention to map the properties and constructor for structs automatically. I'm not sure if we'll make it part of the default convention pack or require you to register it yourself. Making it part of the default conventions could break some people relying on the fact that we don't map structs.

Thanks,
Craig

Comment by Justin Caldicott [ 27/Jan/14 ]

Thanks for the quick response Craig. I've managed to work around it by creating a custom serializer for our structs. It's based on something I saw in a Google forum: https://gist.github.com/bluebirdtech/1d94a88680594406909c

There are three downsides to this that I see:

  • I have to maintain this code going forward (granted it's quite small)
  • I'm not able to make use of the conventions that we've applied via the ConventionRegistry. I have to explicitly create the same behaviour separately for structs. This means that the code is specific to our use-case and not reusable.
  • I have to add [BsonSerializer(typeof(StructBsonSerializer))] to structs. Possible to forget, but mostly the issue is that I'd like to be able to keep the project that has our business objects free of the reference to using MongoDB.Bson. This is going to be used on mobile devices, where it will be serializing using NewtonSoft.Json. (Though I'm interested to look into using MongoDB.Bson for consistency across our system.)

Regarding your approach, being able to create a convention would be great. I'm not so keen on forcing immutability. I agree that in most cases it's a best practise, but it's not always possible. For example, with our on device data store we're able to mark changed objects, then either commit or discard the changes. Discarding the changes requires to update the existing object.

Comment by Craig Wilson [ 27/Jan/14 ]

I need to confer with my colleague, but a little testing shows that the work done in CSHARP-476 enables this to work with some minor tweaks. There are a couple of caveats, although if you are using structs correctly, shouldn't be an issue.

  1. As is always the case, structs are meant to be immutable. As such, you'll only be able to use structs that take all their values as construct arguments. Setters won't work.
  2. You'll either need to manually map all structs or create a convention to do so. We could probably be convinced to enable that convention by default for structs. It would basically map all read-only properties where a correlating constructor parameter matches the property name (possibly differing in case).

Let me know if this sounds satisfactory and I'll begin working on a solution, hopefully in our next release.

Comment by Justin Caldicott [ 26/Jan/14 ]

.NET structs aren't supported at all with Mongo? This does seem like core functionality to be missing. Does anyone have a workaround that doesn't involve making the struct a class?

Comment by Christina [ 10/Sep/13 ]

I can't believe this is marked as minor priority... There is no easy workaround and this is important functionality that you are missing!

Comment by Jason Fry [ 01/Jul/13 ]

I agree with Michael and Gustavo - this is critical functionality that is missing from this driver. I too have classes with nested structs that will all need to be converted to classes. Please prioritize this bug!

Comment by Gustavo Gondim [ 28/Jan/13 ]

I was using MongoDB applied to a DDD project, that used the "Value Objects" as structs inside classes. After the exception, it confused me and forced me to believe the problem was with interface deserialization (which is another problem in the driver). After questions in StackOverflow and after implement custom serialziers for the interfaces, I have found this issue.

I rolled back the project to inicial state (without custom serializers) and changed the struct types to classes. It worked. But, in some cases I REALLY will need the types be passed as a value, not as a reference.

This is issue should be priorized, and considered a major implementation. I am trying to help you with this and another to issues (interfaces deserialization and LinQ projections).

Please, someone give some attention to this issue.

Comment by Michael Bertelsen [ 11/Jan/13 ]

I do not understand that this is considered as a Minor bug, and is not resolved?
I have many lists with structs that I would like to serialize. What about vectors, complex numbers etc?

Comment by Travis Laborde [ 09/Feb/12 ]

In the meantime, it would perhaps be easy/quick to add something preventing these from even going INTO the database. Since you won't be able to get them back out again without modifying your classes. Sort of going with the "fail fast" mentality. Let the developer know as soon as possible that this isn't going to work out...

Comment by Robert Stam [ 08/Nov/10 ]

Thanks for the information. We probably are not going to implement support for structs until after the 1.0 release.

Comment by Valera Kolupaev [ 08/Nov/10 ]

Here is a recipe, for ref parameters in LINQ Expressions:
http://efreedom.com/Question/1-1272454/Generate-Dynamic-Method-Set-Field-Struct-Instead-Using-Reflection

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