Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Asymmetric handling of DataContract with "Id" DataMember #427

Closed
steevcoco opened this issue Jan 31, 2018 · 4 comments
Closed

Asymmetric handling of DataContract with "Id" DataMember #427

steevcoco opened this issue Jan 31, 2018 · 4 comments
Assignees

Comments

@steevcoco
Copy link

steevcoco commented Jan 31, 2018

Hello ... I am completely new to DocumentDb (and want free advice Lol) -- I am just running simple tests.

If I push an object that is DataContract and defines a DataMember named "Id" --- which I set myself. Then when reading back the object, AND then casting through dynamic to my own type, then that DataMember is set from the database "id" --- which doesn't seem symmetric at least.

I don't know much about the bit of magic that allows me to cast through dynamic back to my object (I'd like to learn more about the round trip from DataContract) ... But the DataMember 'Id" wasn't used in the Db, so it's not stable to get it back that way.

... Edit 2:
[At one point, I thought perhaps it might be intended in some way ... You get back the Db-generated id. (I now see the disableAutomaticIdGeneration argument.) However, the round trip is not stable --- there is an "Id" (capital) going in, but a different one comes out; and the Db is fussy about the specific lowercase "id" DataMember name in other places ... so it does seem odd.]

[And for the record, I think it is far too lax not to have very concrete coupling to DataContract here.]

This test displays the behavior (the final Cast is the significant bit):

using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.Serialization;
using System.Threading.Tasks;
using Ad.LicensedUser.Core.Catalog;
using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using Microsoft.Extensions.Configuration;


namespace ConsoleApp1
{
    [DataContract]
    internal class MyProduct
    {
        [DataMember]
        public string Id { get; set; }

        [DataMember]
        public string Name { get; set; }

        public override string ToString()
            => $"{GetType() .Name}['{Name}' My Id: {Id}]";
    }


    [SuppressMessage("ReSharper", "LocalizableElement")]
    internal class TestAddAdProduct
            : IDisposable
    {
        private readonly DocumentClient documentClient;
        private readonly string databaseName;
        private readonly string collectionName;


        internal TestAddAdProduct(IConfiguration configuration)
        {
            databaseName = configuration["DatabaseName"];
            collectionName = configuration["CollectionName"];
            documentClient
                    = new DocumentClient(
                            new Uri(configuration["EndPointUrl"], UriKind.Absolute),
                            configuration["AuthorizationKey"]);
        }


        public async Task Run()
        {
            Uri uri = UriFactory.CreateDocumentCollectionUri(databaseName, collectionName);
            Console.WriteLine("Upsert document ...");
            MyProduct myProduct
                    = new MyProduct
                    {
                        Id = "AAAAAAAA-AAAA-4930-A733-89AF7ED5CE2F",
                        Name = "My Name"
                    };
            ResourceResponse<Document> response = await documentClient.UpsertDocumentAsync(uri, myProduct);
            Console.WriteLine(response.Resource);

            Console.WriteLine("Read document ...");
            uri = UriFactory.CreateDocumentUri(databaseName, collectionName, response.Resource.Id);
            response = await documentClient.ReadDocumentAsync(uri);
            Console.WriteLine(response.Resource);
            Console.WriteLine("Cast ...");
            try {
                Console.WriteLine((MyProduct)(dynamic)response.Resource);
            } catch (Exception exception) {
                Console.WriteLine(exception);
            }
        }


        public void Dispose()
            => documentClient.Dispose();
    }
}

The output is:

Upsert document ...
{
  "Id": "AAAAAAAA-AAAA-4930-A733-89AF7ED5CE2F",
  "Name": "My Name",
  "id": "0f88465e-5fe4-4334-bbef-0e3d405c4cac",
  "_rid": "UKxzANg4PgAMAAAAAAAAAA==",
  "_self": "dbs/UKxzAA==/colls/UKxzANg4PgA=/docs/UKxzANg4PgAMAAAAAAAAAA==/",
  "_etag": "\"00004800-0000-0000-0000-5a715b780000\"",
  "_attachments": "attachments/",
  "_ts": 1517378424
}
Read document ...
{
  "Id": "AAAAAAAA-AAAA-4930-A733-89AF7ED5CE2F",
  "Name": "My Name",
  "id": "0f88465e-5fe4-4334-bbef-0e3d405c4cac",
  "_rid": "UKxzANg4PgAMAAAAAAAAAA==",
  "_self": "dbs/UKxzAA==/colls/UKxzANg4PgA=/docs/UKxzANg4PgAMAAAAAAAAAA==/",
  "_etag": "\"00004800-0000-0000-0000-5a715b780000\"",
  "_attachments": "attachments/",
  "_ts": 1517378424
}
Cast ...
MyProduct['My Name' My Id: 0f88465e-5fe4-4334-bbef-0e3d405c4cac]
@kirankumarkolli
Copy link
Member

Yup this is an issue will name collision. This is an issues with Newtonsoft json deserialzier JamesNK/Newtonsoft.Json#815

Implementation here actually delegated the conversion to Newtonsoft json through Object.ToObject().

One alternative is to use different NamingStrategy which will avoid the conflicting names.

@steevcoco
Copy link
Author

Wow! Thanks ... I'm really hoping that there is not too much lean away from DataContract and concretely coupled structure ... In fact, just the opposite ...

I will certainly comment there.

@steevcoco
Copy link
Author

steevcoco commented Feb 4, 2018

I thought I should post a comment here because I made a comment over on the NewtonSoft list ...

It might not be the most critical possible viewpoint, but I HAVE to say that it's a bad experience to have it linger that "there is a case insensitivity that might mean X_Y_Z in your model". That's a headache.

DataContract has been a very rock solid --- AND obvious. Now there is a new headache about the abstraction between POCO (!) objects and a representation. I'm still amazed that I could get an asymmetric result on what seems to be the first possible activity to make against Cosmos; and I am also still uneasy about some layer of transforms, converters, or handlers or something to get things right.

Also PS: Cosmos is GREAT!

@kirankumarkolli kirankumarkolli self-assigned this Feb 7, 2018
@kirankumarkolli
Copy link
Member

@steevcoco work-around is to use different NamingStrategy to avoid the conflicting names.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants