Adds additional capabilities extending Microsoft.Azure.Cosmos
that standardise and simply usage of the Cosmos Database
for Beef.
To encapsulate the Cosmos database access the CosmosDb or CosmosDbBase is inherited to enable.
The following demonstrates the usage:
public class MyCosmosDb : CosmosDb<MyCosmosDb>
{
public MyCosmosDb() : base(new Microsoft.Azure.Cosmos("https://localhost:8081", "C2=="), "Beef.UnitTest", true)
{ }
}
Mapping between the .NET entity to/from a cosmos-oriented .NET model (these can be the same) is managed using AutoMapper.
The CosmosDbArgs
provides the required Container
operation arguments.
Property | Description |
---|---|
Mapper |
The AutoMapper IMapper instance to be used to perform the source to/from destination model mapping. |
ContainerId |
The Cosmos Container identifier. |
PartitionKey |
The PartitionKey (defaults to PartitionKey.None ). |
Paging |
The paging configuration (used by Query operation only). |
ItemRequestOptions |
The ItemRequestOptions used for Get , Create , Update and Delete . |
QueryRequestOptions |
The QueryRequestOptions used for Query only. |
NullOnNotFoundResponse |
Indicates that a null is to be returned where the response has an HttpStatusCode.NotFound on a Get . |
SetAuthorizedFilter |
Sets the filter (IQueryable ) for all operations to ensure consistent authorisation is applied. Applies automatically to all queries, in that the filter is applied each time a CosmosDbQuery or CosmosDbValueQuery is executed. Additionally, the filter is applied to the standard Get , Create , Update and Delete (CRUD) operations to ensure only authorised data is accessed and modified. |
The following demonstrates the usage:
var args1 = CosmosDbArgs.Create(_mapping, "Persons");
var args2 = CosmosDbArgs.Create(_mapping, "Persons", paging);
A CosmosDbContainer
(and CosmosDbValueContainer
for CosmosDbValue
) enables all of the CRUD and Query access for a configured Cosmos Container
; versus, having to specify the container identifier per operation.
Examples as follows:
public class CosmosDb : CosmosDbBase
{
public CosmosDb() : base(new Microsoft.Azure.Cosmos("https://localhost:8081", "C2=="), "Beef.UnitTest", true)
{
Persons = new CosmosDbContainer<Person, Person>(this, CosmosMapper.Default.CreateArgs("Persons"));
}
public CosmosDbContainer<Person, Person> Persons { get; private set; }
}
...
var db = new CosmosDb();
var v = await db.Persons.GetAsync(Guid.NewGuid());
The primary data persistence activities are CRUD (Create, Read, Update and Delete) related; CosmosDbContainer
(and CosmosDbValueContainer
for CosmosDbValue
) enable:
Operation | Description |
---|---|
GetAsync |
Gets the entity for the specified key where found; otherwise, null (default) or NotFoundException depending on the corresponding CosmosDbArgs.NullOnNotFoundResponse . |
CreateAsync |
Creates the entity. Automatically updates the Created* fields of IChangeLog where implemented. Where the the corresponding CosmosDbArgs.SetIdentifierOnCreate is true (default), then the Cosmos Id will be set to Guid.NewGuid (overriding any prior value). |
UpdateAsync |
Updates the entity. Automatically updates the Updated* fields of IChangeLog where implemented; also ensuring that the existing Created* fields are not changed. |
DeleteAsync |
Deletes the entity. Given a delete is idempotent it will be successful even where the entity does not exist. |
Additional information:
- Where the entity implements
IETag
then theUpdateAsync
will be performed with anIf-Match
header; and a correspondingConcurrencyException
will be thrown where it does not match. Note: for theETag
to function correctly the JSON name on the model must be_etag
. - Where uniqueness has been defined for the
Container
and a create or update results in a duplicate aDuplicateException
will be thrown.
More advanced query operations are enabled via by the CosmosDbQuery
(and CosmosDbValueQuery
for CosmosDbValue
) which further extends on the LINQ capabilities provided by the Container
. This supports an overload where a query Func
can be added to simplify the likes of filtering, etc. where needed:
Operation | Description |
---|---|
AsQueryable |
Gets a prepared IQueryable (with any CosmosDbValue.Type filtering as applicable). Note: for this reason this is the recommended approach for all ad-hoc queries. Note: CosmosDbArgs.Paging is not supported and must be applied using the provided IQueryable.Paging . |
SelectFirst |
Selects the first item.* |
SelectFirstOrDefault |
Selects the first item or default.* |
SelectSingle |
Selects a single item.* |
SelectSingleOrDefault |
Selects a single item or default.* |
SelectQuery |
Select multiple items and either creates, or updates an existing, collection. Where the corresponding CosmosDbArgs.Paging is provided the configured paging, and optional get count, will be enacted. |
* These are provided for use versus than the default IQueryable
equivalents (which are currently not supported) as they will only internally page one or two items accordingly to minimise query and data costs. Paging cannot be applied more than once as it will result in a invalid sub-query.
Where the Entity does not naturally map to a Cosmos Model a couple of options are provided:
- Inherit Model from
CosmosDbModelBase
; this provides the basicId
,_etag
andttl
. - Use the
CosmosDbValueContainer
that in turn leverages theCosmosDbValue
for persisting the ModelValue
. This inherits fromCosmosDbModelBase
, and extends by adding aType
(enables values with multiple types to be persisted in a single containger; for example, reference data), and theValue
itself.
Additional row-level like authorisation can be applied to all CRUD and Query operations. The CosmosDbArgs.SetAuthorizedFilter
(described here) defines the authorization filter. This should be set before any operation is performed. The beef code-generation provides a _onDataArgsCreate
method that can be set to perform; this will be invoked each time a CosmosDbArgs
is instantiated.
Example as follows. This demonstrates filtering a Content
entity by allowable ContentType
that has been added to the ExecutionContext
set when the user is configured at startup:
_onDataArgsCreate = OnDataArgsCreate;
...
private void OnDataArgsCreate(ICosmosDbArgs dbArgs)
{
dbArgs.SetAuthorizedFilter((q) => ((IQueryable<CosmosDbValue<Content>>)q).Where(c => ExecutionContext.Current.ContentType.Contains(c.Value.ContentType)));
}
Given there are costs associated with using Cosmos DB, consider using the local emulator for development and testing purposes: https://docs.microsoft.com/en-us/azure/cosmos-db/local-emulator