简体   繁体   中英

Cosmos DB - CreateDocumentQuery not Deserializing Abstract Type

I am trying to get the Cosmos DB .NET SDK v1.19.1 to automatically deserialize objects into the correct type using Json.net serialization settings. This seems to work fine except when querying for documents. Take the following code for example:

    public abstract class Shape
    {
        public int Area { get; set; }
    }

    public class Square : Shape { }
    public class Triangle : Shape { }

    public class Entity
    {
        public string Id { get; set; }
        public string Name { get; set; }
        public Shape Shape { get; set; }
    }

    static void Main(string[] args)
    {
        JsonConvert.DefaultSettings = () => new JsonSerializerSettings()
        {
            ContractResolver = new CamelCasePropertyNamesContractResolver(),
            TypeNameHandling = TypeNameHandling.Auto
        };
        var database = "db";
        var collection = "coll";
        var client = new DocumentClient(new Uri("https://docdburi.documents.azure.com:443/"), "supersecretkey", JsonConvert.DefaultSettings());

        var entity = new Entity() { Id = "testid", Name = "John Doe", Shape = new Square() { Area = 5 } };
        var doc = client.CreateDocumentAsync(UriFactory.CreateDocumentCollectionUri(database, collection), entity).Result;
        entity = client.ReadDocumentAsync<Entity>(UriFactory.CreateDocumentUri(database, collection, entity.Id)).Result;

        // all good so far ... next line throws serialization exception on abstract shape type
        var result = client.CreateDocumentQuery<Entity>(UriFactory.CreateDocumentCollectionUri(database, collection), new FeedOptions() { MaxItemCount = 1 })
            .Where(x => x.Id == entity.Id).AsDocumentQuery()
            .ExecuteNextAsync<Entity>().Result;

The document gets created in Cosmos DB with the $type attribute on the shape as expected and retrieving the document works fine. It is only if I try and query for the document where an exception is thrown. Any ideas on how to get this to work?

Any other suggestions on how to best approach abstract types? I have a fairly deep object graph with several layers of abstract types.

This helper usually works for me:

public abstract class SerializableObject<T>
{    
    public static T FromJObject(JObject jObject) => 
         Parse($"{jObject}");

    public static T Parse(string json) =>
        JsonConvert.DeserializeObject<T>(json, 
            new JsonSerializerSettings
            {
                TypeNameHandling = TypeNameHandling.Objects
            });

    public JObject ToJObject() => JObject.Parse(ToJson());

    public string ToJson() =>
        JsonConvert.SerializeObject(this, Formatting.Indented, 
            new JsonSerializerSettings
            {
                TypeNameHandling = TypeNameHandling.Objects
            });
}

Now inherit your Entity class:

public class Entity : SerializableObject<Entity>
{
    public string Id { get; set; }
    public string Name { get; set; }
    public Shape Shape { get; set; }
}

and try querying it somehow using JObject :

var result = client.CreateDocumentQuery<JObject>(
    UriFactory.CreateDocumentCollectionUri(database, collection), 
    new FeedOptions() { MaxItemCount = 1 })
        .Where(x => x.Id == entity.Id)
        .AsEnumerable()
        .Select(Entity.FromJObject)
        .FirstOrDefault();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM