Helpwiz.CosmosDbFramework
1.0.9
dotnet add package Helpwiz.CosmosDbFramework --version 1.0.9
NuGet\Install-Package Helpwiz.CosmosDbFramework -Version 1.0.9
<PackageReference Include="Helpwiz.CosmosDbFramework" Version="1.0.9" />
<PackageVersion Include="Helpwiz.CosmosDbFramework" Version="1.0.9" />
<PackageReference Include="Helpwiz.CosmosDbFramework" />
paket add Helpwiz.CosmosDbFramework --version 1.0.9
#r "nuget: Helpwiz.CosmosDbFramework, 1.0.9"
#:package Helpwiz.CosmosDbFramework@1.0.9
#addin nuget:?package=Helpwiz.CosmosDbFramework&version=1.0.9
#tool nuget:?package=Helpwiz.CosmosDbFramework&version=1.0.9
Helpwiz CosmosDB Framework
This package contains a foundation library for getting up-and-running with CosmosDB quickly. Helpwiz is a marketplace for domestic cleaners - we allow clients who want to find a cleaner for their houses to choose and book a cleaner in a simple, one-pass process.
We provide this package to anyone who wants to use it, but please give us a mention if you can.
Basic Elements
The basic elements of this package are:
- DocumentTransaction - A unit-of-work / repository pattern for CosmosDB.
- ChangeTrackingDocumentBase - A base class for documents that automatically track changes.
- DocumentBase - A base class for documents without change tracking.
Getting Started
Creating or accessing a Database and Collection
To either access an existing database and collection, or to create a new one if it doesn't exist, and to create a read-only transaction for querying documents, you do the following:
var documentTransaction = await DocumentTransactionFactory.CreateDocumentTransactionAsync(
DocumentDataTarget.CreateDefault(
ACCOUNT_ENDPOINT_URL,
ACCOUNT_KEY,
THROUGHPUT,
DATABASE_NAME,
COLLECTION_NAME),
TransactionBehaviourEnum.ReadOnly,
ConcurrencyBehaviourEnum.OverwriteIfNewest,
transactionSaveErrorsCallback: CALLBACK_FN)
);
Implementing a Document
Your documents must derive from ChangeTrackingDocumentBase<T> or DocumentBase<T>
To implement a document, you
- Derive from one of the bases.
- Add two constructors, one with parameters for creation, one parameterless for deserialization
- Add any properties you want to serialize
Here is an example:
internal sealed class MyDocument : ChangeTrackingDocumentBase<MyDocument>
{
private static readonly int currentVersion = 2;
// Creation constructor
public MyDocument(int myData, string stringData, Guid customerId, Guid cityId)
: base(Guid.NewGuid(), cityId.ToString(), currentVersion)
{
MyData = myData;
StringData = stringData;
CityId = cityId;
CustomerId = customerId;
}
// Json Deserialization
public MyDocument()
{
}
//Upgrades
[OnDeserialized]
private void OnDeserialized(StreamingContext ctx)
{
//Added city id - all data was in New York before.
DoUpgradeIfNecessary(0, () => CityId = TestDocumentBase.CityIds.NewYork);
//Added string data - defaults to null
DoUpgradeIfNecessary(1, () => { });
}
public int MyData { get; set; }
// Added in version 2.
public string StringData { get; set; }
// Added in version 1
public Guid CityId { get; set; }
public Guid CustomerId { get; set; }
}
This document will automatically track changes and add to the transaction if needed.
The document shown here has had two previous versions. When changing the version number, you should upgrade the document via the [OnDeserialized] callback, using the DoUpgradeIfNecessary helper method, as shown.
Adding a document to a DataTransaction
Here is what adding a document to the database looks like:
//Create the transaction.
using (var transaction = await CreateTransaction(TransactionBehaviourEnum.AbandonIfNotSaved))
{
var document = new MyDocument(customerIntegerData, customerStringData, customerId, cityId);
transaction.AddDocument(document);
transaction.Save();
}
Finding a specific document
You can find a specific document using the asynchronous FindDocument<T> method:
var document = await transaction.FindDocument<MyDocument>(id, cityKey.ToString());
You can find multiple documents under a partition using the FindDocuments<T> method:
var multipleDocuments = await transaction.FindDocuments<MyDocument>(new[] {id, id2, id3}, cityKey.ToString());
Querying documents
To query for matching documents:
var matchingDocuments = await transaction.Builder.MakeQueryWhereAsync<MyDocument>(t => t.MyData < 100);
To query for matching ordered documents:
var ordered = await transaction.Builder.MakeQueryWhereOrderByDescendingLimitAsync<MyDocument, int>(
t => t.CustomerId == id, //Find documents with this customer id.
t => t.MyData, //Order by descending MyData values.
10); //Limit to first 10 results.
To get an ascending query, just invert the calculation.
Query Caching
For the lifetime of a transaction, all queries are cached. Running an equivalent query twice will only result in one execution against the database. In addition if changes (additions, modifications, deletions) have been made locally to any documents, the query will return the correct result as if the changes had already been committed.
Deletion
To delete a document, using the DocumentTransaction.Delete() method:
transaction.Delete(document)
DocumentTransaction also supports soft deletion. To soft-delete a document, using the DocumentTransaction.SoftDelete() method:
transaction.SoftDelete(document)
To get soft deleted documents, you have to use the special methods for this:
- DocumentTransaction.FindDocumentIncDeleted<T>
- DocumentTransaction.Builder.MakeQueryWhereIncDelete<T>
- DocumentTransaction.Builder.MakeQueryWhereOrderByDescendingLimit<T>
Transaction Behaviours
When creating a transaction, you specify the transaction behaviour from TransactionBehaviourEnum. The options are:
- ReadOnly: A read-only transaction.
- AbandonIfNotSaved: A writable transaction that will abandon changes unless you Save()
- SaveIfNotAbandoned: A writable transaction that will save changes unless Abandon()
Committing Changes
No changes are committed to the database unless you dispose the transaction, either by calling dispose, or putting it in a using statement.
Handling DateTime values
To store dates, it is recommended to store them as doubles (or nullable doubles in the case of nullable DateTime values):
internal sealed class StoringDateTimeDocument : ChangeTrackingDocumentBase<StoringDateTimeDocument>
{
//...
// Value is stored as a double...
[JsonProperty]
private double DoubleTimeStamp { get; set; }
// But used as a date time...
[JsonIgnore]
public DateTime TimeStamp
{
get => DoubleTimeStamp.ToDotNetDateTime();
set => DoubleTimeStamp = value.ToDocumentTime();
}
}
The doubles allow efficient indexing, but the program can use DateTime. The extension methods also support nullables.
Unit Testing Your Implementations
We provide a meta-tests to help you unit test your documents:
public void ExampleTestMethod()
{
var testContext = DocumentMetaTestContext.CreateFromAssembly(typeof(MyDocument).Assembly);
var testResults = DocumentMetaTests.TestDocumentsHaveNoDateTimeProperties(testContext);
var failures = testResults.Where(t => !t.IsSuccess).Select(t => t.Error).ToArray();
Assert.That(failures, Is.Empty);
}
We recommend that you use all of the meta tests in your test fixture.
Mentioning Us
If you do get any use out of this library, please link to our website at helpwiz.com and mention us. Thanks.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
.NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
.NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen40 was computed. tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Helpwiz.DotNetHelpers (>= 1.0.1)
- Microsoft.Azure.Cosmos (>= 3.10.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Previously: Added base classes for documents, include a base class for auto-change tracking. Added concurrency levels to DocumentTransactionFactory. Added concurrency behaviour on transaction save and an error callback.
1.0.3 Fixed deletion and LoadExisting methods.
1.0.4 Fixed accidentally reintroduced issue.
1.0.5 Changed transaction timestamp to UtcNow, instead of BristishNow - also works on unix systems.
1.0.6 Added support for single-partition queries
1.0.7 Added CommitAsync() method to Transaction, to allow transaction to cache documents but commit modifications on request.
1.0.8 Added protections to make CommitAsync() multi-call safe (updating Etag of upserted documents, resetting changes etc...)
1.0.9 Fixed updates to loaded dictionary from deleted, added and modified elements on CommitAsync().