DocumentQueryException: Query expression is invalid - azure-cosmosdb

I need to query a collection in cosmosdb.
My entity is:
public class Tenant
{
public string Id { get; set; }
public string ClientId { get; set; }
public string ClientSecret { get; set; }
public string TenantDomainUrl { get; set; }
public bool Active { get; set; }
public string SiteCollectionTestUrl { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
And my controller action is:
[HttpGet]
[Route("api/Tenant/GetActiveTenant")]
public Tenant GetActiveTenant()
{
var tenantStore = CosmosStoreFactory.CreateForEntity<Tenant>();
return tenantStore.Query().Where(x => x.Active == true).FirstOrDefault();
}
However when trying to use this endpoint, I get this error
DocumentQueryException: Query expression is invalid, expression
https://cosmosdb-app-centralus.documents.azure.com/dbs/ToDoList/colls/tenants.Where(x
=> (x.Active == True)).FirstOrDefault() is unsupported. Supported expressions are 'Queryable.Where', 'Queryable.Select' &
'Queryable.SelectMany'
emphasized text
I am using cosmonaut nuget package.
The only document I have in the collection:
{
"ClientId": "aaaaaaaa-4817-447d-9969-e81df29c813d",
"ClientSecret": "aaaaaaaaaaaaaaaaaa/esrQib6r7FAGd0=",
"TenantDomainUrl": "abc.onmicrosoft.com",
"SiteCollectionTestUrl": "https://abc.sharepoint.com/sites/Site1",
"Active": true,
"id": "d501acc6-6b63-4f0f-9782-1473af469b56",
"_rid": "kUZJAOPekgAEAAAAAAAAAA==",
"_self": "dbs/kUZJAA==/colls/kUZJAOPekgA=/docs/kUZJAOPekgAEAAAAAAAAAA==/",
"_etag": "\"00002602-0000-0000-0000-5b69fe790000\"",
"_attachments": "attachments/",
"_ts": 1533673081
}

As Cosmonaut's ReadMe page states, you should be using the Async method extensions for Cosmonaut because they will go properly though the SDK's async flow.
For example in your case, you should change your code to await tenantStore.Query().Where(x => x.Active == true).FirstOrDefaultAsync();
PS: You should also consider adding the [JsonAttribute("id")] attribute to your Id property. Even though it's not needed, it is recommended especially if you want to do querying based on the Id.

Related

Working with restricted string field values in .NET Core WebAPI 3.1

I'm working on a webapi project using .netcore.
I have a model with the following properties:
public class Criterial {
[Required]
public string Field { get; set; }
[Required]
public Operator Operator { get; set; }
[Required]
public string Value { get; set; }
public bool Result { get; set; }
}
public enum Operator {
greater_than,
equal_to,
lower_than
}
I'm trying to use enum to restrict the values that the Operator propertie can receive, but when I make a POST request to the API I got the following scenario:
POST Request Body:
"criterials": [
{
"field": "amount",
"operator": "greater_than",
"value": "50"
}
]
Response from the API:
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"traceId": "|7e53377-444fa4a723ac655c.",
"errors": {
"$.criterials[0].operator": [
"The JSON value could not be converted to LeagueOfFateApi.Models.Operator. Path: $.criterials[0].operator | LineNumber: 5 | BytePositionInLine: 26."
]
}
}
Searching about the issue on the internet I found the [JsonConverter(typeof(JsonStringEnumConverter))] Data Annotation.
So I added it to my code and the issue was "solved":
[Required]
[JsonConverter(typeof(JsonStringEnumConverter))]
public Operator Operator { get; set; }
New response from the API:
"criterials": [
{
"field": "amount",
"operator": "greater_than",
"value": "50",
"result": false
}
]
The problem is: in my MongoDB collection a new document was saved with the int value 0 of the enums, and not the string value "greater_than":
"Criterials" : [
{
"Field" : "amount",
"Operator" : 0,
"Value" : "50",
"Result" : false
}
]
Besides, another problem is that the "criterial" field can receive any int value with no restrictions.
Is there any other practical way to restrict a string's options without using enums? Or is there anything I can add to this solution using enums?
Thank you very much for your attention and your time!
According to your description, I suggest you could write custom set and get method for the Operator property.
You could set the Operator's type is string and use Enum.IsDefined to check the Operator value is enum Operator or not.
More details, you could refer to below codes:
public class Criterial
{
[Required]
public string Field { get; set; }
private string _Operator;
[Required]
public string Operator {
get {
return this._Operator;
}
set {
if (Enum.IsDefined(typeof(Operator), value))
{
this._Operator = value;
}
else
{
this._Operator = "Error you used wrong string";
}
}
}
[Required]
public string Value { get; set; }
public bool Result { get; set; }
}
public enum Operator
{
greater_than,
equal_to,
lower_than
}
Result:

Modeling mongodb subobjects in ASP.NET MVC application

I am running into issues after adding a sub-object to my mongo documents. The query no longer returns results, even though I've added an object to my model to store the new sub-object.
I believe the issue is in adding the class for the sub-object to the object model. I can't seem to find any references anywhere online, so perhaps I'm searching for the wrong thing?
Mongo elements look as so:
{
_id: [id],
Name: "Paul",
Phone1: {
Name: "Work",
Number: "15551234567"
},
Phone2: {
Name: "Work",
Number: "15551234567"
}
}
In C# my model looks as so:
public class PersonModel {
[BsonId]
public ObjectId _Id { get; set; }
public string Name { get; set; }
public Phone Phone1 { get; set; }
public Phone Phone2 { get; set; }
}
public class Phone {
public string Name { get; set; }
public string Number { get; set; }
}
My query looks as so:
public async Task<List<PersonModel>> GetPerson(string name)
{
var people = new List<PersonModel>();
var allDocuments = await PersonCollection.FindAsync(
ds => ds.Name == name);
await allDocuments.ForEachAsync(doc => people.Add(doc));
return people;
}
Any references to a working example would be appreciated.
Thank you for looking.
The above implementation is correct. After many hours of trouble shooting it turned out I didn't have the datapoint in my database that I was querying against. Unbelievable.
If anyone else is struggling, I also found this guide that confirmed I was dealing with the subobject correctly: https://www.codementor.io/pmbanugo/working-with-mongodb-in-net-1-basics-g4frivcvz

Using JSON.Net to parse a property of an array

I have a JSON response that I would like to parse using JSON.NET. I have done this with single values before but never when the response could contain an object that consist of an array as the errors property does below.
{
"code": "InvalidObject",
"message": "payment object is invalid",
"errors": [
{
"code": "AccountingApi",
"message": "Paid amount cannot be greater than the amount of the invoice.",
"resource": "payment",
"field": "amount"
},
{
"code": "AccountingApi",
"message": "Payment has not been verified",
"resource": "payment",
"field": "verification"
}
]
}
I would like to extract the error messages into a List. How do I specify that I want to grab the message property in the errors collection?
List<string> errorMessages = parsedJson["errors"].ToList<string>();
You could use
class Error
{
public string code { get; set; }
public string message { get; set; }
public string resource { get; set; }
public string field { get; set; }
}
class Some
{
public string code { get; set; }
public string message { get; set; }
public List<Error> errors { get; set; }
}
Then (Probably you'll send your json string as param )
List<string> parse()
{
var s = new StringBuilder();
s.Append("{");
s.Append(" \"code\": \"InvalidObject\",");
s.Append("\"message\": \"payment object is invalid\",");
s.Append("\"errors\": [");
s.Append("{");
s.Append("\"code\": \"AccountingApi\",");
s.Append("\"message\": \"Paid amount cannot be greater than the amount of the invoice.\",");
s.Append("\"resource\": \"payment\",");
s.Append("\"field\": \"amount\"");
s.Append("},");
s.Append("{");
s.Append("\"code\": \"AccountingApi\",");
s.Append("\"message\": \"Payment has not been verified\",");
s.Append("\"resource\": \"payment\",");
s.Append("\"field\": \"verification\" ");
s.Append("}");
s.Append("]");
s.Append("}");
var json = s.ToString();
var obj = JsonConvert.DeserializeObject<Some>(json);
return obj.errors.Select(x => x.message).ToList();
}

Parsing device listing from Urban Airship with JSON.Net

For the life of me, I can't figure out how to parse the collection of device_tokens out of this using JSON.Net. I can parse out the top level collection fine, but am bombing on parsing out the device tokens in any way shape or form. Anyone have any ideas?
{
"next_page": "https://go.urbanairship.com/api/device_tokens/?start=<MY_TOKEN>&limit=2",
"device_tokens_count": 87,
"device_tokens": [
{
"device_token": "<MY_TOKEN>",
"active": false,
"alias": null,
"tags": []
},
{
"device_token": "<MY_TOKEN>",
"active": true,
"alias": null,
"tags": ["tag1", "tag2"]
}
],
"active_device_tokens_count": 37
}
Heres how you can do it using Json.NET
First create a class to represent a single device_token:
public class DeviceToken
{
public string device_token { get; set; }
public bool active { get; set; }
public object alias { get; set; }
public List<object> tags { get; set; }
}
Then using the JsonConvert class you can deserialize the json device_token array to a list of DeviceToken objects.
string json = "{\"next_page\": \"https://go.urbanairship.com/api/device_tokens/?start=07AAFE44CD82C2F4E3FBAB8962A95B95F90A54857FB8532A155DE3510B481C13&limit=2\",\"device_tokens_count\": 87,\"device_tokens\": [{\"device_token\": \"0101F9929660BAD9FFF31A0B5FA32620FA988507DFFA52BD6C1C1F4783EDA2DB\",\"active\": false,\"alias\": null,\"tags\": []},{\"device_token\": \"07AAFE44CD82C2F4E3FBAB8962A95B95F90A54857FB8532A155DE3510B481C13\",\"active\": true,\"alias\": null,\"tags\": [\"tag1\", \"tag2\"] }],\"active_device_tokens_count\": 37}";
JObject obj = JObject.Parse(json);
var deviceTokens = JsonConvert.DeserializeObject<List<DeviceToken>>(obj["device_tokens"].ToString());

Custom impromptuobject for json.net deserialization

I was playing around with impromptu interface over a jobject and ran into the following issue
https://code.google.com/p/impromptu-interface/issues/detail?id=17
The issue is marked as 'Won't fix' and in the comments the author says that it could be fixed by implementing a custom impromptuobject.
Anyone have a sample of such an implementation? Or know another solution to this problem?
So the problem is that JArray has GetEnumerator() defined as interface-only, which makes the method no longer duck callable by the DLR. So below I've overriden the trygetmember to check if the result is a JArray's and convert it to a JEnumerable that implements GetEnumerator() in a dlr invokable way.
public class NonRecursiveJArrayConversionDictionary : ImpromptuDictionary{
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if(base.TryGetMember(binder, out result)){
if(result is JArray){
result = ((JArray)result).AsJEnumerable();
}
return true;
}
result = null;
return false;
}
}
However, this will only work for json structures that don't have arrays more then one property deep. You'll either have modify the above to recursively check anytime anything is returned maybe with a proxy, or modify the dictionary indexer's set to check and convert when deserialized instead.
Update: Json.net verion >= 5.0.4.16101 and ImpromptuInterface >= 6.1.4 will work out of the box.
void Main()
{
ICustomer customer = Impromptu.ActLike(JObject.Parse(#"
{
Id: 1,
Name:'Test',
Location:'Somewhere',
Employees: [
{ Id:1, EmployerId:39421, Name:'Joe' },
{ Id:2, EmployerId:39421, Name:'Jane' },
]
}
"));
foreach(var employee in customer.Employees){
employee.Id.Dump();
employee.Name.Dump();
}
}
public interface ICustomer
{
int Id { get; set; }
string Name { get; set; }
string Location { get; set; }
IList<IEmployee> Employees { get; }
}
public interface IEmployee
{
int Id { get; set; }
int EmployerId { get; set; }
string Name { get; set; }
}

Resources