Σε κάθε database το βασικό και πρακτικό ερώτημα είναι το πως διαβάζω και επεξεργάζομαι δεδομένα και αυτά παρουσιάζονται στο άρθρο αυτό.
Retrieving Data
Το πρώτο που ζητάει κάποιος από μια βάση δεδομένων είναι να μάθει πως θα αντλεί στοιχεία από αυτή.
Στην Cosmos DB υπάρχουν τρεις διαφορετικοί τρόποι να κάνουμε αυτό είναι με SQL, LINQ, Lambda.
Σε κάθε περίπτωση χρησιμοποιείται υπάρχουσα γνώση εφόσον φυσικά έχουμε ασχοληθεί με αυτά στο παρελθόν και αυτό βοηθάει ιδιαίτερα.
Δεν έχω την πρόθεση στο άρθρο αυτό να επαναλάβω ήδη υπάρχοντα πράγματα για αυτό σας προτείνω να δείτε το documentation που είναι εξαιρετικό και είναι στα παρακάτω links:
Αυτό που θέλω να τονίσω είναι ότι στις εφαρμογές μας μπορούμε να διαβάσουμε δεδομένα χρησιμοποιώντας διάφορες classes που υπάρχουν στο Microsoft.Azure.Documents.Client namespace.
Μια από τις βασικότερες class είναι η DocumentClient και μπορούμε να συνδεθούμε και να διαβάσουμε δεδομένα από την εφαρμογή μας όπως στα παρακάτω code examples
// Connect to Cosmos DB
string AccountKey = "<your account key>";
string EndpointURI = "https:// XXXXX.documents.azure.com:443";
client = new DocumentClient(new Uri(EndpointUrl), AccountKey);
// Simple query
var deviceQuery = this.client.CreateDocumentQuery<T>(myCollection, $"SELECT DISTINCT c.propname FROM c");
var deviceData = await Task.Run(() => deviceQuery.ToList());
// In-line result processing
string deviceQueryString = $"SELECT DISTINCT c.propname FROM {this.collection} c";
var deviceQuery = this.client.CreateDocumentQuery<T>(myCollection, $"SELECT DISTINCT c.propname FROM {this.collection} c").AsDocumentQuery();
while (deviceQuery.HasMoreResults)
{
var records = deviceQuery.ExecuteNextAsync().Result;
foreach (var record in records)
{
Console.WriteLine($"{record}\n");
}
}
// Using LINQ
string queryString = $"SELECT c.prop1, c.prop2, c.prop3 FROM T AS c";
SqlQuerySpec querySpec = new SqlQuerySpec()
{
QueryText = queryString
};
var query = this.client.CreateDocumentQuery<T>(myCollection, querySpec).AsEnumerable();
var docs = from doc in query
group doc by doc.prop1 into grp
select new mydata
{
…
};
var d = await Task.Run(() => docs.ToList());
Creating Documents
Μπορούμε να εισάγουμε νέα documents χρησιμοποιώντας την CreateDocumentAsync method της DocumentClient class αφού πρώτα έχουμε ένα POCO class serialized σε json για το document το οποίο το δίνουμε σαν URI στην αντίστοιχη παράμετρο της μεθόδου
MyPOCO p= new MyPOCO
{
Prop1 = …,
Prop2 = …,
…
PropN = …
};
Uri collectionUri = UriFactory.CreateDocumentCollectionUri(this.database, this.collection);
var response = await this.client.CreateDocumentAsync(collectionUri, p);
Deleting Documents
Με την DeleteDocumentAsync method της DocumentClient class διαγράφουμε documents όπως στο παρακάτω code sample
Console.WriteLine("Enter document ID");
string id = Console.ReadLine();
Uri docUri = UriFactory.CreateDocumentUri(this.database, this.collection, id);
var response = await this.client.DeleteDocumentAsync(docUri);
Modifying documents
Με την ReplaceDocumentAsync method της DocumentClient class κάνουμε αλλαγές στο documents όμως θα πρέπει να γνωρίζουμε δεν υπάρχει in-place update για αυτό θα πρέπει να διαβάσουμε όλο το document στον client να κάνουμε την αλλαγή σε αυτό και μετά να το κάνουμε όλο update στην βάση και εδώ πρέπει να λάβουμε σοβαρά το concurrency που θα επιλέξουμε ή έχουμε επιλέξει.
Η Cosmos DB έχει ETags τα οποία η κάθε εφαρμογή μπορεί να χρησιμοποιήσει ώστε να αποφευχθούν concurrency issues. Αυτό μπορεί να γίνει με την χρήση του AccessCondition object το οποίο το δίνω σαν request option στην ReplaceDocument Async για το ETag του document και αν στο replace αυτό έχει από αλλού αλλάξει τότε παίρνω λάθος όπως στο παρακάτω code sample.
Console.WriteLine("Enter document ID");
string id = Console.ReadLine();
Uri docUri = UriFactory.CreateDocumentUri(this.database, this.collection, id);
var documentResponse = await client.ReadDocumentAsync(docUri);
var doc = documentResponse.Resource;
Console.WriteLine(doc.ToString());
double T = doc.GetPropertyValue<double>("p");
doc.SetPropertyValue("p", T + 1000);
RequestOptions options = new RequestOptions
{
AccessCondition = new AccessCondition
{
Condition = doc.ETag,
Type = AccessConditionType.IfMatch
}
};
var response = await client.ReplaceDocumentAsync(doc.SelfLink, doc, options);
Error Handling during manipulations
Σε όλες τις παραπάνω διαδικασίες πρέπει πάντα στο τέλος να ελέγχω αν αυτές έχουν γίνει με επιτυχία ή όχι και για αυτό το λόγο όλα τα response object που δημιουργώ για αυτές επιστρέφουν την ResourceResponse class η οποία έχει το StatusCode property όπως στο code sample
Console.WriteLine($"Status code is {response.StatusCode}");
Όλες μπορείτε να τις επιστρεφόμενες τιμές μπορείτε να τις δείτε εδώ
Attachments
Όπως έχω αναφέρει σε παλαιότερο άρθρο μου αν έχω blobs ή document property που είναι πάνω από 2ΜΒ αποθηκεύω αυτό σαν document attachment.
Η DocumentClient class έχει τις εξής μεθόδους για κάνουμε κάτι τέτοιο
- CreateAttachmentAsync - create an attachment.
- DeleteAttachmentAsync - delete an attachment.
- ReplaceAttachmentAsync - replace an attachment.
- CreateAttachmentQuery - query the attachments associated with a document.
- ReadMediaAsync - read an attachment by its Id.
Code Samples
// Add Attachemnt
var response = await this.client.CreateDocumentAsync(collectionUri, doc);
//... Set up a stream to read the data to upload for the attachment
// Create the attachment
var attachmentResponse = await client.CreateAttachmentAsync(response.Resource.AttachmentsLink, stream,
new MediaOptions
{
ContentType = "application/jpeg",
Slug = Guid.NewGuid().ToString()
});
// Querying Attachments
foreach (var attachment in client.CreateAttachmentQuery(doc.SelfLink))
{
var data = await client.ReadMediaAsync(attachment.MediaLink);
StreamReader reader = new StreamReader(data.Media);
... Read the data for the attachment using the StreamReader
}
// Directly Read an Attachment
var attachLink = doc.AttachmentsLink + "{your attachmentid }/";
Attachment attachment = await client.ReadAttachmentAsync(attachLink);
//antonch