Azure / azure-cosmos-dotnet-v2

Contains samples and utilities relating to the Azure Cosmos DB .NET SDK
MIT License
579 stars 837 forks source link

Data type long values can be rounded #288

Open chriswill opened 7 years ago

chriswill commented 7 years ago

I am running the .Net Core version, 1.3.2. I'm running VS 2017 on Windows 10 (64 bit).

I am using some entities that have values with very large long values, and it seems that these are being rounded in an odd way and not stored in CosmosDB correctly.

I created a scaled down entity for testing that looks like this:

public class TestAccount
{
    [JsonProperty(PropertyName = "id")]
    public string Id { get; set; }

    [JsonProperty(PropertyName = "idLong")]
    public long IdLong { get; set; }      
}

I instantiated it and saved it like so:

TestAccount testAccount = new TestAccount();
testAccount.Id = "769308320146468865";
testAccount.IdLong = 769308320146468865;
Assert.IsTrue(testAccount.IdLong < long.MaxValue, "testAccount.Id < long.MaxValue");

await cosmosRepository.CreateDocumentAsync(testAccount);

And the persisted entity looks like this. Note the difference in the values.

{
  "id": "769308320146468865",
  "idLong": 769308320146468900
}

I was able to duplicate this in three scenarios: Running Release code in Azure App Service (web site set to 64 bit) Running under 64 bit IIS express Running under Resharper unit test runner

My active platform is set to x64 (not AnyCPU). Can you replicate this issue, or do you have any suggestions?

ghost commented 7 years ago

It could be serious issue for us too. I guess it uses double internally because of JavaScript etc. We'll try to reproduce later.

ghost commented 7 years ago

We tried to reproduce it and found, that data is serialized and stored correctly. The issue is only at portal, Document/Query Explorer view "rounds" them.

chriswill commented 7 years ago

You're correct. It's just a UI issue in the portal. Do you think that this may be fixed in the future?

chriswill commented 7 years ago

OK, I have something that's also interesting.

Assume we've created the same document as previously.

TestAccount testAccount = new TestAccount();
testAccount.Id = "769308320146468865";
testAccount.IdLong = 769308320146468865;

await cosmosRepository.CreateDocumentAsync(testAccount);

which will create this document:

{
  "id": "769308320146468865",
  "idLong": 769308320146468865
}

If we use a query to pull the document back by it's ID, yes we can verify that all is well and the idLong property has the expected value:

var document = await cosmosRepository.GetDocumentByIdAsync("769308320146468865");
long prop = document.GetPropertyValue<long>("idLong");
Assert.AreEqual(769308320146468865, prop);

This is probably what you did to check the persistence of the document.

Now, let's use a query to find it. We build a simple query using either a SqlQuerySpec or using a string to pull a document collection.

SqlQuerySpec query = new SqlQuerySpec("Select * from c where c.id = @docId", 
                new SqlParameterCollection{new SqlParameter("@docId", "769308320146468865") });

var documents = cosmosRepository.GetDocuments<TestAccount>(query);
Assert.AreEqual(769308320146468865, documents.First().IdLong, "SqlQuerySpec: Document Ids are not equal");

const string querytest = "select * from c where c.id = \"769308320146468865\"";
documents = cosmosRepository.GetDocuments<TestAccount>(querytest);
Assert.AreEqual(769308320146468865, documents.First().IdLong, "StringQuery: Document Ids are not equal");

In both queries above, the IdLong value of the document returned is 769308320146468864, which causes our test to fail (expected value 769308320146468865).

Just for completeness, here's the methods in the repository used above. They are trivial.

        public List<T> GetDocuments<T>(string query)
        {                                 
            return documentClient.CreateDocumentQuery<T>(UriFactory.CreateDocumentCollectionUri(database, collection), query)
                .AsEnumerable().ToList();            
        }

        public List<T> GetDocuments<T>(SqlQuerySpec query)
        {
            return documentClient.CreateDocumentQuery<T>(UriFactory.CreateDocumentCollectionUri(database, collection), query)
                .AsEnumerable().ToList();
        }

Can you see similar results? These are done in the Resharper unit test runner, and I haven't tested the exact test in other ways (IISExpress), but it matches what I've been seeing.

chriswill commented 7 years ago

This appears to be the same issue as #293.