DocumentDB - modelowanie danych
Chociaż bazy danych bez schematów, takie jak DocumentDB, bardzo ułatwiają wprowadzanie zmian w modelu danych, nadal powinieneś poświęcić trochę czasu na myślenie o swoich danych.
Masz wiele opcji. Oczywiście możesz po prostu pracować z grafami obiektów JSON, a nawet nieprzetworzonymi ciągami tekstu JSON, ale możesz także używać obiektów dynamicznych, które umożliwiają tworzenie powiązań z właściwościami w czasie wykonywania bez definiowania klasy w czasie kompilacji.
Możesz także pracować z rzeczywistymi obiektami C # lub encjami, jak są one nazywane, które mogą być klasami domeny biznesowej.
Relacje
Przyjrzyjmy się hierarchicznej strukturze dokumentu. Ma kilka właściwości najwyższego poziomu, takich jak wymagany id, a także lastName i isRegistered, ale ma również właściwości zagnieżdżone.
{
"id": "AndersenFamily",
"lastName": "Andersen",
"parents": [
{ "firstName": "Thomas", "relationship": "father" },
{ "firstName": "Mary Kay", "relationship": "mother" }
],
"children": [
{
"firstName": "Henriette Thaulow",
"gender": "female",
"grade": 5,
"pets": [ { "givenName": "Fluffy", "type": "Rabbit" } ]
}
],
"location": { "state": "WA", "county": "King", "city": "Seattle"},
"isRegistered": true
}
Na przykład właściwość parent jest dostarczana jako tablica JSON oznaczona nawiasami kwadratowymi.
Mamy również inną tablicę dla dzieci, mimo że w tym przykładzie jest tylko jedno dziecko w tablicy. W ten sposób modelujesz odpowiednik relacji jeden do wielu w dokumencie.
Po prostu używasz tablic, w których każdy element tablicy może być prostą wartością lub innym złożonym obiektem, a nawet inną tablicą.
Tak więc jedna rodzina może mieć wielu rodziców i wiele dzieci, a jeśli spojrzysz na obiekty podrzędne, mają one własność zwierzaka, która sama jest zagnieżdżoną tablicą dla relacji jeden do wielu między dziećmi a zwierzętami.
W przypadku właściwości lokalizacji łączymy w obiekt trzy powiązane właściwości - stan, hrabstwo i miasto.
Osadzanie obiektu w ten sposób, zamiast osadzania tablicy obiektów, jest podobne do relacji jeden do jednego między dwoma wierszami w oddzielnych tabelach w relacyjnej bazie danych.
Osadzanie danych
Rozpoczynając modelowanie danych w magazynie dokumentów, takim jak DocumentDB, spróbuj traktować encje jako samodzielne dokumenty reprezentowane w formacie JSON. Pracując z relacyjnymi bazami danych, zawsze normalizujemy dane.
Normalizacja danych zazwyczaj obejmuje przejęcie podmiotu, takiego jak klient, i rozbicie go na dyskretne fragmenty danych, takie jak dane kontaktowe i adresy.
Aby odczytać klienta ze wszystkimi jego danymi kontaktowymi i adresami, musisz użyć POŁĄCZEŃ, aby skutecznie agregować dane w czasie wykonywania.
Przyjrzyjmy się teraz, jak modelowalibyśmy te same dane jako samodzielną jednostkę w bazie danych dokumentów.
{
"id": "1",
"firstName": "Mark",
"lastName": "Upston",
"addresses": [
{
"line1": "232 Main Street",
"line2": "Unit 1",
"city": "Brooklyn",
"state": "NY",
"zip": 11229
}
],
"contactDetails": [
{"email": "[email protected]"},
{"phone": "+1 356 545-86455", "extension": 5555}
]
}
Jak widać, zdenormalizowaliśmy rekord klienta, w którym wszystkie informacje o kliencie są osadzone w jednym dokumencie JSON.
W NoSQL mamy darmowy schemat, więc możesz dodawać dane kontaktowe i adresy również w innym formacie. W NoSQL można pobrać rekord klienta z bazy danych w jednej operacji odczytu. Podobnie aktualizowanie rekordu jest również pojedynczą operacją zapisu.
Poniżej przedstawiono kroki tworzenia dokumentów za pomocą .Net SDK.
Step 1- Utwórz wystąpienie DocumentClient. Następnie zapytamy o bazę danych myfirstdb, a także o kolekcję MyCollection, którą przechowujemy w tej prywatnej kolekcji zmiennych, aby była dostępna w całej klasie.
private static async Task CreateDocumentClient() {
// Create a new instance of the DocumentClient
using (var client = new DocumentClient(new Uri(EndpointUrl), AuthorizationKey)) {
database = client.CreateDatabaseQuery("SELECT * FROM c WHERE c.id =
'myfirstdb'").AsEnumerable().First();
collection = client.CreateDocumentCollectionQuery(database.CollectionsLink,
"SELECT * FROM c WHERE c.id = 'MyCollection'").AsEnumerable().First();
await CreateDocuments(client);
}
}
Step 2 - Utwórz dokumenty w zadaniu CreateDocuments.
private async static Task CreateDocuments(DocumentClient client) {
Console.WriteLine();
Console.WriteLine("**** Create Documents ****");
Console.WriteLine();
dynamic document1Definition = new {
name = "New Customer 1", address = new {
addressType = "Main Office",
addressLine1 = "123 Main Street",
location = new {
city = "Brooklyn", stateProvinceName = "New York"
},
postalCode = "11229", countryRegionName = "United States"
},
};
Document document1 = await CreateDocument(client, document1Definition);
Console.WriteLine("Created document {0} from dynamic object", document1.Id);
Console.WriteLine();
}
Pierwszy dokument zostanie wygenerowany z tego dynamicznego obiektu. To może wyglądać jak JSON, ale oczywiście tak nie jest. To jest kod C # i tworzymy rzeczywisty obiekt .NET, ale nie ma definicji klasy. Zamiast tego właściwości są wywnioskowane ze sposobu inicjalizacji obiektu. Możesz również zauważyć, że nie dostarczyliśmy właściwości Id dla tego dokumentu.
Step 3 - Rzućmy teraz okiem na CreateDocument i wygląda jak ten sam wzorzec, który widzieliśmy przy tworzeniu baz danych i kolekcji.
private async static Task<Document> CreateDocument(DocumentClient client,
object documentObject) {
var result = await client.CreateDocumentAsync(collection.SelfLink, documentObject);
var document = result.Resource;
Console.WriteLine("Created new document: {0}\r\n{1}", document.Id, document);
return result;
}
Step 4- Tym razem wywołujemy CreateDocumentAsync, określając SelfLink kolekcji, do której chcemy dodać dokument. Otrzymujemy odpowiedź z właściwością zasobu, która w tym przypadku reprezentuje nowy dokument z jego właściwościami wygenerowanymi przez system.
W kolejnym zadaniu CreateDocuments utworzyliśmy trzy dokumenty.
W pierwszym dokumencie obiekt Document jest zdefiniowaną klasą w SDK, która dziedziczy po zasobie, a więc ma wszystkie typowe właściwości zasobów, ale zawiera również właściwości dynamiczne, które definiują sam dokument bez schematu.
private async static Task CreateDocuments(DocumentClient client) {
Console.WriteLine();
Console.WriteLine("**** Create Documents ****");
Console.WriteLine();
dynamic document1Definition = new {
name = "New Customer 1", address = new {
addressType = "Main Office",
addressLine1 = "123 Main Street",
location = new {
city = "Brooklyn", stateProvinceName = "New York"
},
postalCode = "11229",
countryRegionName = "United States"
},
};
Document document1 = await CreateDocument(client, document1Definition);
Console.WriteLine("Created document {0} from dynamic object", document1.Id);
Console.WriteLine();
var document2Definition = @" {
""name"": ""New Customer 2"",
""address"": {
""addressType"": ""Main Office"",
""addressLine1"": ""123 Main Street"",
""location"": {
""city"": ""Brooklyn"", ""stateProvinceName"": ""New York""
},
""postalCode"": ""11229"",
""countryRegionName"": ""United States""
}
}";
Document document2 = await CreateDocument(client, document2Definition);
Console.WriteLine("Created document {0} from JSON string", document2.Id);
Console.WriteLine();
var document3Definition = new Customer {
Name = "New Customer 3",
Address = new Address {
AddressType = "Main Office",
AddressLine1 = "123 Main Street",
Location = new Location {
City = "Brooklyn", StateProvinceName = "New York"
},
PostalCode = "11229",
CountryRegionName = "United States"
},
};
Document document3 = await CreateDocument(client, document3Definition);
Console.WriteLine("Created document {0} from typed object", document3.Id);
Console.WriteLine();
}
Ten drugi dokument działa tylko z nieprzetworzonym ciągiem JSON. Teraz przechodzimy do przeciążenia dla CreateDocument, które używa JavaScriptSerializer do deserializacji ciągu do obiektu, który następnie przekazuje do tej samej metody CreateDocument, której użyliśmy do utworzenia pierwszego dokumentu.
W trzecim dokumencie wykorzystaliśmy obiekt C # Customer zdefiniowany w naszej aplikacji.
Przyjrzyjmy się temu klientowi, ma on właściwość Id i address, w której adres jest zagnieżdżonym obiektem z własnymi właściwościami, w tym lokalizacją, który jest kolejnym zagnieżdżonym obiektem.
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DocumentDBDemo {
public class Customer {
[JsonProperty(PropertyName = "id")]
public string Id { get; set; }
// Must be nullable, unless generating unique values for new customers on client
[JsonProperty(PropertyName = "name")]
public string Name { get; set; }
[JsonProperty(PropertyName = "address")]
public Address Address { get; set; }
}
public class Address {
[JsonProperty(PropertyName = "addressType")]
public string AddressType { get; set; }
[JsonProperty(PropertyName = "addressLine1")]
public string AddressLine1 { get; set; }
[JsonProperty(PropertyName = "location")]
public Location Location { get; set; }
[JsonProperty(PropertyName = "postalCode")]
public string PostalCode { get; set; }
[JsonProperty(PropertyName = "countryRegionName")]
public string CountryRegionName { get; set; }
}
public class Location {
[JsonProperty(PropertyName = "city")]
public string City { get; set; }
[JsonProperty(PropertyName = "stateProvinceName")]
public string StateProvinceName { get; set; }
}
}
Mamy też atrybuty właściwości JSON, ponieważ chcemy zachować odpowiednie konwencje po obu stronach ogrodzenia.
Dlatego po prostu tworzę obiekt New Customer wraz z jego zagnieżdżonymi obiektami podrzędnymi i jeszcze raz wzywam CreateDocument. Chociaż nasz obiekt klienta ma właściwość Id, nie podaliśmy dla niego wartości, więc DocumentDB wygenerował ją na podstawie identyfikatora GUID, tak jak w przypadku poprzednich dwóch dokumentów.
Kiedy powyższy kod zostanie skompilowany i wykonany, otrzymasz następujące dane wyjściowe.
**** Create Documents ****
Created new document: 575882f0-236c-4c3d-81b9-d27780206b2c
{
"name": "New Customer 1",
"address": {
"addressType": "Main Office",
"addressLine1": "123 Main Street",
"location": {
"city": "Brooklyn",
"stateProvinceName": "New York"
},
"postalCode": "11229",
"countryRegionName": "United States"
},
"id": "575882f0-236c-4c3d-81b9-d27780206b2c",
"_rid": "kV5oANVXnwDGPgAAAAAAAA==",
"_ts": 1450037545,
"_self": "dbs/kV5oAA==/colls/kV5oANVXnwA=/docs/kV5oANVXnwDGPgAAAAAAAA==/",
"_etag": "\"00006fce-0000-0000-0000-566dd1290000\"",
"_attachments": "attachments/"
}
Created document 575882f0-236c-4c3d-81b9-d27780206b2c from dynamic object
Created new document: 8d7ad239-2148-4fab-901b-17a85d331056
{
"name": "New Customer 2",
"address": {
"addressType": "Main Office",
"addressLine1": "123 Main Street",
"location": {
"city": "Brooklyn",
"stateProvinceName": "New York"
},
"postalCode": "11229",
"countryRegionName": "United States"
},
"id": "8d7ad239-2148-4fab-901b-17a85d331056",
"_rid": "kV5oANVXnwDHPgAAAAAAAA==",
"_ts": 1450037545,
"_self": "dbs/kV5oAA==/colls/kV5oANVXnwA=/docs/kV5oANVXnwDHPgAAAAAAAA==/",
"_etag": "\"000070ce-0000-0000-0000-566dd1290000\"",
"_attachments": "attachments/"
}
Created document 8d7ad239-2148-4fab-901b-17a85d331056 from JSON string
Created new document: 49f399a8-80c9-4844-ac28-cd1dee689968
{
"id": "49f399a8-80c9-4844-ac28-cd1dee689968",
"name": "New Customer 3",
"address": {
"addressType": "Main Office",
"addressLine1": "123 Main Street",
"location": {
"city": "Brooklyn",
"stateProvinceName": "New York"
},
"postalCode": "11229",
"countryRegionName": "United States"
},
"_rid": "kV5oANVXnwDIPgAAAAAAAA==",
"_ts": 1450037546,
"_self": "dbs/kV5oAA==/colls/kV5oANVXnwA=/docs/kV5oANVXnwDIPgAAAAAAAA==/",
"_etag": "\"000071ce-0000-0000-0000-566dd12a0000\"",
"_attachments": "attachments/"
}
Created document 49f399a8-80c9-4844-ac28-cd1dee689968 from typed object