DocumentDb DateTimeOffset string - azure-cosmosdb

I have documents with string fields that contain DateTimeOffset values. For example:
public class DateTimePocoDocument : Resource
{
public string startTime { get; set; }
public string endTime { get; set; }
}
Imagine a string value being set as follows.
myDateTimePocoDocument.startTime = DateTimeOffset.UtcNow.ToString("o");
Documents are created in DocumentDb using the .NET DocumentClient.
public async Task<Document> InsertAsync(TDocument data)
{
return await Client.CreateDocumentAsync(Collection.SelfLink, data);
}
Viewing the document in DocumentDb shows the string fields properly stored.
[
{
"startTime": "2016-10-01T13:00:00.0000000+00:00",
"endTime": "2016-10-01T14:35:17.215947+00:00",
"id": "2b6e53e1-2099-41f8-8405-f9daf750cfc8",
"_rid": "6qt9AJ0xkgDkAwAAAAAAAA==",
"_self": "dbs/6qt9AA==/colls/6qt9AJ0xkgA=/docs/6qt9AJ0xkgDkAwAAAAAAAA==/",
"_etag": "\"3d00c96d-0000-0000-0000-586e67b40000\"",
"_attachments": "attachments/",
"_ts": 1483630513
}
]
I do this because I want to manually handle all serialization and deserialization of DateTimeOffset values. I need precision and predictability as data moves across controllers, gets serialized to Azure App client, gets serialized into SQLite and back, etc, etc.
When I execute a query as follows:
Client.CreateDocumentQuery<TDocument>(Collection.DocumentsLink,
query,
new FeedOptions { EnableScanInQuery = true, EnableCrossPartitionQuery = false });
The documents return the startTime string above as "10/01/2016 13:00:00". I've created a custom JsonConverter and attached it to the property to see what was being assigned to the string property. The converter confirms that a DateTime is being assigned to the string field. The DocumentDb client is choosing to treat the string as a date value because it looks like a date. Unfortunately that results in a changed string value in this case. Why is it performing that translation on my string and how can I prevent that without having to customize the string?
Thanks

Looks like Azure Cosmos DB doesn't support DateTimeOffset type yet.
The request for such support is already filed and can be tracked here.

Related

Need help understanding API and LINQ

I'm trying to set up an API for a system I'm working on, but the LINQ seems to not grab the parameters.
A bit of background: During covid I've been working with a local business owner to develop an info system for his business. So far, everything has been kept in the browser, but now we want to create a small windows form application the users can download instead of using the browser. The application will be much smaller in scope than the full site, but I don't want the SQL connection in the form.
So I guess my first question is, am I being overly cautious at not wanting the SQL connector in the client and wanting them to connect to the database, via an API, or is it safe enough to add the connection and calls directly in the application (I know how to do this, it's the API part I can't figure out). I'm thinking about it from a security point of view - would the users be able to find the connection and potentially do harm to my database by having it straight in the application or is it safe there?
If using API calls is the proper way to go, that leads me to my second question. How do I configure it properly?
This is my table (I've been following the Microsoft ToDoItems tutorials):
CREATE TABLE [dbo].[TodoItems] (
[Id] [int] identity(1,1) NOT NULL,
[Name] [nvarchar](50) NULL,
[IsComplete] [bit] NULL,
[Secret] [nvarchar](10) NULL
) ON [PRIMARY]
GO
My test form has a single button, which when pressed calls this method:
static async Task RunAsync()
{
// Update port # in the following line.
client.BaseAddress = new Uri("https://localhost:7217/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
try
{
// Create a new product
TodoItem product = new TodoItem
{
Name = "Gizmo",
IsComplete = false,
Secret = "false"
};
var url = await CreateProductAsync(product);
Console.WriteLine($"Created at {url}");
// Get the product
product = await GetProductAsync(url.PathAndQuery);
ShowProduct(product);
// Update the product
Console.WriteLine("Updating IsCompleted...");
product.IsComplete = true;
await UpdateProductAsync(product);
// Get the updated product
product = await GetProductAsync(url.PathAndQuery);
ShowProduct(product);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
My ToDoItem class looks like this:
public class TodoItem
{
public long Id { get; set; }
public string Name { get; set; }
public bool IsComplete { get; set; }
public string Secret { get; set; }
}
My first issue is creating the ToDoItem. This method should do the trick:
static async Task<Uri> CreateProductAsync(TodoItem product)
{
HttpResponseMessage response = await client.PostAsJsonAsync(
"api/todoitems", product);
response.EnsureSuccessStatusCode();
// return URI of the created resource.
return response.Headers.Location;
}
However, when I run the method my API logs this error and nothing is posted to the database:
Executed DbCommand (46ms) [Parameters=[#p0='?' (DbType = Boolean), #p1='?' (Size = 4000), #p2='?' (Size = 4000)], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
INSERT INTO [TodoItems] ([IsComplete], [Name], [Secret])
VALUES (#p0, #p1, #p2);
SELECT [Id]
FROM [TodoItems]
WHERE ##ROWCOUNT = 1 AND [Id] = scope_identity();
The way I read this, and I might be wrong, the method CreateProductAsync (which gets a product with the values "Gizmo", false and "false") simply doesn't transfer the values to the API.
For reference, my API ToDoContext class look like this:
public class TodoContext : DbContext
{
public TodoContext(DbContextOptions<TodoContext> options)
: base(options)
{
}
public DbSet<TodoItem> TodoItems { get; set; } = null!;
}
Do I need to add something to this class? I'm wholly unfamiliar with both API and LINQ, but I did figure out that changing the table name to ToDoItems made the connection for me on its own.

ParitionKey extracted from document doesn't match the one specified in the header - C#

I'm new to CosmosDB and trying to figure out what's going on. I am using Microsoft.Azure.Cosmos NuGet package for development.
This is the line that creates my container:
Container = await database.CreateContainerIfNotExistsAsync(Program.ContainerId, "/id", 400)
This is my class:
public class REProperty
{
public const string PartitionKey = "id";
[JsonProperty(PropertyName = "id")]
public string Id { get; set; }
public string Number { get; set; }
public User Owner { get; set; }
And finally the code where I try to create a new document:
ItemResponse<REProperty> Response = await Program.Container.CreateItemAsync<REProperty>(C, new PartitionKey(REProperty.PartitionKey));
I am using the exact same PartitionKey everywhere yet I am still getting this error every time. Am I missing anything obvious?
Error message:
(Message: {"Errors":["PartitionKey extracted from document doesn't match the one specified in the header"]
You've defined the collection to use the id property as the partition key. The value given as id will then be the partition key used. However, you are specifying this:
ItemResponse<REProperty> Response = await Program.Container.CreateItemAsync<REProperty>(C, new PartitionKey(REProperty.PartitionKey));
This will always set the value "id" as the partition key, which is not correct. The actual value is different from document to document. So either you set it like this: new PartitionKey(C.Id) or you just omit the partition key part in the item creation - I think it should be enough to just have the property set, but give it a try to check it.

CosmosDb not using the ContractResolver provided when generating select queries

I have a project where I'm using CosmosDb (SQL API) as my database. It's a .Net Core project and I'm using the latest stable NuGet packages.
The document client is created as follows and use a custom contract resolver.
new DocumentClient(new Uri(settings.DatabaseUri), settings.DatabaseKey,
new JsonSerializerSettings
{
ContractResolver = new PrivateSetterCamelCasePropertyNamesContractResolver(),
Converters = new List<JsonConverter>
{
new EmailJsonConverter()
}
});
I have a collection called EmailAccount
public class EmailAccount : Entity
{
public string Email { get; private set; }
public string DisplayName { get; private set; }
public EmailAccount(DDD.Core.ValueObjects.Email email,
string displayName)
{
Email = email ?? throw new ArgumentNullException(nameof(email));
DisplayName = string.IsNullOrWhiteSpace(displayName) ? throw new ArgumentNullException(nameof(displayName)) : displayName;
}
}
All the properties are converted into camel-case when serialized which all works fine. But the problem is when I try to filter the documents. The SQL query that's generated looks something like this when I try to filter by the Email.
SELECT * FROM root WHERE (root["Email"] = "amila#iagto.com")
The problem is with the case of the property (Email). The property in the database is email but the query generator doesn't seem to be adhering to the ContractResolver provided and generates the above sql query which doesn't return any result.
If I put [JsonProperty("email")] above the Email property, the query is generated properly. Anyway to get the query generated properly without using attributes in the Entity class?
Any help much appreciated.
You need to set the JsonSerializerSettings at the CreateDocumentQuery level for the LINQ to SQL to pick it up.
This property was added in the SDK on 2.0.0+ versions.

How can the Identity.GetUserId() be made to return a Guid instead of a string?

I am using ASP.Net Identity 2 but soon hope to change to Identity 3 when it becomes more stable (anyone know when that might be?). Here's a sample of my code:
content.ModifiedBy = User.Identity.GetUserId();
The Content table stores ModifedBy as a UNIQUEIDENTIFIER and the Content object assigns a datatype of Guid to ModifiedBy
When I look at the signature for GetUserId() it returns a string.
So how can I take the users UserId and put it into the ModifiedBy which is a Guid?
A guid can take a string as a constructor
content.ModifiedBy = new Guid( User.Identity.GetUserId());
You can use Guid.Parse() or Guid.TryParse()
content.ModifiedBy = Guid.Parse(User.Identity.GetUserId());
https://msdn.microsoft.com/en-us/library/system.guid.parse%28v=vs.110%29.aspx
As I was using same method over and over I added the following extension:
public static class ExtensionMethods
{
public static Guid ToGuid(this string value)
{
Guid result= Guid.Empty;
Guid.TryParse(value, out result);
return result;
}
}
and then I used this:
User.Identity.GetUserId().ToGuid()

Read multiple json objects from a json file [duplicate]

This question already has an answer here:
What is the correct way to use JSON.NET to parse stream of JSON objects?
(1 answer)
Closed 5 years ago.
I have a file with multiple json objects.How can i read this in .net
I need the below string as two json obejcts
"{hello:'there'} {goodbye:'you',Thanks:'you'}"
#Anu: first of all, I do not think the string you specified "{hello:'there'} {goodbye:'you',Thanks:'you'}" is the proper Json string.
It should be like this one to deserialize properly
{"Arrival":{"Hello":"there"},"Departure":{"GoodBye":"you","Thanks":"you"}}.
Take a look at the example below for proper structure:
//You need to install the Json.Net and use it as shown below:
using Newtonsoft.Json;
//The object for Serializing and Deserializing:
public class Greeting
{
public Arrival Arrival { get; set; }
public Departure Departure { get; set; }
}
public class Arrival
{
public string Hello { get; set; }
}
public class Departure
{
public string GoodBye { get; set; }
public string Thanks { get; set; }
}
//Populate the Getting Object C# way
var greeting = new Greeting
{
Arrival = new Arrival { Hello = "there" },
Departure = new Departure { GoodBye = "you", Thanks = "you" }
};
//Serialize the C# object to Json
var jsonString = JsonConvert.SerializeObject(new
{
Arrival = new
{
greeting.Arrival.Hello
},
Departure = new { greeting.Departure.GoodBye, greeting.Departure.Thanks }
});
//Which produce this Json String: {"Arrival":{"Hello":"there"},"Departure":{"GoodBye":"you","Thanks":"you"}}
//Convert the Json string back to C# object - Deserialize the object
var greeting1 = (Greeting)JsonConvert.DeserializeObject(jsonString, typeof(Greeting));
Hope this helps and answered your question.
Good luck!
Dibu is right - your string is not a valid JSON object, so you can't deserialize it back to object(s) automatically.
You should either write your own parser (oops, don't do this) or force changing source input file to have one of:
Contain one valid JSON object (made of several separate objects), so you can deserialize it easily
Contain some separator(s) between individual objects, so you can split source string manually and deserialize objects one by one.
The best option is to change source file to contain one valid object, but I guess this was not your decision about such source file format...
There is a way. You need a JsonConverter to pass when you deserialize.
string json = #"{hello:'there'} {goodbye:'you',Thanks:'you'}";
IList<Greeting> greetings= new List<Greeting>();
JsonTextReader reader = new JsonTextReader(new StringReader(json));
// this is the important part, which makes the reader not throw exception
//when one json object is finished
reader.SupportMultipleContent = true;
while (true)
{
if (!reader.Read())
{
break;
}
JsonSerializer serializer = new JsonSerializer();
Greeting greeting= serializer.Deserialize<Greeting>(reader);
greetings.Add(greeting);
}

Resources