Problem
Here's the deal, if you have a dynamic
property then RavenDB will always deserialize it as a RavenJObject
. For example:
public class User {
public dynamic Data = new ExpandoObject();
}
...
var user = new User();
user.Data.SomethingNew1 = "foo";
This looks harmless & works fine when you create your user. But when you Load user, RavenDB doesn't know what type you want for dynamic
so it uses RavenJObject
. You can't dynamically create properties (expando style) with RavenJObject
so this fails:
var user = session.Find<User>(...);
user.Data.SomethingNew2 = "foo"; //compiles, but throws
My Solution
Use an ExpandoObject
and explicitly define its type in the property that is serialized. That lets RavenDB (or JSON i guess) know what type you're expecting & it doesn't have to guess RavenJObject
. Then, to keep your syntactical magic, wrap the property with a dynamic accessor.
public class User {
public ExpandoObject _Data = new ExpandoObject();
public dynamic Data {
get { return _Data; }
}
}
There are ways to make the expando object private & to create a setter for Data
, but you get the idea.
More Problems
Update: Unfortunately this solution exposes more problems. Say you store a list of strings in your dynamic data:
user.Data.Keys = new List<String>{"a","b","c"};
After you serialize/deserialize, JSON/Raven again doesn't know what type you are expecting. So if you try this (see below) then it compiles but you get a runtime exception Cannot implicitly convert type 'Raven.Abstractions.Linq.DynamicList' to 'System.Collections.Generic.List':
List<string> keys = user.Data.Keys;