How can I read a `uuid` from a column in a sqlite3 db using sqlite-net-pcl in Xamarin Forms - xamarin.forms

I have a database schema that I do not control (it's a sqlite3 file exported from a desktop application that I need to interoperate with), that contains UUIDs for some of the columns. I'm using sqlite-net-pcl in a Xamarin.Forms app, and I cannot work out how to successfully read these columns. Here's what I've tried:
using the sqlite3 command line, I've confirmed that the schema has type uuid for the relevant column, and using select count(distinct uuidcolumn) from mytable; I've confirmed that there are values for each row. (The column is nullable which is relevant for the code snippet below but in practice all the rows have non-null values)
I have this model object:
namespace brahms.Model
{
[Table("mytable")]
public class MyTable
{
[Column("uuidcolumn")]
public Guid UUIDColumn { get; }
[PrimaryKey, AutoIncrement, NotNull]
[Column("recordid")]
public int RecordID { get; set; }
}
}
if I fetch an object using database.Query<MyTable>() queries, UUIDColumn is always equal to Guid.Empty.
I tried switching the type in the class definition to Byte[]; it's always null.
I tried switching the type in the class definition to string; it's always null.
Same applies to the UInt16[] type (the GUID might be stored as a blob of 16-bit words, so I tried that type too)
How can I read the values in uuid-typed columns using sqlite-net-pcl?

I gave up on using the ORM features in sqlite-net-pcl and used this query:
db.executeScalar<byte[]>('select hex(uuidcolumn) from mytable where recordid=1');
What I get back is 72 bytes, which appear to represent the 36 ASCII characters in a string representation of a Guid (every so often one of the characters is 2D, which is - in the ASCII set). So I think that the backing store is a blob but one that's storing the text representation of the Guid, which is weird, but I'll be able to reconstruct the Guid from here.
Using this answer and getting that blob as a string, I ended up with this implementation:
public Guid GetUUIDColumn()
{
string dbRep = _database.ExecuteScalar<string>("select hex(uuidcolumn) from mytable where recordid = ?", RecordID);
if (dbRep == null || dbRep == string.Empty) return Guid.Empty;
var bytes = new byte[dbRep.Length / 2];
// each pair of bytes represents the ASCII code (in hexadecimal) for a character in the string representation of a Guid.
for (var i = 0; i < bytes.Length; i++)
{
bytes[i] = Convert.ToByte(dbRep.Substring(i * 2, 2), 16);
}
string asString = Encoding.ASCII.GetString(bytes);
return new Guid(asString);
}

Related

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.

Create a DATETIME column in SQLite FLutter database?

I create a TABLE with some columns and everything's ok until I tried to insert a new DATETIME column:
_onCreate(Database db, int version) async {
await db.
execute("CREATE TABLE $TABLE ($ID INTEGER PRIMARY KEY, $NAME TEXT, $COLOUR
TEXT, $BREED TEXT, $BIRTH DATETIME)");
}
My Model Class to store is:
class Pet {
int id;
String name;
DateTime birth;
String breed;
String colour;
Pet({this.id, this.name, this.birth, this.colour, this.breed});
}
In the controller, I tried to store a new instance of Pet, instantiating a new variable DateTime _date = new DateTime.now(); and saving all in
Pet newPet = Pet(
id: null,
name: _name,
colour: _colour,
breed: _breed,
birth: _date
);
But when I insert in the database I receive:
Unhandled Exception: Invalid argument: Instance of 'DateTime'
You are passing DateTime object directly.
Use class methods like:
https://api.dart.dev/stable/2.9.3/dart-core/DateTime/millisecondsSinceEpoch.html
or
https://api.dartlang.org/stable/2.4.0/dart-core/DateTime/toIso8601String.html
You need to pass String or int instead of an Object. The reason being, you must use supported SQLite data types to store in a SQLite table, find a list of supported data types here https://www.sqlite.org/datatype3.html
Also checkout:
https://pub.dev/packages/sqflite
DateTime is not a supported SQLite type. Personally I store them as int (millisSinceEpoch) or string (iso8601)
To retrieve your DateTime object again:
From Integer:
DateTime.fromMillisecondsSinceEpoch(yourValue);
From String:
DateTime.parse(yourValue);
or better way:
DateTime.tryParse(yourValue);

Unable to query by Timestamp in CosmosDB with Table API

I'm creating a query to include Cosmos entities from the last 30 days:
var filter = TableQuery.GenerateFilterConditionForDate(
"Timestamp",
QueryComparisons.GreaterThanOrEqual,
DateTimeOffset.Now.Date.AddDays(-30));
Next I create a query using this filter:
var query = new TableQuery<ResponseEntity>().Where(filter);
Next I execute the query:
var result = await table.ExecuteQuerySegmentedAsync(query, null);
However, for some reason, the result always contains zero (0) hits.
If I execute the query without any filter...
var query = new TableQuery<ResponseEntity>();
...I do get all entities.
Looking at the generated filter string, it looks OK to me (and identical to the one in the Azure portal when using the query builder for Cosmos):
Timestamp ge datetime'2018-09-15T22:00:00.0000000Z'
Is there any limitation on querying based on Timestamp?
Edit: Tried switching to the new Microsoft.Azure.Cosmos.Table NuGet package (currently in preview, version 0.9.1), but I'm still not getting any results when filtering by Timestamp.
Please refer to my working code.
code:
using Microsoft.Azure.CosmosDB.Table;
using Microsoft.Azure.Storage;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JayGongCosmosTable
{
class Program
{
static void Main(string[] args)
{
TableQuerySegment <ResponseEntity> resultE= QueryTableAsync("test").Result;
foreach(ResponseEntity re in resultE)
{
Console.WriteLine("Timestamp: "+re.Timestamp);
Console.WriteLine("------------------------------------------");
}
Console.WriteLine("execute done");
Console.ReadLine();
}
public static async Task<TableQuerySegment<ResponseEntity>> QueryTableAsync(string tableName)
{
CloudStorageAccount storageAccount = CreateStorageAccountFromConnectionString("***");
CloudTableClient tableClient = storageAccount.CreateCloudTableClient();
CloudTable table = tableClient.GetTableReference(tableName);
var filter = TableQuery.GenerateFilterConditionForDate(
"Timestamp",
QueryComparisons.GreaterThanOrEqual,
//QueryComparisons.LessThanOrEqual,
DateTimeOffset.Now.AddDays(-10).Date);
Console.WriteLine(filter);
var query = new TableQuery<ResponseEntity>().Where(filter);
var result = await table.ExecuteQuerySegmentedAsync(query, null);
return result;
}
}
class ResponseEntity : TableEntity
{
public string Name { get; set; }
public DateTimeOffset logtime { get; set; }
}
}
My data list as below without filter:
If I used Timestamp as filter, it works:
Another thing I'd like to mention is, please avoid timestamp interval query if possible. Such a query will result in a whole table scan in server side. If timestamp interval query is needed usually in your scenario, please consider choosing timestamp as your partition key or row key to optimize the query performance.
Just for summary, finally , the solution is uninstalling WindowsAzure.Storage and switch using statements to use types from Microsoft.Azure.CosmosDB.Table instead.

ASP.NET Core 2 IFormCollection returning StringValues

Recently the IFormCollection in the platform I'm building started returning values of the type Microsoft.Extensions.Primitives.StringValues. when it used to return strings.
The controllers were made with strings in mind and now that are a lot of forms that are not working.
Is there any explanation to this, or a way to revert it?
As far as I'm aware ASP.NET Core's IFormCollection has always been a collection of StringValues. The reason is simple: multiple values can be posted for any particular key, making it potentially impossible to set the value if the type was merely string. There is no way to "revert" this. Change your code accordingly.
Or, better yet, stop using IFormCollection. Bind to strongly-typed models. That's always the best way.
For others coming here also confused by seeing IFormCollection giving StringValues. You may be familiar with .NET Frameworks FormCollection class, which gives strings. The reason for the change is valid and explained by #Chris Pratt in his answer here.
To make IFormCollection and StringValues feel familiar again consider any of these simple extensions:
// Example: var name = collection["name"].FirstOrNull();
// Equal to (.NET Framework): var name = collection["name"];
public static string FirstOrNull(this StringValues values)
{
if (values.Count > 0)
{
return values[0];
}
return null;
}
// Example: var name = collection["name"].FirstOr("John Doe");
// Equal to (.NET Framework): var name = collection["name"] ?? "John Doe";
public static string FirstOr(this StringValues values, string fallback)
{
if (values.Count > 0)
{
return values[0];
}
return fallback;
}
// Example: var name = collection.ValueOrFallback("name", "John Doe");
// Equal to (.NET Framework): var name = collection["name"] ?? "John Doe";
public static string ValueOrFallback(this IFormCollection collection, string key, string fallback)
{
if (collection[key].Count > 0)
{
return collection[key][0];
}
return fallback;
}
Also consider the built-in TryGetValue:
if (collection.TryGetValue("name", out var name))
{
// at least one name did exist
}
Alt.
var name = collection.TryGetValue("name", out var names) ? names[0] : "John Doe";

Converting created document result to POCO

I have the following code that calls DocumentDB and creates a new Employee document. How do I then convert the result to Employee document again? Basically, I want to capture the created document and convert it to Employee object.
var result = await client.CreateDocumentAsync(collection.SelfLink, employee);
if(result.StatusCode == System.Net.HttpStatusCode.Created)
{
Employee emp = result.Resource; // How do I convert the result to Employee object here?
}
You can do a dynamic cast like this:
Employee emp = (dynamic)result.Resource;
I wrote an extension method to do this:
public static async Task<T> ReadAsAsync<T>(this Document d)
{
using (var ms = new MemoryStream())
using (var reader = new StreamReader(ms))
{
d.SaveTo(ms);
ms.Position = 0;
return JsonConvert.DeserializeObject<T>(await reader.ReadToEndAsync());
}
}
Then you can use it like
Document d;
var myObj = await d.ReadAsAsync<MyObject>();
(Copying over Andrew Davis's answer, from the DocumentDB MSDN forums, for the stackoverflow community)
The simplest way would be to have your Employee class inherit from Document, and then cast result.Resource to Employee. If you don't want to inherit from Document, you could also define an explicit cast between Document and Employee.
Having the Employee class inherit from Document should work out-of-the-box if the names of the members of your Employee class match the names of the corresponding properties of the JSON representation.
Defining your own type conversion gives you more control, and might look something like this:
public static explicit operator Employee(Document doc)
{
Employee emp = new Employee();
emp.Name = doc.GetPropertyValue<string>("employeeName");
emp.Number = doc.GetPropertyValue<int>("employeeNumber");
/* and so on, for all the properties of Employee */
return emp;
}
This would define an explicit cast from Document to Employee. You'll have to make sure the GetPropertyValue strings (and type arguments) match your JSON properties.
Here's a a synchronous extension method that doesn't silently miss properties like the (dynamic) cast method can. Uses recent .NET Core features Span and System.Text.Json for performance.
Usage:
Document doc; // source Document
MyType converted = doc.ConvertTo<MyType>();
Implementation:
public static T ConvertTo<T>(this Document item)
{
using var stream = new MemoryStream();
item.SaveTo(stream);
var bytes = new ReadOnlySpan<byte>(stream.GetBuffer()).Slice(0, (int)stream.Length);
return JsonSerializer.Deserialize<T>(bytes);
}

Resources